W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
DOM 讓我們可以對(duì)元素和它們中的內(nèi)容做任何事,但是首先我們需要獲取到對(duì)應(yīng)的 DOM 對(duì)象。
對(duì) DOM 的所有操作都是以 ?document
? 對(duì)象開(kāi)始。它是 DOM 的主“入口點(diǎn)”。從它我們可以訪問(wèn)任何節(jié)點(diǎn)。
這里是一張描述對(duì)象間鏈接的圖片,通過(guò)這些鏈接我們可以在 DOM 節(jié)點(diǎn)之間移動(dòng)。
讓我們更詳細(xì)地討論它們吧。
最頂層的樹(shù)節(jié)點(diǎn)可以直接作為 document
的屬性來(lái)使用:
?<html>
? = ?document.documentElement
?
最頂層的 document 節(jié)點(diǎn)是 ?document.documentElement
?。這是對(duì)應(yīng) ?<html>
? 標(biāo)簽的 DOM 節(jié)點(diǎn)。
?<body>
? = ?document.body
?
另一個(gè)被廣泛使用的 DOM 節(jié)點(diǎn)是 ?<body>
? 元素 —— ?document.body
?。
?<head>
? = ?document.head
?
?<head>
? 標(biāo)簽可以通過(guò) ?document.head
? 訪問(wèn)。
這里有個(gè)問(wèn)題:document.body 的值可能是 null
腳本無(wú)法訪問(wèn)在運(yùn)行時(shí)不存在的元素。
尤其是,如果一個(gè)腳本是在
<head>
中,那么腳本是訪問(wèn)不到document.body
元素的,因?yàn)闉g覽器還沒(méi)有讀到它。
所以,下面例子中的第一個(gè)
alert
顯示null
:
<html> <head> <script> alert( "From HEAD: " + document.body ); // null,這里目前還沒(méi)有 <body> </script> </head> <body> <script> alert( "From BODY: " + document.body ); // HTMLBodyElement,現(xiàn)在存在了 </script> </body> </html>
在 DOM 的世界中,?
null
? 就意味著“不存在”在 DOM 中,
null
值就意味著“不存在”或者“沒(méi)有這個(gè)節(jié)點(diǎn)”。
從現(xiàn)在開(kāi)始,我們將使用下面這兩個(gè)術(shù)語(yǔ):
<head>
? 和 ?<body>
? 就是 ?<html>
? 元素的子元素。例如,這里 <body>
有子元素 <div>
和 <ul>
(以及一些空白的文本節(jié)點(diǎn)):
<html>
<body>
<div>Begin</div>
<ul>
<li>
<b>Information</b>
</li>
</ul>
</body>
</html>
……<body>
元素的子孫元素不僅包含直接的子元素 <div>
和 <ul>
,還包含像 <li>
(<ul>
的子元素)和 <b>
(<li>
的子元素)這樣的元素
— 整個(gè)子樹(shù)。
childNodes
集合列出了所有子節(jié)點(diǎn),包括文本節(jié)點(diǎn)。
下面這個(gè)例子顯示了 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>
請(qǐng)注意這里的一個(gè)有趣的細(xì)節(jié)。如果我們運(yùn)行上面這個(gè)例子,所顯示的最后一個(gè)元素是 <script>
。實(shí)際上,文檔下面還有很多東西,但是在這個(gè)腳本運(yùn)行的時(shí)候,瀏覽器還沒(méi)有讀到下面的內(nèi)容,所以這個(gè)腳本也就看不到它們。
firstChild
和 lastChild
屬性是訪問(wèn)第一個(gè)和最后一個(gè)子元素的快捷方式。
它們只是簡(jiǎn)寫(xiě)。如果元素存在子節(jié)點(diǎn),那么下面的腳本運(yùn)行結(jié)果將是 true:
elem.childNodes[0] === elem.firstChild
elem.childNodes[elem.childNodes.length - 1] === elem.lastChild
這里還有一個(gè)特別的函數(shù) elem.hasChildNodes()
用于檢查節(jié)點(diǎn)是否有子節(jié)點(diǎn)。
正如我們看到的那樣,childNodes
看起來(lái)就像一個(gè)數(shù)組。但實(shí)際上它并不是一個(gè)數(shù)組,而是一個(gè) 集合 —— 一個(gè)類數(shù)組的可迭代對(duì)象。
這個(gè)性質(zhì)會(huì)導(dǎo)致兩個(gè)重要的結(jié)果:
for..of
? 來(lái)迭代它:for (let node of document.body.childNodes) {
alert(node); // 顯示集合中的所有節(jié)點(diǎn)
}
這是因?yàn)榧鲜强傻模ㄌ峁┝怂枰?nbsp;Symbol.iterator
屬性)。
alert(document.body.childNodes.filter); // undefined(這里沒(méi)有 filter 方法?。?/code>
集合的性質(zhì)所得到的第一個(gè)結(jié)果很不錯(cuò)。第二個(gè)結(jié)果也還可以忍受,因?yàn)槿绻覀兿胍褂脭?shù)組的方法的話,我們可以使用 Array.from
方法來(lái)從集合創(chuàng)建一個(gè)“真”數(shù)組:
alert( Array.from(document.body.childNodes).filter ); // function
DOM 集合是只讀的
DOM 集合,甚至可以說(shuō)本章中列出的 所有 導(dǎo)航(navigation)屬性都是只讀的。
我們不能通過(guò)類似
childNodes[i] = ...
的操作來(lái)替換一個(gè)子節(jié)點(diǎn)。
修改子節(jié)點(diǎn)需要使用其它方法。我們將會(huì)在下一章中看到它們。
DOM 集合是實(shí)時(shí)的
除小部分例外,幾乎所有的 DOM 集合都是 實(shí)時(shí) 的。換句話說(shuō),它們反映了 DOM 的當(dāng)前狀態(tài)。
如果我們保留一個(gè)對(duì)
elem.childNodes
的引用,然后向 DOM 中添加/移除節(jié)點(diǎn),那么這些節(jié)點(diǎn)的更新會(huì)自動(dòng)出現(xiàn)在集合中。
不要使用 ?
for..in
? 來(lái)遍歷集合可以使用
for..of
對(duì)集合進(jìn)行迭代。但有時(shí)候人們會(huì)嘗試使用for..in
來(lái)迭代集合。
請(qǐng)不要這么做。
for..in
循環(huán)遍歷的是所有可枚舉的(enumerable)屬性。集合還有一些“額外的”很少被用到的屬性,通常這些屬性也是我們不期望得到的:
<body> <script> // 顯示 0,1,length,item,values 及其他。 for (let prop in document.body.childNodes) alert(prop); </script> </body>
兄弟節(jié)點(diǎn)(sibling) 是指有同一個(gè)父節(jié)點(diǎn)的節(jié)點(diǎn)。
例如,<head>
和 <body>
就是兄弟節(jié)點(diǎn):
<html>
<head>...</head><body>...</body>
</html>
<body>
? 可以說(shuō)是 ?<head>
? 的“下一個(gè)”或者“右邊”兄弟節(jié)點(diǎn)。<head>
? 可以說(shuō)是 ?<body>
? 的“前一個(gè)”或者“左邊”兄弟節(jié)點(diǎn)。下一個(gè)兄弟節(jié)點(diǎn)在 nextSibling
屬性中,上一個(gè)是在 previousSibling
屬性中。
可以通過(guò) parentNode
來(lái)訪問(wèn)父節(jié)點(diǎn)。
例如:
// <body> 的父節(jié)點(diǎn)是 <html>
alert( document.body.parentNode === document.documentElement ); // true
// <head> 的后一個(gè)是 <body>
alert( document.head.nextSibling ); // HTMLBodyElement
// <body> 的前一個(gè)是 <head>
alert( document.body.previousSibling ); // HTMLHeadElement
上面列出的導(dǎo)航(navigation)屬性引用 所有 節(jié)點(diǎn)。例如,在 childNodes
中我們可以看到文本節(jié)點(diǎn),元素節(jié)點(diǎn),甚至包括注釋節(jié)點(diǎn)(如果它們存在的話)。
但是對(duì)于很多任務(wù)來(lái)說(shuō),我們并不想要文本節(jié)點(diǎn)或注釋節(jié)點(diǎn)。我們希望操縱的是代表標(biāo)簽的和形成頁(yè)面結(jié)構(gòu)的元素節(jié)點(diǎn)。
所以,讓我們看看更多只考慮 元素節(jié)點(diǎn) 的導(dǎo)航鏈接(navigation link):
這些鏈接和我們?cè)谏厦嫣岬竭^(guò)的類似,只是在詞中間加了 Element
:
children
? —— 僅那些作為元素節(jié)點(diǎn)的子代的節(jié)點(diǎn)。firstElementChild
?,?lastElementChild
? —— 第一個(gè)和最后一個(gè)子元素。previousElementSibling
?,?nextElementSibling
? —— 兄弟元素。parentElement
? —— 父元素。為什么是 ?
parentElement
?? 父節(jié)點(diǎn)可以不是一個(gè)元素嗎?
parentElement
屬性返回的是“元素類型”的父節(jié)點(diǎn),而parentNode
返回的是“任何類型”的父節(jié)點(diǎn)。這些屬性通常來(lái)說(shuō)是一樣的:它們都是用于獲取父節(jié)點(diǎn)。
唯一的例外就是
document.documentElement
:
alert( document.documentElement.parentNode ); // document alert( document.documentElement.parentElement ); // null
因?yàn)楦?jié)點(diǎn)
document.documentElement
(<html>
)的父節(jié)點(diǎn)是document
。但document
不是一個(gè)元素節(jié)點(diǎn),所以parentNode
返回了document
,但parentElement
返回的是null
。
當(dāng)我們想從任意節(jié)點(diǎn)
elem
到<html>
而不是到document
時(shí),這個(gè)細(xì)節(jié)可能很有用:
while(elem = elem.parentElement) { // 向上,直到 <html> alert( elem ); }
讓我們修改上面的一個(gè)示例:用 children
來(lái)替換 childNodes
。現(xiàn)在它只顯示元素:
<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>
到現(xiàn)在,我們已經(jīng)描述了基本的導(dǎo)航(navigation)屬性。
方便起見(jiàn),某些類型的 DOM 元素可能會(huì)提供特定于其類型的其他屬性。
表格(Table)是一個(gè)很好的例子,它代表了一個(gè)特別重要的情況:
<table>
元素支持 (除了上面給出的,之外) 以下屬性:
table.rows
? —— ?<tr>
? 元素的集合。table.caption/tHead/tFoot
? —— 引用元素 ?<caption>
?,?<thead>
?,?<tfoot>
?。table.tBodies
? —— ?<tbody>
? 元素的集合(根據(jù)標(biāo)準(zhǔn)還有很多元素,但是這里至少會(huì)有一個(gè) —— 即使沒(méi)有被寫(xiě)在 HTML 源文件中,瀏覽器也會(huì)將其放入 DOM 中)。<thead>
,<tfoot>
,<tbody>
元素提供了 rows
屬性:
tbody.rows
? —— 表格內(nèi)部 ?<tr>
? 元素的集合。<tr>
:
tr.cells
? —— 在給定 ?<tr>
? 中的 ?<td>
? 和 ?<th>
? 單元格的集合。tr.sectionRowIndex
? —— 給定的 ?<tr>
? 在封閉的 ?<thead>/<tbody>/<tfoot>
? 中的位置(索引)。tr.rowIndex
? —— 在整個(gè)表格中 ?<tr>
? 的編號(hào)(包括表格的所有行)。<td>
和 <th>
:
td.cellIndex
? —— 在封閉的 ?<tr>
? 中單元格的編號(hào)。用法示例:
<table id="table">
<tr>
<td>one</td><td>two</td>
</tr>
<tr>
<td>three</td><td>four</td>
</tr>
</table>
<script>
// 獲取帶有 "two" 的 td(第一行,第二列)
let td = table.rows[0].cells[1];
td.style.backgroundColor = "red"; // highlight it
</script>
規(guī)范:tabular data。
HTML 表單(form)還有其它導(dǎo)航(navigation)屬性。稍后當(dāng)我們開(kāi)始使用表單(form)時(shí),我們將對(duì)其進(jìn)行研究。
給定一個(gè) DOM 節(jié)點(diǎn),我們可以使用導(dǎo)航(navigation)屬性訪問(wèn)其直接的鄰居。
這些屬性主要分為兩組:
parentNode
?,?childNodes
?,?firstChild
?,?lastChild
?,?previousSibling
?,?nextSibling
?。parentElement
?,?children
?,?firstElementChild
?,?lastElementChild
?,?previousElementSibling
?,?nextElementSibling
?。某些類型的 DOM 元素,例如 table,提供了用于訪問(wèn)其內(nèi)容的其他屬性和集合。
對(duì)于這個(gè)頁(yè)面:
<html>
<body>
<div>Users:</div>
<ul>
<li>John</li>
<li>Pete</li>
</ul>
</body>
</html>
對(duì)于以下各項(xiàng),請(qǐng)給出至少一種訪問(wèn)方式:
<div>
? DOM 節(jié)點(diǎn)?<ul>
? DOM 節(jié)點(diǎn)?<li>
? 節(jié)點(diǎn)(即包含 Pete 的節(jié)點(diǎn))?這里有很多種方法,例如:
獲取 <div>
DOM 節(jié)點(diǎn):
document.body.firstElementChild
// 或
document.body.children[0]
// 或(第一個(gè)節(jié)點(diǎn)是空格,所以我們應(yīng)該獲取的是第二個(gè))
document.body.childNodes[1]
獲取 <ul>
DOM 節(jié)點(diǎn):
document.body.lastElementChild
// 或
document.body.children[1]
獲取第二個(gè) <li>
(即包含 Pete 的節(jié)點(diǎn)):
// 獲取 <ul>,然后獲取它的最后一個(gè)子元素
document.body.lastElementChild.lastElementChild
重要程度: 5
如果 ?elem
? 是任意一個(gè) DOM 元素節(jié)點(diǎn)……
elem.lastChild.nextSibling
? 值一直都是 ?null
?,這個(gè)判定是不是真的?elem.children[0].previousSibling
? 值一直都是 ?null
?,這個(gè)判定是不是真的?elem.lastChild
? 就是最后一個(gè)節(jié)點(diǎn),它沒(méi)有 ?nextSibling
?。elem.children[0]
? 是元素中的第一個(gè)子元素。但是在它前面可能存在非元素的節(jié)點(diǎn)。所以 ?previousSibling
? 可能是一個(gè)文本節(jié)點(diǎn)。請(qǐng)注意,對(duì)于這兩種情況,如果沒(méi)有子節(jié)點(diǎn),那么就會(huì)報(bào)錯(cuò)。
如果這里沒(méi)有子節(jié)點(diǎn),那么 elem.lastChild
是 null
,所以我們就訪問(wèn)不到 elem.lastChild.nextSibling
。并且 elem.children
是空的(像一個(gè)空數(shù)組一樣 []
)。
編寫(xiě)代碼來(lái)把表格中的對(duì)角單元格都繪制成紅色。
你需要用代碼從 <table>
中獲取所有的對(duì)角單元格 <td>
,然后繪制它們:
// td 表示的是對(duì)單元格的引用
td.style.backgroundColor = 'red';
結(jié)果應(yīng)該如下所示:
我們將使用 rows
和 cells
屬性來(lái)獲取表格中的對(duì)角單元格。
<!DOCTYPE HTML>
<html>
<head>
<style>
table {
border-collapse: collapse;
}
td {
border: 1px solid black;
padding: 3px 5px;
}
</style>
</head>
<body>
<table>
<tr>
<td>1:1</td>
<td>2:1</td>
<td>3:1</td>
<td>4:1</td>
<td>5:1</td>
</tr>
<tr>
<td>1:2</td>
<td>2:2</td>
<td>3:2</td>
<td>4:2</td>
<td>5:2</td>
</tr>
<tr>
<td>1:3</td>
<td>2:3</td>
<td>3:3</td>
<td>4:3</td>
<td>5:3</td>
</tr>
<tr>
<td>1:4</td>
<td>2:4</td>
<td>3:4</td>
<td>4:4</td>
<td>5:4</td>
</tr>
<tr>
<td>1:5</td>
<td>2:5</td>
<td>3:5</td>
<td>4:5</td>
<td>5:5</td>
</tr>
</table>
<script>
let table = document.body.firstElementChild;
for (let i = 0; i < table.rows.length; i++) {
let row = table.rows[i];
row.cells[i].style.backgroundColor = 'red';
}
</script>
</body>
</html>
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: