Cafedev chia sẻ cho ace về cách chỉnh sửa DOM với Javascript. DOM cho phép chúng ta làm bất cứ điều gì với các phần tử và nội dung của chúng, nhưng trước tiên chúng ta cần tiếp cận đối tượng DOM tương ứng.
Tất cả các hoạt động trên DOM đều bắt đầu với đối tượng document. Đó là “điểm vào” chính của DOM. Từ nó, chúng ta có thể truy cập bất kỳ nút(phần tử) nào.
Đây là hình ảnh về các liên kết cho phép di chuyển giữa các nút DOM:
Hãy thảo luận chi tiết hơn về chúng.
Nội dung chính
1.Phần tử cha trên cùng: documentElement và body
Các nút cây trên cùng có sẵn trực tiếp dưới dạng document với các thuộc tính:
<html>
= document.documentElement
Nút tài liệu trên cùng là document.documentElement. Đó là nút DOM của thẻ < html >.
<body>
= document.body
Một nút DOM khác được sử dụng rộng rãi là phần tử <body>
– document.body.
<head>
= document.head
Các thẻ < head > có sẵn như là document.head.
Có một vấn đề: document.body có thể null
Tập lệnh không thể truy cập một phần tử không tồn tại tại thời điểm chạy.
Đặc biệt, nếu một tập lệnh nằm bên trong<head>
, thì document.body không có sẵn, vì trình duyệt chưa đọc nó.
Vì vậy, trong ví dụ dưới đây, đầu tiên alert hiển thị null:
<html>
<head>
<script>
alert( "From HEAD: " + document.body ); // null, there's no <body> yet
</script>
</head>
<body>
<script>
alert( "From BODY: " + document.body ); // HTMLBodyElement, now it exists
</script>
</body>
</html>
Trong thế giới DOM null có nghĩa là “không tồn tại”
Trong DOM, null giá trị có nghĩa là “không tồn tại” hoặc “không có nút như vậy”.
2. Phần tử con: childNodes, firstChild, lastChild
Có hai thuật ngữ mà chúng ta sẽ sử dụng từ bây giờ:
- Các nút con (hoặc nút con) – các phần tử là con trực tiếp. Nói cách khác, chúng được lồng chính xác trong một cái đã cho. Ví dụ,
<head>
và<body>
là con của phần tử<html>
. - Con cháu – tất cả các phần tử được lồng trong một phần tử đã cho, bao gồm các phần tử con.. Ví dụ: ở đây
<body>
có con<div>
và<ul>
(và một vài nút văn bản trống):
<html>
<body>
<div>Begin</div>
<ul>
<li>
<b>Information</b>
</li>
</ul>
</body>
</html>
… Và con cháu của <body>
không chỉ là con trực tiếp của <div>
, <ul>
mà còn là các phần tử lồng nhau sâu hơn, chẳng hạn như <li>
(con của <ul>
) và <b>
(con của <li>
) – toàn bộ cây con.
Bộ child Nodes sưu tập liệt kê tất cả các nút con, bao gồm cả các nút văn bản.
Ví dụ dưới đây cho thấy con cái của document.body:
<html>
<body>
<div>Begin</div>
<ul>
<li>Information</li>
</ul>
<div>End</div>
<script>
for (let i = 0; i < document.body.childNodes.length; i++) {
alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT
}
</script>
...more stuff...
</body>
</html>
Xin lưu ý một chi tiết thú vị ở đây. Nếu chúng ta chạy ví dụ trên, phần tử cuối cùng được hiển thị là <script>
. Trên thực tế, tài liệu có nhiều nội dung bên dưới, nhưng tại thời điểm thực thi tập lệnh, trình duyệt chưa đọc nó nên không nhìn thấy tập lệnh.
Thuộc tính firstChild và lastChild cấp quyền truy cập nhanh cho con đầu tiên và con cuối cùng.
Họ chỉ là những cái gọi tắt. Nếu tồn tại các nút con, thì điều sau luôn đúng:
elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild
Ngoài ra còn có một chức năng đặc biệt elem.hasChildNodes() để kiểm tra xem có bất kỳ nút con nào không.
2.1 Bộ sưu tập DOM
Như chúng ta thấy, childNodes trông giống như một mảng. Nhưng thực ra nó không phải là một mảng, mà là một tập hợp – một đối tượng có thể lặp lại giống mảng đặc biệt.
Có hai hệ quả quan trọng:
Chúng ta có thể sử dụng for..of để lặp lại nó:
for (let node of document.body.childNodes) {
alert(node); // shows all nodes from the collection
}
Đó là bởi vì nó có thể lặp lại (cung cấp thuộc tính Symbol.iterator).
Các phương thức mảng sẽ không hoạt động, bởi vì nó không phải là một mảng:
alert(document.body.childNodes.filter); // undefined (there's no filter method!)
Điều đầu tiên là tốt đẹp. Thứ hai là có thể chấp nhận được, vì chúng ta có thể sử dụng Array.from để tạo một mảng “thực” từ bộ sưu tập, nếu chúng ta muốn các phương thức mảng:
alert( Array.from(document.body.childNodes).filter ); // function
Bộ sưu tập DOM ở chế độ chỉ có thể đọc
Bộ sưu tập DOM và thậm chí hơn thế nữa – tất cả các thuộc tính điều hướng được liệt kê trong chương này là chỉ đọc.
Chúng ta không thể thay thế một node child bằng một thứ khác bằng cách gán childNodes[i] = ….
Thay đổi DOM cần các phương pháp khác. Chúng ta sẽ thấy chúng trong chương tiếp theo.
Bộ sưu tập DOM đang hoạt động
Hầu hết tất cả các bộ sưu tập DOM với các ngoại lệ nhỏ đều đang hoạt động . Nói cách khác, chúng phản ánh trạng thái hiện tại của DOM.
Nếu chúng ta giữ một tham chiếu đến elem.childNodes và thêm / xóa các nút vào DOM, thì chúng sẽ tự động xuất hiện trong bộ sưu tập.
Không sử dụng for..in để lặp lại các bộ sưu tập
Bộ sưu tập có thể lặp lại bằng cách sử dụng for..of. Đôi khi mọi người cố gắng sử dụng for..in cho điều đó.
Làm ơn, đừng. Các for..in vòng lặp qua tất cả các thuộc tính đếm được. Và bộ sưu tập có một số thuộc tính “bổ sung” hiếm khi được sử dụng mà chúng tôi thường không muốn lấy:
<body>
<script>
// shows 0, 1, length, item, values and more.
for (let prop in document.body.childNodes) alert(prop);
</script>
</body>
3. Các nút(phần tử) anh em và nút cha.
Anh chị em là các nút con của cùng một cha mẹ.
Ví dụ, đây <head>
và <body>
là anh chị em:
<html>
<head>...</head><body>...</body>
</html>
<body>
được cho là anh chị em “tiếp theo” hoặc “đúng” của <head>
,
<head>
được cho là anh chị em “trước” hoặc “trái” của<body>
.
Anh chị em tiếp theo là phần tử nextSibling và người trước đó – trong previousSibling.
Nút cha mẹ có sẵn dưới dạng parentNode.
Ví dụ:
// parent of <body> is <html>
alert( document.body.parentNode === document.documentElement ); // true
// after <head> goes <body>
alert( document.head.nextSibling ); // HTMLBodyElement
// before <body> goes <head>
alert( document.body.previousSibling ); // HTMLHeadElement
4. Điều hướng phần tử
Thuộc tính điều hướng liệt kê ở trên đề cập đến tất cả các nút. Ví dụ, trong childNodes chúng ta có thể thấy cả nút văn bản, nút phần tử và thậm chí cả nút chú thích nếu có.
Nhưng đối với nhiều tác vụ, chúng ta không muốn các nút văn bản hoặc nhận xét(comment). Chúng ta muốn thao tác các nút phần tử đại diện cho các thẻ và hình thành cấu trúc của trang.
Vì vậy, hãy xem thêm các liên kết điều hướng chỉ tính đến các nút phần tử :
Các liên kết tương tự như những liên kết được đưa ra ở trên, chỉ với Element từ bên trong:
- children – chỉ những phần tử con là nút phần tử.
- firstElementChild, lastElementChild- phần tử con đầu tiên và cuối cùng.
- previousElementSibling, nextElementSibling- các yếu tố hàng xóm.
- parentElement – phần tử cha.
Tại sao parentElement? Cha mẹ có thể không phải là một phần tử?
Các thuộc tính parentElement trả về “Element” cha mẹ, trong khi parentNode trả về “bất kỳ nút” cha mẹ nào. Các thuộc tính này thường giống nhau: Vì chúng đều nhận được từ gốc.
Với một ngoại lệ của document.documentElement:
alert( document.documentElement.parentNode ); // document
alert( document.documentElement.parentElement ); // null
Lý do là nút gốc document.documentElement( <html>
) có vai trò document là nút cha của nó. Nhưng document không phải là một nút phần tử, vì vậy parentNode trả về nó và parentElement không trả về .
Chi tiết này có thể hữu ích khi chúng ta muốn đi lên từ một yếu tố tùy ý phần tử <html>
, nhưng không cần đến document:
while(elem = elem.parentElement) { // go up till <html>
alert( elem );
}
Hãy sửa đổi một trong các ví dụ trên: thay thế childNodes bằng children. Bây giờ nó chỉ hiển thị các phần tử:
<html>
<body>
<div>Begin</div>
<ul>
<li>Information</li>
</ul>
<div>End</div>
<script>
for (let elem of document.body.children) {
alert(elem); // DIV, UL, DIV, SCRIPT
}
</script>
...
</body>
</html>
5. Các liên kết khác: bảng
Cho đến bây giờ chúng ta đã mô tả các thuộc tính điều hướng cơ bản.
Một số loại phần tử DOM nhất định có thể cung cấp các thuộc tính bổ sung, cụ thể cho loại của chúng,
Các bảng là một ví dụ tuyệt vời về điều đó và đại diện cho một trường hợp đặc biệt quan trọng:
Phần tử <table>
hỗ trợ (ngoài các thuộc tính đã cho ở trên) sau:
- table.rows – tập hợp các phần tử
<tr>
của bảng. - table.caption/tHead/tFoot- tài liệu tham khảo đến các yếu tố
<caption>
,<thead>
,<tfoot>
. - table.tBodies- tập hợp các phần tử
<tbody>
(có thể có nhiều phần tử theo tiêu chuẩn, nhưng sẽ luôn có ít nhất một phần tử – ngay cả khi nó không có trong HTML nguồn, trình duyệt sẽ đặt nó trong DOM).
< thead >, < tfoot >,< tbody > là các Yếu tố cung cấp có thuộc tính rows:
- tbody.rows- bộ sưu tập
<tr>
bên trong.
<tr>
:
- tr.cells- tập hợp các
<td>
và<th>
với các ô bên trong đã cho<tr>
. - tr.sectionRowIndex- vị trí (chỉ số) của giá trị đã cho bên trong bao bọc
<thead>/<tbody>/<tfoot>
. - tr.rowIndex- số lượng toàn bộ
<tr>
trong bảng (bao gồm tất cả các hàng trong bảng).
<td>
and <th>
:
- td.cellIndex- số lượng ô bên trong bao quanh
<tr>
.
Một ví dụ về cách sử dụng:
<table id="table">
<tr>
<td>one</td><td>two</td>
</tr>
<tr>
<td>three</td><td>four</td>
</tr>
</table>
<script>
// get td with "two" (first row, second column)
let td = table.rows[0].cells[1];
td.style.backgroundColor = "red"; // highlight it
</script>
Đặc điểm kỹ thuật: dữ liệu dạng bảng .
Ngoài ra còn có các thuộc tính điều hướng bổ sung cho các biểu mẫu HTML. Chúng ta sẽ xem xét chúng sau khi chúng ta bắt đầu làm việc với các biểu mẫu.
6. Tóm lược
Với một nút DOM, chúng ta có thể đi đến các vùng lân cận của nó bằng cách sử dụng các thuộc tính điều hướng.
Có hai nhóm chính trong số họ:
- Đối với tất cả các nút: parentNode, childNodes, firstChild, lastChild, previousSibling, nextSibling.
- Đối với các nút phần tử duy nhất: parentElement, children, firstElementChild, lastElementChild, previousElementSibling, nextElementSibling.
Một số loại phần tử DOM, ví dụ như bảng, cung cấp các thuộc tính và bộ sưu tập bổ sung để truy cập nội dung của chúng.
Nguồn và tài liệu tiếng anh tham khảo:
Full series tự học Javascript từ cơ bản tới nâng cao tại đây nha.
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!