W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
來自《JavaScript 標(biāo)準(zhǔn)參考教程(alpha)》,by 阮一峰
DOM是JavaScript操作網(wǎng)頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網(wǎng)頁轉(zhuǎn)為一個(gè)JavaScript對象,從而可以用腳本進(jìn)行各種操作(比如增刪內(nèi)容)。
瀏覽器會(huì)根據(jù)DOM模型,將結(jié)構(gòu)化文檔(比如HTML和XML)解析成一系列的節(jié)點(diǎn),再由這些節(jié)點(diǎn)組成一個(gè)樹狀結(jié)構(gòu)(DOM Tree)。所有的節(jié)點(diǎn)和最終的樹狀結(jié)構(gòu),都有規(guī)范的對外接口。所以,DOM可以理解成網(wǎng)頁的編程接口。DOM有自己的國際標(biāo)準(zhǔn),目前的通用版本是DOM 3,下一代版本DOM 4正在擬定中。
嚴(yán)格地說,DOM不屬于JavaScript,但是操作DOM是JavaScript最常見的任務(wù),而JavaScript也是最常用于DOM操作的語言。本章介紹的就是JavaScript對DOM標(biāo)準(zhǔn)的實(shí)現(xiàn)和用法。
DOM的最小組成單位叫做節(jié)點(diǎn)(node)。文檔的樹形結(jié)構(gòu)(DOM樹),就是由各種不同類型的節(jié)點(diǎn)組成。每個(gè)節(jié)點(diǎn)可以看作是文檔樹的一片葉子。
節(jié)點(diǎn)的類型有七種。
Document
:整個(gè)文檔樹的頂層節(jié)點(diǎn)DocumentType
:doctype
標(biāo)簽(比如<!DOCTYPE html>
)Element
:網(wǎng)頁的各種HTML標(biāo)簽(比如<body>
、<a>
等)Attribute
:網(wǎng)頁元素的屬性(比如class="right"
)Text
:標(biāo)簽之間或標(biāo)簽包含的文本Comment
:注釋DocumentFragment
:文檔的片段這七種節(jié)點(diǎn)都屬于瀏覽器原生提供的節(jié)點(diǎn)對象的派生對象,具有一些共同的屬性和方法。
一個(gè)文檔的所有節(jié)點(diǎn),按照所在的層級,可以抽象成一種樹狀結(jié)構(gòu)。這種樹狀結(jié)構(gòu)就是DOM。
最頂層的節(jié)點(diǎn)就是document
節(jié)點(diǎn),它代表了整個(gè)文檔。文檔里面最高一層的HTML標(biāo)簽,一般是<html>
,它構(gòu)成樹結(jié)構(gòu)的根節(jié)點(diǎn)(root node),其他HTML標(biāo)簽節(jié)點(diǎn)都是它的下級。
除了根節(jié)點(diǎn)以外,其他節(jié)點(diǎn)對于周圍的節(jié)點(diǎn)都存在三種關(guān)系。
DOM提供操作接口,用來獲取三種關(guān)系的節(jié)點(diǎn)。其中,子節(jié)點(diǎn)接口包括firstChild
(第一個(gè)子節(jié)點(diǎn))和lastChild
(最后一個(gè)子節(jié)點(diǎn))等屬性,同級節(jié)點(diǎn)接口包括nextSibling
(緊鄰在后的那個(gè)同級節(jié)點(diǎn))和previousSibling
(緊鄰在前的那個(gè)同級節(jié)點(diǎn))屬性。
所有節(jié)點(diǎn)對象都是瀏覽器內(nèi)置的Node
對象的實(shí)例,繼承了Node
屬性和方法。這是所有節(jié)點(diǎn)的共同特征。
以下屬性與節(jié)點(diǎn)對象本身的特征相關(guān)。
nodeName
屬性返回節(jié)點(diǎn)的名稱,nodeType
屬性返回節(jié)點(diǎn)類型的常數(shù)值。具體的返回值,可查閱下方的表格。
類型 | nodeName | nodeType |
---|---|---|
ELEMENT_NODE | 大寫的HTML元素名 | 1 |
ATTRIBUTE_NODE | 等同于Attr.name | 2 |
TEXT_NODE | #text | 3 |
COMMENT_NODE | #comment | 8 |
DOCUMENT_NODE | #document | 9 |
DOCUMENT_FRAGMENT_NODE | #document-fragment | 11 |
DOCUMENT_TYPE_NODE | 等同于DocumentType.name | 10 |
以document
節(jié)點(diǎn)為例,它的nodeName
屬性等于#document
,nodeType
屬性等于9。
document.nodeName // "#document"
document.nodeType // 9
如果是一個(gè)<p>
節(jié)點(diǎn),它的nodeName
是P
,nodeType
是1。文本節(jié)點(diǎn)的nodeName
是#text
,nodeType
是3。
通常來說,使用nodeType
屬性確定一個(gè)節(jié)點(diǎn)的類型,比較方便。
document.querySelector('a').nodeType === 1
// true
document.querySelector('a').nodeType === Node.ELEMENT_NODE
// true
上面兩種寫法是等價(jià)的。
Node.nodeValue
屬性返回一個(gè)字符串,表示當(dāng)前節(jié)點(diǎn)本身的文本值,該屬性可讀寫。
由于只有Text節(jié)點(diǎn)、Comment節(jié)點(diǎn)、XML文檔的CDATA節(jié)點(diǎn)有文本值,因此只有這三類節(jié)點(diǎn)的nodeValue
可以返回結(jié)果,其他類型的節(jié)點(diǎn)一律返回null
。同樣的,也只有這三類節(jié)點(diǎn)可以設(shè)置nodeValue
屬性的值。對于那些返回null
的節(jié)點(diǎn),設(shè)置nodeValue
屬性是無效的。
Node.textContent
屬性返回當(dāng)前節(jié)點(diǎn)和它的所有后代節(jié)點(diǎn)的文本內(nèi)容。
// HTML代碼為
// <div id="divA">This is <span>some</span> text</div>
document.getElementById('divA').textContent
// This is some text
textContent
屬性自動(dòng)忽略當(dāng)前節(jié)點(diǎn)內(nèi)部的HTML標(biāo)簽,返回所有文本內(nèi)容。
該屬性是可讀寫的,設(shè)置該屬性的值,會(huì)用一個(gè)新的文本節(jié)點(diǎn),替換所有原來的子節(jié)點(diǎn)。它還有一個(gè)好處,就是自動(dòng)對HTML標(biāo)簽轉(zhuǎn)義。這很適合用于用戶提供的內(nèi)容。
document.getElementById('foo').textContent = '<p>GoodBye!</p>';
上面代碼在插入文本時(shí),會(huì)將<p>
標(biāo)簽解釋為文本,而不會(huì)當(dāng)作標(biāo)簽處理。
對于Text節(jié)點(diǎn)和Comment節(jié)點(diǎn),該屬性的值與nodeValue
屬性相同。對于其他類型的節(jié)點(diǎn),該屬性會(huì)將每個(gè)子節(jié)點(diǎn)的內(nèi)容連接在一起返回,但是不包括Comment節(jié)點(diǎn)。如果一個(gè)節(jié)點(diǎn)沒有子節(jié)點(diǎn),則返回空字符串。
document
節(jié)點(diǎn)和doctype
節(jié)點(diǎn)的textContent
屬性為null
。如果要讀取整個(gè)文檔的內(nèi)容,可以使用document.documentElement.textContent
。
Node.baseURI
屬性返回一個(gè)字符串,表示當(dāng)前網(wǎng)頁的絕對路徑。如果無法取到這個(gè)值,則返回null
。瀏覽器根據(jù)這個(gè)屬性,計(jì)算網(wǎng)頁上的相對路徑的URL。該屬性為只讀。
// 當(dāng)前網(wǎng)頁的網(wǎng)址為
// http://www.example.com/index.html
document.baseURI
// "http://www.example.com/index.html"
不同節(jié)點(diǎn)都可以調(diào)用這個(gè)屬性(比如document.baseURI
和element.baseURI
),通常它們的值是相同的。
該屬性的值一般由當(dāng)前網(wǎng)址的URL(即window.location
屬性)決定,但是可以使用HTML的<base>
標(biāo)簽,改變該屬性的值。
<base rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" >
<base target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" >
設(shè)置了以后,baseURI
屬性就返回<base>
標(biāo)簽設(shè)置的值。
以下屬性返回當(dāng)前節(jié)點(diǎn)的相關(guān)節(jié)點(diǎn)。
Node.ownerDocument
屬性返回當(dāng)前節(jié)點(diǎn)所在的頂層文檔對象,即document
對象。
var d = p.ownerDocument;
d === document // true
document
對象本身的ownerDocument
屬性,返回null
。
Node.nextSibling
屬性返回緊跟在當(dāng)前節(jié)點(diǎn)后面的第一個(gè)同級節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)后面沒有同級節(jié)點(diǎn),則返回null
。注意,該屬性還包括文本節(jié)點(diǎn)和評論節(jié)點(diǎn)。因此如果當(dāng)前節(jié)點(diǎn)后面有空格,該屬性會(huì)返回一個(gè)文本節(jié)點(diǎn),內(nèi)容為空格。
var el = document.getElementById('div-01').firstChild;
var i = 1;
while (el) {
console.log(i + '. ' + el.nodeName);
el = el.nextSibling;
i++;
}
上面代碼遍歷div-01
節(jié)點(diǎn)的所有子節(jié)點(diǎn)。
下面兩個(gè)表達(dá)式指向同一個(gè)節(jié)點(diǎn)。
document.childNodes[0].childNodes[1]
document.firstChild.firstChild.nextSibling
previousSibling屬性返回當(dāng)前節(jié)點(diǎn)前面的、距離最近的一個(gè)同級節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)前面沒有同級節(jié)點(diǎn),則返回null。
// html代碼如下
// <a><b1 id="b1"/><b2 id="b2"/></a>
document.getElementById("b1").previousSibling // null
document.getElementById("b2").previousSibling.id // "b1"
對于當(dāng)前節(jié)點(diǎn)前面有空格,則previousSibling
屬性會(huì)返回一個(gè)內(nèi)容為空格的文本節(jié)點(diǎn)。
parentNode
屬性返回當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)。對于一個(gè)節(jié)點(diǎn)來說,它的父節(jié)點(diǎn)只可能是三種類型:element
節(jié)點(diǎn)、document
節(jié)點(diǎn)和documentfragment
節(jié)點(diǎn)。
下面代碼是如何從父節(jié)點(diǎn)移除指定節(jié)點(diǎn)。
if (node.parentNode) {
node.parentNode.removeChild(node);
}
對于document節(jié)點(diǎn)和documentfragment節(jié)點(diǎn),它們的父節(jié)點(diǎn)都是null。另外,對于那些生成后還沒插入DOM樹的節(jié)點(diǎn),父節(jié)點(diǎn)也是null。
parentElement屬性返回當(dāng)前節(jié)點(diǎn)的父Element節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)沒有父節(jié)點(diǎn),或者父節(jié)點(diǎn)類型不是Element節(jié)點(diǎn),則返回null。
if (node.parentElement) {
node.parentElement.style.color = "red";
}
上面代碼設(shè)置指定節(jié)點(diǎn)的父Element節(jié)點(diǎn)的CSS屬性。
在IE瀏覽器中,只有Element節(jié)點(diǎn)才有該屬性,其他瀏覽器則是所有類型的節(jié)點(diǎn)都有該屬性。
childNodes屬性返回一個(gè)NodeList集合,成員包括當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)。注意,除了HTML元素節(jié)點(diǎn),該屬性返回的還包括Text節(jié)點(diǎn)和Comment節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)不包括任何子節(jié)點(diǎn),則返回一個(gè)空的NodeList集合。由于NodeList對象是一個(gè)動(dòng)態(tài)集合,一旦子節(jié)點(diǎn)發(fā)生變化,立刻會(huì)反映在返回結(jié)果之中。
var ulElementChildNodes = document.querySelector('ul').childNodes;
firstChild
屬性返回當(dāng)前節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn),如果當(dāng)前節(jié)點(diǎn)沒有子節(jié)點(diǎn),則返回null
(注意,不是undefined
)。
<p id="para-01"><span>First span</span></p>
<script type="text/javascript">
console.log(
document.getElementById('para-01').firstChild.nodeName
) // "span"
</script>
上面代碼中,p
元素的第一個(gè)子節(jié)點(diǎn)是span
元素。
注意,firstChild
返回的除了HTML元素子節(jié)點(diǎn),還可能是文本節(jié)點(diǎn)或評論節(jié)點(diǎn)。
<p id="para-01">
<span>First span</span>
</p>
<script type="text/javascript">
console.log(
document.getElementById('para-01').firstChild.nodeName
) // "#text"
</script>
上面代碼中,p
元素與span
元素之間有空白字符,這導(dǎo)致firstChild
返回的是文本節(jié)點(diǎn)。
Node.lastChild
屬性返回當(dāng)前節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn),如果當(dāng)前節(jié)點(diǎn)沒有子節(jié)點(diǎn),則返回null。
Node.appendChild
方法接受一個(gè)節(jié)點(diǎn)對象作為參數(shù),將其作為最后一個(gè)子節(jié)點(diǎn),插入當(dāng)前節(jié)點(diǎn)。
var p = document.createElement('p');
document.body.appendChild(p);
如果參數(shù)節(jié)點(diǎn)是DOM中已經(jīng)存在的節(jié)點(diǎn),appendChild
方法會(huì)將其從原來的位置,移動(dòng)到新位置。
Node.hasChildNodes
方法返回一個(gè)布爾值,表示當(dāng)前節(jié)點(diǎn)是否有子節(jié)點(diǎn)。
var foo = document.getElementById("foo");
if (foo.hasChildNodes()) {
foo.removeChild(foo.childNodes[0]);
}
上面代碼表示,如果foo節(jié)點(diǎn)有子節(jié)點(diǎn),就移除第一個(gè)子節(jié)點(diǎn)。
hasChildNodes
方法結(jié)合firstChild
屬性和nextSibling
屬性,可以遍歷當(dāng)前節(jié)點(diǎn)的所有后代節(jié)點(diǎn)。
function DOMComb(parent, callback) {
if (parent.hasChildNodes()) {
for (var node = parent.firstChild; node; node = node.nextSibling) {
DOMComb(node, callback);
}
}
callback.call(parent);
}
上面代碼的DOMComb
函數(shù)的第一個(gè)參數(shù)是某個(gè)指定的節(jié)點(diǎn),第二個(gè)參數(shù)是回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)會(huì)依次作用于指定節(jié)點(diǎn),以及指定節(jié)點(diǎn)的所有后代節(jié)點(diǎn)。
function printContent() {
if (this.nodeValue) {
console.log(this.nodeValue);
}
}
DOMComb(document.body, printContent);
Node.cloneNode
方法用于克隆一個(gè)節(jié)點(diǎn)。它接受一個(gè)布爾值作為參數(shù),表示是否同時(shí)克隆子節(jié)點(diǎn),默認(rèn)是false
,即不克隆子節(jié)點(diǎn)。
var cloneUL = document.querySelector('ul').cloneNode(true);
需要注意的是,克隆一個(gè)節(jié)點(diǎn),會(huì)拷貝該節(jié)點(diǎn)的所有屬性,但是會(huì)喪失addEventListener
方法和on-
屬性(即node.onclick = fn
),添加在這個(gè)節(jié)點(diǎn)上的事件回調(diào)函數(shù)。
克隆一個(gè)節(jié)點(diǎn)之后,DOM樹有可能出現(xiàn)兩個(gè)有相同ID屬性(即id="xxx"
)的HTML元素,這時(shí)應(yīng)該修改其中一個(gè)HTML元素的ID屬性。
Node.insertBefore
方法用于將某個(gè)節(jié)點(diǎn)插入當(dāng)前節(jié)點(diǎn)的指定位置。它接受兩個(gè)參數(shù),第一個(gè)參數(shù)是所要插入的節(jié)點(diǎn),第二個(gè)參數(shù)是當(dāng)前節(jié)點(diǎn)的一個(gè)子節(jié)點(diǎn),新的節(jié)點(diǎn)將插在這個(gè)節(jié)點(diǎn)的前面。該方法返回被插入的新節(jié)點(diǎn)。
var text1 = document.createTextNode('1');
var li = document.createElement('li');
li.appendChild(text1);
var ul = document.querySelector('ul');
ul.insertBefore(li, ul.firstChild);
上面代碼使用當(dāng)前節(jié)點(diǎn)的firstChild
屬性,在<ul>
節(jié)點(diǎn)的最前面插入一個(gè)新建的<li>
節(jié)點(diǎn),新節(jié)點(diǎn)變成第一個(gè)子節(jié)點(diǎn)。
parentElement.insertBefore(newElement, parentElement.firstChild);
上面代碼中,如果當(dāng)前節(jié)點(diǎn)沒有任何子節(jié)點(diǎn),parentElement.firstChild
會(huì)返回null
,則新節(jié)點(diǎn)會(huì)成為當(dāng)前節(jié)點(diǎn)的唯一子節(jié)點(diǎn)。
如果insertBefore
方法的第二個(gè)參數(shù)為null
,則新節(jié)點(diǎn)將插在當(dāng)前節(jié)點(diǎn)的最后位置,即變成最后一個(gè)子節(jié)點(diǎn)。
注意,如果所要插入的節(jié)點(diǎn)是當(dāng)前DOM現(xiàn)有的節(jié)點(diǎn),則該節(jié)點(diǎn)將從原有的位置移除,插入新的位置。
由于不存在insertAfter
方法,如果要插在當(dāng)前節(jié)點(diǎn)的某個(gè)子節(jié)點(diǎn)后面,可以用insertBefore
方法結(jié)合nextSibling
屬性模擬。
parentDiv.insertBefore(s1, s2.nextSibling);
上面代碼可以將s1
節(jié)點(diǎn),插在s2
節(jié)點(diǎn)的后面。如果s2
是當(dāng)前節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn),則s2.nextSibling
返回null
,這時(shí)s1
節(jié)點(diǎn)會(huì)插在當(dāng)前節(jié)點(diǎn)的最后,變成當(dāng)前節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn),等于緊跟在s2
的后面。
Node.removeChild
方法接受一個(gè)子節(jié)點(diǎn)作為參數(shù),用于從當(dāng)前節(jié)點(diǎn)移除該子節(jié)點(diǎn)。它返回被移除的子節(jié)點(diǎn)。
var divA = document.getElementById('A');
divA.parentNode.removeChild(divA);
上面代碼是如何移除一個(gè)指定節(jié)點(diǎn)。
注意,這個(gè)方法是在父節(jié)點(diǎn)上調(diào)用的,不是在被移除的節(jié)點(diǎn)上調(diào)用的。
下面是如何移除當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)。
var element = document.getElementById('top');
while (element.firstChild) {
element.removeChild(element.firstChild);
}
被移除的節(jié)點(diǎn)依然存在于內(nèi)存之中,但不再是DOM的一部分。所以,一個(gè)節(jié)點(diǎn)移除以后,依然可以使用它,比如插入到另一個(gè)節(jié)點(diǎn)下面。
Node.replaceChild
方法用于將一個(gè)新的節(jié)點(diǎn),替換當(dāng)前節(jié)點(diǎn)的某一個(gè)子節(jié)點(diǎn)。它接受兩個(gè)參數(shù),第一個(gè)參數(shù)是用來替換的新節(jié)點(diǎn),第二個(gè)參數(shù)將要被替換走的子節(jié)點(diǎn)。它返回被替換走的那個(gè)節(jié)點(diǎn)。
replacedNode = parentNode.replaceChild(newChild, oldChild);
下面是一個(gè)例子。
var divA = document.getElementById('A');
var newSpan = document.createElement('span');
newSpan.textContent = 'Hello World!';
divA.parentNode.replaceChild(newSpan, divA);
上面代碼是如何替換指定節(jié)點(diǎn)。
Node.contains
方法接受一個(gè)節(jié)點(diǎn)作為參數(shù),返回一個(gè)布爾值,表示參數(shù)節(jié)點(diǎn)是否為當(dāng)前節(jié)點(diǎn)的后代節(jié)點(diǎn)。
document.body.contains(node)
上面代碼檢查某個(gè)節(jié)點(diǎn),是否包含在當(dāng)前文檔之中。
注意,如果將當(dāng)前節(jié)點(diǎn)傳入contains方法,會(huì)返回true。雖然從意義上說,一個(gè)節(jié)點(diǎn)不應(yīng)該包含自身。
nodeA.contains(nodeA) // true
compareDocumentPosition
方法的用法,與contains
方法完全一致,返回一個(gè)7個(gè)比特位的二進(jìn)制值,表示參數(shù)節(jié)點(diǎn)與當(dāng)前節(jié)點(diǎn)的關(guān)系。
二進(jìn)制值 | 數(shù)值 | 含義 |
---|---|---|
000000 | 0 | 兩個(gè)節(jié)點(diǎn)相同 |
000001 | 1 | 兩個(gè)節(jié)點(diǎn)不在同一個(gè)文檔(即有一個(gè)節(jié)點(diǎn)不在當(dāng)前文檔) |
000010 | 2 | 參數(shù)節(jié)點(diǎn)在當(dāng)前節(jié)點(diǎn)的前面 |
000100 | 4 | 參數(shù)節(jié)點(diǎn)在當(dāng)前節(jié)點(diǎn)的后面 |
001000 | 8 | 參數(shù)節(jié)點(diǎn)包含當(dāng)前節(jié)點(diǎn) |
010000 | 16 | 當(dāng)前節(jié)點(diǎn)包含參數(shù)節(jié)點(diǎn) |
100000 | 32 | 瀏覽器的私有用途 |
// HTML代碼為
// <div id="mydiv">
// <form>
// <input id="test" />
// </form>
// </div>
var div = document.getElementById('mydiv');
var input = document.getElementById('test');
div.compareDocumentPosition(input) // 20
input.compareDocumentPosition(div) // 10
上面代碼中,節(jié)點(diǎn)div
包含節(jié)點(diǎn)input
,而且節(jié)點(diǎn)input
在節(jié)點(diǎn)div
的后面,所以第一個(gè)compareDocumentPosition
方法返回20
(二進(jìn)制010100
),第二個(gè)compareDocumentPosition
方法返回10
(二進(jìn)制001010
)。
由于compareDocumentPosition返回值的含義,定義在每一個(gè)比特位上,所以如果要檢查某一種特定的含義,就需要使用比特位運(yùn)算符。
var head = document.head;
var body = document.body;
if (head.compareDocumentPosition(body) & 4) {
console.log("文檔結(jié)構(gòu)正確");
} else {
console.log("<head> 不能在 <body> 前面");
}
上面代碼中,compareDocumentPosition的返回值與4(又稱掩碼)進(jìn)行與運(yùn)算(&),得到一個(gè)布爾值,表示head是否在body前面。
在這個(gè)方法的基礎(chǔ)上,可以部署一些特定的函數(shù),檢查節(jié)點(diǎn)的位置。
Node.prototype.before = function (arg) {
return !!(this.compareDocumentPosition(arg) & 2)
}
nodeA.before(nodeB)
上面代碼在Node對象上部署了一個(gè)before方法,返回一個(gè)布爾值,表示參數(shù)節(jié)點(diǎn)是否在當(dāng)前節(jié)點(diǎn)的前面。
isEqualNode方法返回一個(gè)布爾值,用于檢查兩個(gè)節(jié)點(diǎn)是否相等。所謂相等的節(jié)點(diǎn),指的是兩個(gè)節(jié)點(diǎn)的類型相同、屬性相同、子節(jié)點(diǎn)相同。
var targetEl = document.getElementById("targetEl");
var firstDiv = document.getElementsByTagName("div")[0];
targetEl.isEqualNode(firstDiv)
normailize方法用于清理當(dāng)前節(jié)點(diǎn)內(nèi)部的所有Text節(jié)點(diǎn)。它會(huì)去除空的文本節(jié)點(diǎn),并且將毗鄰的文本節(jié)點(diǎn)合并成一個(gè)。
var wrapper = document.createElement("div");
wrapper.appendChild(document.createTextNode("Part 1 "));
wrapper.appendChild(document.createTextNode("Part 2 "));
wrapper.childNodes.length // 2
wrapper.normalize();
wrapper.childNodes.length // 1
上面代碼使用normalize方法之前,wrapper節(jié)點(diǎn)有兩個(gè)Text子節(jié)點(diǎn)。使用normalize方法之后,兩個(gè)Text子節(jié)點(diǎn)被合并成一個(gè)。
該方法是Text.splitText
的逆方法,可以查看《Text節(jié)點(diǎn)》章節(jié),了解更多內(nèi)容。
節(jié)點(diǎn)都是單個(gè)對象,有時(shí)會(huì)需要一種數(shù)據(jù)結(jié)構(gòu),能夠容納多個(gè)節(jié)點(diǎn)。DOM提供兩種集合對象,用于實(shí)現(xiàn)這種節(jié)點(diǎn)的集合:NodeList
和HTMLCollection
。
這兩個(gè)對象都是構(gòu)造函數(shù)。
typeof NodeList // "function"
typeof HTMLCollection // "function"
但是,一般不把它們當(dāng)作函數(shù)使用,甚至都沒有直接使用它們的場合。主要是許多DOM屬性和方法,返回的結(jié)果是NodeList
實(shí)例或HTMLCollection
實(shí)例,所以一般只使用它們的實(shí)例。
NodeList
實(shí)例對象是一個(gè)類似數(shù)組的對象,它的成員是節(jié)點(diǎn)對象。Node.childNodes
、document.querySelectorAll()
返回的都是NodeList
實(shí)例對象。
document.childNodes instanceof NodeList // true
NodeList
實(shí)例對象可能是動(dòng)態(tài)集合,也可能是靜態(tài)集合。所謂動(dòng)態(tài)集合就是一個(gè)活的集合,DOM樹刪除或新增一個(gè)相關(guān)節(jié)點(diǎn),都會(huì)立刻反映在NodeList接口之中。Node.childNodes
返回的,就是一個(gè)動(dòng)態(tài)集合。
var parent = document.getElementById('parent');
parent.childNodes.length // 2
parent.appendChild(document.createElement('div'));
parent.childNodes.length // 3
上面代碼中,parent.childNodes
返回的是一個(gè)NodeList
實(shí)例對象。當(dāng)parent
節(jié)點(diǎn)新增一個(gè)子節(jié)點(diǎn)以后,該對象的成員個(gè)數(shù)就增加了1。
document.querySelectorAll
方法返回的是一個(gè)靜態(tài)集合。DOM內(nèi)部的變化,并不會(huì)實(shí)時(shí)反映在該方法的返回結(jié)果之中。
NodeList
接口實(shí)例對象提供length
屬性和數(shù)字索引,因此可以像數(shù)組那樣,使用數(shù)字索引取出每個(gè)節(jié)點(diǎn),但是它本身并不是數(shù)組,不能使用pop
或push
之類數(shù)組特有的方法。
// 數(shù)組的繼承鏈
myArray --> Array.prototype --> Object.prototype --> null
// NodeList的繼承鏈
myNodeList --> NodeList.prototype --> Object.prototype --> null
從上面的繼承鏈可以看到,NodeList
實(shí)例對象并不繼承Array.prototype
,因此不具有數(shù)組的方法。如果要在NodeList
實(shí)例對象使用數(shù)組方法,可以將NodeList
實(shí)例轉(zhuǎn)為真正的數(shù)組。
var div_list = document.querySelectorAll('div');
var div_array = Array.prototype.slice.call(div_list);
注意,采用上面的方法將NodeList
實(shí)例轉(zhuǎn)為真正的數(shù)組以后,div_array
就是一個(gè)靜態(tài)集合了,不再能動(dòng)態(tài)反映DOM的變化。
另一種方法是通過call
方法,間接在NodeList
實(shí)例上使用數(shù)組方法。
var forEach = Array.prototype.forEach;
forEach.call(element.childNodes, function(child){
child.parentNode.style.color = '#0F0';
});
上面代碼讓數(shù)組的forEach
方法在NodeList
實(shí)例對象上調(diào)用。注意,Chrome瀏覽器在NodeList.prototype
上部署了forEach
方法,所以可以直接使用,但它是非標(biāo)準(zhǔn)的。
遍歷NodeList
實(shí)例對象的首選方法,是使用for
循環(huán)。
for (var i = 0; i < myNodeList.length; ++i) {
var item = myNodeList[i];
}
不要使用for...in
循環(huán)去遍歷NodeList
實(shí)例對象,因?yàn)?code class="highlighter-rouge">for...in循環(huán)會(huì)將非數(shù)字索引的length
屬性和下面要講到的item
方法,也遍歷進(jìn)去,而且不保證各個(gè)成員遍歷的順序。
ES6新增的for...of
循環(huán),也可以正確遍歷NodeList
實(shí)例對象。
var list = document.querySelectorAll('input[type=checkbox]');
for (var item of list) {
item.checked = true;
}
NodeList
實(shí)例對象的item
方法,接受一個(gè)數(shù)字索引作為參數(shù),返回該索引對應(yīng)的成員。如果取不到成員,或者索引不合法,則返回null
。
nodeItem = nodeList.item(index)
// 實(shí)例
var divs = document.getElementsByTagName("div");
var secondDiv = divs.item(1);
上面代碼中,由于數(shù)字索引從零開始計(jì)數(shù),所以取出第二個(gè)成員,要使用數(shù)字索引1
。
所有類似數(shù)組的對象,都可以使用方括號運(yùn)算符取出成員,所以一般情況下,都是使用下面的寫法,而不使用item
方法。
nodeItem = nodeList[index]
HTMLCollection
實(shí)例對象與NodeList
實(shí)例對象類似,也是節(jié)點(diǎn)的集合,返回一個(gè)類似數(shù)組的對象。document.links
、document.forms
、document.images
等屬性,返回的都是HTMLCollection
實(shí)例對象。
HTMLCollection
與NodeList
的區(qū)別有以下幾點(diǎn)。
(1)HTMLCollection
實(shí)例對象的成員只能是Element
節(jié)點(diǎn),NodeList
實(shí)例對象的成員可以包含其他節(jié)點(diǎn)。
(2)HTMLCollection
實(shí)例對象都是動(dòng)態(tài)集合,節(jié)點(diǎn)的變化會(huì)實(shí)時(shí)反映在集合中。NodeList
實(shí)例對象可以是靜態(tài)集合。
(3)HTMLCollection
實(shí)例對象可以用id
屬性或name
屬性引用節(jié)點(diǎn)元素,NodeList
只能使用數(shù)字索引引用。
HTMLCollection
實(shí)例的item
方法,可以根據(jù)成員的位置參數(shù)(從0
開始),返回該成員。如果取不到成員或數(shù)字索引不合法,則返回null
。
var c = document.images;
var img1 = c.item(1);
// 等價(jià)于下面的寫法
var img1 = c[1];
HTMLCollection
實(shí)例的namedItem
方法根據(jù)成員的ID
屬性或name
屬性,返回該成員。如果沒有對應(yīng)的成員,則返回null
。這個(gè)方法是NodeList
實(shí)例不具有的。
// HTML代碼為
// <form id="myForm"></form>
var elem = document.forms.namedItem('myForm');
// 等價(jià)于下面的寫法
var elem = document.forms['myForm'];
由于item
方法和namedItem
方法,都可以用方括號運(yùn)算符代替,所以建議一律使用方括號運(yùn)算符。
不同的節(jié)點(diǎn)除了繼承Node接口以外,還會(huì)繼承其他接口。ParentNode接口用于獲取當(dāng)前節(jié)點(diǎn)的Element子節(jié)點(diǎn),ChildNode接口用于處理當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)(包含但不限于Element子節(jié)點(diǎn))。
ParentNode接口用于獲取Element子節(jié)點(diǎn)。Element節(jié)點(diǎn)、Document節(jié)點(diǎn)和DocumentFragment節(jié)點(diǎn),部署了ParentNode接口。凡是這三類節(jié)點(diǎn),都具有以下四個(gè)屬性,用于獲取Element子節(jié)點(diǎn)。
(1)children
children屬性返回一個(gè)動(dòng)態(tài)的HTMLCollection集合,由當(dāng)前節(jié)點(diǎn)的所有Element子節(jié)點(diǎn)組成。
下面代碼遍歷指定節(jié)點(diǎn)的所有Element子節(jié)點(diǎn)。
if (el.children.length) {
for (var i = 0; i < el.children.length; i++) {
// ...
}
}
(2)firstElementChild
firstElementChild屬性返回當(dāng)前節(jié)點(diǎn)的第一個(gè)Element子節(jié)點(diǎn),如果不存在任何Element子節(jié)點(diǎn),則返回null。
document.firstElementChild.nodeName
// "HTML"
上面代碼中,document節(jié)點(diǎn)的第一個(gè)Element子節(jié)點(diǎn)是<HTML>。
(3)lastElementChild
lastElementChild屬性返回當(dāng)前節(jié)點(diǎn)的最后一個(gè)Element子節(jié)點(diǎn),如果不存在任何Element子節(jié)點(diǎn),則返回null。
document.lastElementChild.nodeName
// "HTML"
上面代碼中,document節(jié)點(diǎn)的最后一個(gè)Element子節(jié)點(diǎn)是<HTML>。
(4)childElementCount
childElementCount屬性返回當(dāng)前節(jié)點(diǎn)的所有Element子節(jié)點(diǎn)的數(shù)目。
ChildNode
接口用于處理子節(jié)點(diǎn)(包含但不限于Element
子節(jié)點(diǎn))。Element
節(jié)點(diǎn)、DocumentType
節(jié)點(diǎn)和CharacterData
接口,部署了ChildNode
接口。凡是這三類節(jié)點(diǎn)(接口),都可以使用下面四個(gè)方法。
(1)remove()
remove方法用于移除當(dāng)前節(jié)點(diǎn)。
el.remove()
上面方法在DOM中移除了el
節(jié)點(diǎn)。注意,調(diào)用這個(gè)方法的節(jié)點(diǎn),是被移除的節(jié)點(diǎn)本身,而不是它的父節(jié)點(diǎn)。
(2)before()
before方法用于在當(dāng)前節(jié)點(diǎn)的前面,插入一個(gè)同級節(jié)點(diǎn)。如果參數(shù)是節(jié)點(diǎn)對象,插入DOM的就是該節(jié)點(diǎn)對象;如果參數(shù)是文本,插入DOM的就是參數(shù)對應(yīng)的文本節(jié)點(diǎn)。
(3)after()
after方法用于在當(dāng)前節(jié)點(diǎn)的后面,插入一個(gè)同級節(jié)點(diǎn)。如果參數(shù)是節(jié)點(diǎn)對象,插入DOM的就是該節(jié)點(diǎn)對象;如果參數(shù)是文本,插入DOM的就是參數(shù)對應(yīng)的文本節(jié)點(diǎn)。
(4)replaceWith()
replaceWith方法使用參數(shù)指定的節(jié)點(diǎn),替換當(dāng)前節(jié)點(diǎn)。如果參數(shù)是節(jié)點(diǎn)對象,替換當(dāng)前節(jié)點(diǎn)的就是該節(jié)點(diǎn)對象;如果參數(shù)是文本,替換當(dāng)前節(jié)點(diǎn)的就是參數(shù)對應(yīng)的文本節(jié)點(diǎn)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: