DOM 概述
DOM
DOM 是 JavaScript 操作網(wǎng)頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網(wǎng)頁轉(zhuǎn)為一個 JavaScript 對象,從而可以用腳本進(jìn)行各種操作(比如增刪內(nèi)容)。
瀏覽器會根據(jù) DOM 模型,將結(jié)構(gòu)化文檔(比如 HTML 和 XML)解析成一系列的節(jié)點(diǎn),再由這些節(jié)點(diǎn)組成一個樹狀結(jié)構(gòu)(DOM Tree)。所有的節(jié)點(diǎn)和最終的樹狀結(jié)構(gòu),都有規(guī)范的對外接口。
DOM 只是一個接口規(guī)范,可以用各種語言實(shí)現(xiàn)。所以嚴(yán)格地說,DOM 不是 JavaScript 語法的一部分,但是 DOM 操作是 JavaScript 最常見的任務(wù),離開了 DOM,JavaScript 就無法控制網(wǎng)頁。另一方面,JavaScript 也是最常用于 DOM 操作的語言。
節(jié)點(diǎn)
DOM 的最小組成單位叫做節(jié)點(diǎn)(node)。文檔的樹形結(jié)構(gòu)(DOM 樹),就是由各種不同類型的節(jié)點(diǎn)組成。每個節(jié)點(diǎn)可以看作是文檔樹的一片葉子。
節(jié)點(diǎn)的類型有七種。
-
Document:整個文檔樹的頂層節(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)對象Node,上面這七種節(jié)點(diǎn)都繼承了Node,因此具有一些共同的屬性和方法。
節(jié)點(diǎn)樹
一個文檔的所有節(jié)點(diǎn),按照所在的層級,可以抽象成一種樹狀結(jié)構(gòu)。這種樹狀結(jié)構(gòu)就是 DOM 樹。它有一個頂層節(jié)點(diǎn),下一層都是頂層節(jié)點(diǎn)的子節(jié)點(diǎn),然后子節(jié)點(diǎn)又有自己的子節(jié)點(diǎn),就這樣層層衍生出一個金字塔結(jié)構(gòu),倒過來就像一棵樹。
瀏覽器原生提供document節(jié)點(diǎn),代表整個文檔。
文檔的第一層有兩個節(jié)點(diǎn),第一個是文檔類型節(jié)點(diǎn)(<!doctype html>),第二個是 HTML 網(wǎng)頁的頂層容器標(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)系。
- 父節(jié)點(diǎn)關(guān)系(
parentNode):直接的那個上級節(jié)點(diǎn) - 子節(jié)點(diǎn)關(guān)系(
childNodes):直接的下級節(jié)點(diǎn) - 同級節(jié)點(diǎn)關(guān)系(
sibling):擁有同一個父節(jié)點(diǎn)的節(jié)點(diǎn)
DOM提供操作接口,用來獲取這三種關(guān)系的節(jié)點(diǎn)。比如,子節(jié)點(diǎn)接口包括firstChild(第一個子節(jié)點(diǎn))和lastChild(最后一個子節(jié)點(diǎn))等屬性,同級節(jié)點(diǎn)接口包括nextSibling(緊鄰在后的那個同級節(jié)點(diǎn))和previousSibling(緊鄰在前的那個同級節(jié)點(diǎn))屬性。
Node 接口
所有DOM節(jié)點(diǎn)對象都繼承了Node接口,擁有一些共同的屬性和方法。這是DOM操作的基礎(chǔ)。
屬性
Node.prototype.nodeType
nodeType屬性返回一個整數(shù)值,表示節(jié)點(diǎn)的類型。
document.nodeType // 9
Node對象定義了幾個常量,對應(yīng)這些類型值。
document.nodeType === Node.DOCUMENT_NODE // true
上面代碼中,文檔節(jié)點(diǎn)的nodeType屬性等于常量Node.DOCUMENT_NODE。
不同節(jié)點(diǎn)的nodeType屬性值和對應(yīng)的常量如下。
- 文檔節(jié)點(diǎn)(
document):9,對應(yīng)常量Node.DOCUMENT_NODE - 元素節(jié)點(diǎn)(
element):1,對應(yīng)常量Node.ELEMENT_NODE - 屬性節(jié)點(diǎn)(
attr):2,對應(yīng)常量Node.ATTRIBUTE_NODE - 文本節(jié)點(diǎn)(
text):3,對應(yīng)常量Node.TEXT_NODE - 文檔片斷節(jié)點(diǎn)(
DocumentFragment):11,對應(yīng)常量Node.DOCUMENT_FRAGMENT_NODE - 文檔類型節(jié)點(diǎn)(
DocumentType):10,對應(yīng)常量Node.DOCUMENT_TYPE_NODE - 注釋節(jié)點(diǎn)(
Comment):8,對應(yīng)常量Node.COMMENT_NODE
確定節(jié)點(diǎn)類型時,使用nodeType屬性是常用方法。
var node = document.documentElement.firstChild;
if (node.nodeType === Node.ELEMENT_NODE) {
console.log('該節(jié)點(diǎn)是元素節(jié)點(diǎn)');
}
Node.prototype.nodeName
nodeName屬性返回節(jié)點(diǎn)的名稱。
// <div id="d1">hello world</div>
var div = document.getElementById('d1');
div.nodeName // "DIV"
上面代碼中,元素節(jié)點(diǎn)<div>的nodeName屬性就是大寫的標(biāo)簽名DIV。
不同節(jié)點(diǎn)的nodeName屬性值如下。
- 文檔節(jié)點(diǎn)(
document):#document - 元素節(jié)點(diǎn)(
element):大寫的標(biāo)簽名 - 屬性節(jié)點(diǎn)(
attr):屬性的名稱 - 文本節(jié)點(diǎn)(
text):#text - 文檔片斷節(jié)點(diǎn)(
DocumentFragment):#document-fragment - 文檔類型節(jié)點(diǎn)(
DocumentType):文檔的類型 - 注釋節(jié)點(diǎn)(
Comment):#comment
Node.prototype.nodeValue
nodeValue屬性返回一個字符串,表示當(dāng)前節(jié)點(diǎn)本身的文本值,該屬性可讀寫。
只有文本節(jié)點(diǎn)(text)、注釋節(jié)點(diǎn)(comment)和屬性節(jié)點(diǎn)(attr)有文本值,因此這三類節(jié)點(diǎn)的nodeValue可以返回結(jié)果,其他類型的節(jié)點(diǎn)一律返回null。同樣的,也只有這三類節(jié)點(diǎn)可以設(shè)置nodeValue屬性的值,其他類型的節(jié)點(diǎn)設(shè)置無效。
// HTML 代碼如下
// <div id="d1">hello world</div>
var div = document.getElementById('d1');
div.nodeValue // null
div.firstChild.nodeValue // "hello world"
上面代碼中,div是元素節(jié)點(diǎn),nodeValue屬性返回null。div.firstChild是文本節(jié)點(diǎn),所以可以返回文本值。
Node.prototype.textContent
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)前節(jié)點(diǎn)內(nèi)部的 HTML 標(biāo)簽,返回所有文本內(nèi)容。
該屬性是可讀寫的,設(shè)置該屬性的值,會用一個新的文本節(jié)點(diǎn),替換所有原來的子節(jié)點(diǎn)。它還有一個好處,就是自動對 HTML 標(biāo)簽轉(zhuǎn)義。這很適合用于用戶提供的內(nèi)容。
document.getElementById('foo').textContent = '<p>GoodBye!</p>';
上面代碼在插入文本時,會將<p>標(biāo)簽解釋為文本,而不會當(dāng)作標(biāo)簽處理。
對于文本節(jié)點(diǎn)(text)、注釋節(jié)點(diǎn)(comment)和屬性節(jié)點(diǎn)(attr),textContent屬性的值與nodeValue屬性相同。對于其他類型的節(jié)點(diǎn),該屬性會將每個子節(jié)點(diǎn)(不包括注釋節(jié)點(diǎn))的內(nèi)容連接在一起返回。如果一個節(jié)點(diǎn)沒有子節(jié)點(diǎn),則返回空字符串。
文檔節(jié)點(diǎn)(document)和文檔類型節(jié)點(diǎn)(doctype)的textContent屬性為null。如果要讀取整個文檔的內(nèi)容,可以使用document.documentElement.textContent。
Node.prototype.baseURI
baseURI屬性返回一個字符串,表示當(dāng)前網(wǎng)頁的絕對路徑。瀏覽器根據(jù)這個屬性,計(jì)算網(wǎng)頁上的相對路徑的 URL。該屬性為只讀。
// 當(dāng)前網(wǎng)頁的網(wǎng)址為
// http://www.example.com/index.html
document.baseURI
// "http://www.example.com/index.html"
如果無法讀到網(wǎng)頁的 URL,baseURI屬性返回null。
該屬性的值一般由當(dāng)前網(wǎng)址的 URL(即window.location屬性)決定,但是可以使用 HTML 的<base>標(biāo)簽,改變該屬性的值。
<base >
設(shè)置了以后,baseURI屬性就返回<base>標(biāo)簽設(shè)置的值。
Node.prototype.ownerDocument
Node.ownerDocument屬性返回當(dāng)前節(jié)點(diǎn)所在的頂層文檔對象,即document對象。
var d = p.ownerDocument;
d === document // true
document對象本身的ownerDocument屬性,返回null。
Node.prototype.nextSibling
Node.nextSibling屬性返回緊跟在當(dāng)前節(jié)點(diǎn)后面的第一個同級節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)后面沒有同級節(jié)點(diǎn),則返回null。
// HTML 代碼如下
// <div id="d1">hello</div><div id="d2">world</div>
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d1.nextSibling === d2 // true
注意,該屬性還包括文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)。因此如果當(dāng)前節(jié)點(diǎn)后面有空格,該屬性會返回一個文本節(jié)點(diǎn),內(nèi)容為空格。
nextSibling屬性可以用來遍歷所有子節(jié)點(diǎn)。
var el = document.getElementById('div1').firstChild;
while (el !== null) {
console.log(el.nodeName);
el = el.nextSibling;
}
上面代碼遍歷div1節(jié)點(diǎn)的所有子節(jié)點(diǎn)。
Node.prototype.previousSibling
previousSibling屬性返回當(dāng)前節(jié)點(diǎn)前面的、距離最近的一個同級節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)前面沒有同級節(jié)點(diǎn),則返回null。
// <div id="d1">hello</div><div id="d2">world</div>
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d2.previousSibling === d1 // true
注意,該屬性還包括文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)。因此如果當(dāng)前節(jié)點(diǎn)前面有空格,該屬性會返回一個文本節(jié)點(diǎn),內(nèi)容為空格。
Node.prototype.parentNode
parentNode屬性返回當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)。對于一個節(jié)點(diǎn)來說,它的父節(jié)點(diǎn)只可能是三種類型:元素節(jié)點(diǎn)(element)、文檔節(jié)點(diǎn)(document)和文檔片段節(jié)點(diǎn)(documentfragment)。
if (node.parentNode) {
node.parentNode.removeChild(node);
}
上面代碼中,通過node.parentNode屬性將node節(jié)點(diǎn)從文檔里面移除。
文檔節(jié)點(diǎn)(document)和文檔片段節(jié)點(diǎn)(documentfragment)的父節(jié)點(diǎn)都是null。另外,對于那些生成后還沒插入 DOM 樹的節(jié)點(diǎn),父節(jié)點(diǎn)也是null。
Node.prototype.parentElement
parentElement屬性返回當(dāng)前節(jié)點(diǎn)的父元素節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)沒有父節(jié)點(diǎn),或者父節(jié)點(diǎn)類型不是元素節(jié)點(diǎn),則返回null。
if (node.parentElement) {
node.parentElement.style.color = 'red';
}
由于父節(jié)點(diǎn)只可能是三種類型:元素節(jié)點(diǎn)、文檔節(jié)點(diǎn)(document)和文檔片段節(jié)點(diǎn)(documentfragment)。parentElement屬性相當(dāng)于把后兩種父節(jié)點(diǎn)都排除了。
Node.prototype.firstChild,Node.prototype.lastChild
firstChild屬性返回當(dāng)前節(jié)點(diǎn)的第一個子節(jié)點(diǎn),如果當(dāng)前節(jié)點(diǎn)沒有子節(jié)點(diǎn),則返回null。
// <p id="p1"><span>First span</span></p>
var p1 = document.getElementById('p1');
p1.firstChild.nodeName // "SPAN"
注意,firstChild返回的除了元素節(jié)點(diǎn),還可能是文本節(jié)點(diǎn)或注釋節(jié)點(diǎn)。
// <p id="p1">
// <span>First span</span>
// </p>
var p1 = document.getElementById('p1');
p1.firstChild.nodeName // "#text"
上面代碼中,p元素與span元素之間有空白字符,這導(dǎo)致firstChild返回的是文本節(jié)點(diǎn)。
lastChild屬性返回當(dāng)前節(jié)點(diǎn)的最后一個子節(jié)點(diǎn),如果當(dāng)前節(jié)點(diǎn)沒有子節(jié)點(diǎn),則返回null。用法與firstChild屬性相同。
Node.prototype.childNodes
childNodes屬性返回一個類似數(shù)組的對象(NodeList集合),成員包括當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn)。
var children = document.querySelector('ul').childNodes;
使用該屬性,可以遍歷某個節(jié)點(diǎn)的所有子節(jié)點(diǎn)。
var div = document.getElementById('div1');
var children = div.childNodes;
for (var i = 0; i < children.length; i++) {
// ...
}
文檔節(jié)點(diǎn)(document)就有兩個子節(jié)點(diǎn):文檔類型節(jié)點(diǎn)(docType)和HTML根元素節(jié)點(diǎn)。
var children = document.childNodes;
for (var i = 0; i < children.length; i++) {
console.log(children[i].nodeType);
}
// 10
// 1
上面代碼中,文檔節(jié)點(diǎn)的第一個子節(jié)點(diǎn)的類型是10(即文檔類型節(jié)點(diǎn)),第二個子節(jié)點(diǎn)的類型是1(即元素節(jié)點(diǎn))。
注意,除了元素節(jié)點(diǎn),childNodes屬性的返回值還包括文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)。如果當(dāng)前節(jié)點(diǎn)不包括任何子節(jié)點(diǎn),則返回一個空的NodeList集合。由于NodeList對象是一個動態(tài)集合,一旦子節(jié)點(diǎn)發(fā)生變化,立刻會反映在返回結(jié)果之中。
Node.prototype.isConnected
isConnected屬性返回一個布爾值,表示當(dāng)前節(jié)點(diǎn)是否在文檔之中。
var test = document.createElement('p');
test.isConnected // false
document.body.appendChild(test);
test.isConnected // true
方法
Node.prototype.appendChild()
appendChild()方法接受一個節(jié)點(diǎn)對象作為參數(shù),將其作為最后一個子節(jié)點(diǎn),插入當(dāng)前節(jié)點(diǎn)。該方法的返回值就是插入文檔的子節(jié)點(diǎn)。
var p = document.createElement('p');
document.body.appendChild(p);
上面代碼新建一個<p>節(jié)點(diǎn),將其插入document.body的尾部。
如果參數(shù)節(jié)點(diǎn)是 DOM 已經(jīng)存在的節(jié)點(diǎn),appendChild()方法會將其從原來的位置,移動到新位置。
var div = document.getElementById('myDiv');
document.body.appendChild(div);
上面代碼中,插入的是一個已經(jīng)存在的節(jié)點(diǎn)myDiv,結(jié)果就是該節(jié)點(diǎn)會從原來的位置,移動到document.body的尾部。
如果appendChild()方法的參數(shù)是DocumentFragment節(jié)點(diǎn),那么插入的是DocumentFragment的所有子節(jié)點(diǎn),而不是DocumentFragment節(jié)點(diǎn)本身。返回值是一個空的DocumentFragment節(jié)點(diǎn)。
Node.prototype.hasChildNodes()
hasChildNodes方法返回一個布爾值,表示當(dāng)前節(jié)點(diǎn)是否有子節(jié)點(diǎn)。
var foo = document.getElementById('foo');
if (foo.hasChildNodes()) {
foo.removeChild(foo.childNodes[0]);
}
注意,子節(jié)點(diǎn)包括所有類型的節(jié)點(diǎn),并不僅僅是元素節(jié)點(diǎn)。哪怕節(jié)點(diǎn)只包含一個空格,hasChildNodes方法也會返回true。
判斷一個節(jié)點(diǎn)有沒有子節(jié)點(diǎn),有許多種方法,下面是其中的三種。
node.hasChildNodes()node.firstChild !== nullnode.childNodes && node.childNodes.length > 0
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(parent);
}
// 用法
DOMComb(document.body, console.log)
上面代碼中,DOMComb函數(shù)的第一個參數(shù)是某個指定的節(jié)點(diǎn),第二個參數(shù)是回調(diào)函數(shù)。這個回調(diào)函數(shù)會依次作用于指定節(jié)點(diǎn),以及指定節(jié)點(diǎn)的所有后代節(jié)點(diǎn)。
Node.prototype.cloneNode()
cloneNode方法用于克隆一個節(jié)點(diǎn)。它接受一個布爾值作為參數(shù),表示是否同時克隆子節(jié)點(diǎn)。它的返回值是一個克隆出來的新節(jié)點(diǎn)。
var cloneUL = document.querySelector('ul').cloneNode(true);
該方法有一些使用注意點(diǎn)。
- 克隆一個節(jié)點(diǎn),會拷貝該節(jié)點(diǎn)的所有屬性,但是會喪失
addEventListener方法和on-屬性(即node.onclick = fn),添加在這個節(jié)點(diǎn)上的事件回調(diào)函數(shù)。 - 該方法返回的節(jié)點(diǎn)不在文檔之中,即沒有任何父節(jié)點(diǎn),必須使用諸如
Node.appendChild這樣的方法添加到文檔之中。 - 克隆一個節(jié)點(diǎn)之后,DOM 有可能出現(xiàn)兩個有相同
id屬性(即id="xxx")的網(wǎng)頁元素,這時應(yīng)該修改其中一個元素的id屬性。如果原節(jié)點(diǎn)有name屬性,可能也需要修改。
Node.prototype.insertBefore()
insertBefore方法用于將某個節(jié)點(diǎn)插入父節(jié)點(diǎn)內(nèi)部的指定位置。
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
insertBefore方法接受兩個參數(shù),第一個參數(shù)是所要插入的節(jié)點(diǎn)newNode,第二個參數(shù)是父節(jié)點(diǎn)parentNode內(nèi)部的一個子節(jié)點(diǎn)referenceNode。newNode將插在referenceNode這個子節(jié)點(diǎn)的前面。返回值是插入的新節(jié)點(diǎn)newNode。
var p = document.createElement('p');
document.body.insertBefore(p, document.body.firstChild);
如果insertBefore方法的第二個參數(shù)為null,則新節(jié)點(diǎn)將插在當(dāng)前節(jié)點(diǎn)內(nèi)部的最后位置,即變成最后一個子節(jié)點(diǎn)。
var p = document.createElement('p');
document.body.insertBefore(p, null);
上面代碼中,p將成為document.body的最后一個子節(jié)點(diǎn)。這也說明insertBefore的第二個參數(shù)不能省略。
注意,如果所要插入的節(jié)點(diǎn)是當(dāng)前 DOM 現(xiàn)有的節(jié)點(diǎn),則該節(jié)點(diǎn)將從原有的位置移除,插入新的位置。
由于不存在insertAfter方法,如果新節(jié)點(diǎn)要插在父節(jié)點(diǎn)的某個子節(jié)點(diǎn)后面,可以用insertBefore方法結(jié)合nextSibling屬性模擬。
parent.insertBefore(s1, s2.nextSibling);
上面代碼中,parent是父節(jié)點(diǎn),s1是一個全新的節(jié)點(diǎn),s2是可以將s1節(jié)點(diǎn),插在s2節(jié)點(diǎn)的后面。如果s2是當(dāng)前節(jié)點(diǎn)的最后一個子節(jié)點(diǎn),則s2.nextSibling返回null,這時s1節(jié)點(diǎn)會插在當(dāng)前節(jié)點(diǎn)的最后,變成當(dāng)前節(jié)點(diǎn)的最后一個子節(jié)點(diǎn),等于緊跟在s2的后面。
如果要插入的節(jié)點(diǎn)是DocumentFragment類型,那么插入的將是DocumentFragment的所有子節(jié)點(diǎn),而不是DocumentFragment節(jié)點(diǎn)本身。返回值將是一個空的DocumentFragment節(jié)點(diǎn)。
Node.prototype.removeChild()
removeChild方法接受一個子節(jié)點(diǎn)作為參數(shù),用于從當(dāng)前節(jié)點(diǎn)移除該子節(jié)點(diǎn)。返回值是移除的子節(jié)點(diǎn)。
var divA = document.getElementById('A');
divA.parentNode.removeChild(divA);
上面代碼移除了divA節(jié)點(diǎn)。注意,這個方法是在divA的父節(jié)點(diǎn)上調(diào)用的,不是在divA上調(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 的一部分。所以,一個節(jié)點(diǎn)移除以后,依然可以使用它,比如插入到另一個節(jié)點(diǎn)下面。
如果參數(shù)節(jié)點(diǎn)不是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn),removeChild方法將報(bào)錯。
Node.prototype.replaceChild()
replaceChild方法用于將一個新的節(jié)點(diǎn),替換當(dāng)前節(jié)點(diǎn)的某一個子節(jié)點(diǎn)。
var replacedNode = parentNode.replaceChild(newChild, oldChild);
上面代碼中,replaceChild方法接受兩個參數(shù),第一個參數(shù)newChild是用來替換的新節(jié)點(diǎn),第二個參數(shù)oldChild是將要替換走的子節(jié)點(diǎn)。返回值是替換走的那個節(jié)點(diǎn)oldChild。
var divA = document.getElementById('divA');
var newSpan = document.createElement('span');
newSpan.textContent = 'Hello World!';
divA.parentNode.replaceChild(newSpan, divA);
Node.prototype.contains()
contains方法返回一個布爾值,表示參數(shù)節(jié)點(diǎn)是否滿足以下三個條件之一。
- 參數(shù)節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn)。
- 參數(shù)節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)。
- 參數(shù)節(jié)點(diǎn)為當(dāng)前節(jié)點(diǎn)的后代節(jié)點(diǎn)。
document.body.contains(node)
上面代碼檢查參數(shù)節(jié)點(diǎn)node,是否包含在當(dāng)前文檔之中。
注意,當(dāng)前節(jié)點(diǎn)傳入contains方法,返回true。
nodeA.contains(nodeA) // true
Node.prototype.compareDocumentPosition()
compareDocumentPosition方法的用法,與contains方法完全一致,返回一個六個比特位的二進(jìn)制值,表示參數(shù)節(jié)點(diǎn)與當(dāng)前節(jié)點(diǎn)的關(guān)系。
| 二進(jìn)制值 | 十進(jìn)制值 | 含義 |
|---|---|---|
| 000000 | 0 | 兩個節(jié)點(diǎn)相同 |
| 000001 | 1 | 兩個節(jié)點(diǎn)不在同一個文檔(即有一個節(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 | 瀏覽器內(nèi)部使用 |
// 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(二進(jìn)制010000),而且節(jié)點(diǎn)input在節(jié)點(diǎn)div的后面(二進(jìn)制000100),所以第一個compareDocumentPosition方法返回20(二進(jìn)制010100,即010000 + 000100),第二個compareDocumentPosition方法返回10(二進(jìn)制001010)。
由于compareDocumentPosition返回值的含義,定義在每一個比特位上,所以如果要檢查某一種特定的含義,就需要使用比特位運(yùn)算符。
var head = document.head;
var body = document.body;
if (head.compareDocumentPosition(body) & 4) {
console.log('文檔結(jié)構(gòu)正確');
} else {
console.log('<body> 不能在 <head> 前面');
}
上面代碼中,compareDocumentPosition的返回值與4(又稱掩碼)進(jìn)行與運(yùn)算(&),得到一個布爾值,表示<head>是否在<body>前面。
Node.prototype.isEqualNode(),Node.prototype.isSameNode()
isEqualNode方法返回一個布爾值,用于檢查兩個節(jié)點(diǎn)是否相等。所謂相等的節(jié)點(diǎn),指的是兩個節(jié)點(diǎn)的類型相同、屬性相同、子節(jié)點(diǎn)相同。
var p1 = document.createElement('p');
var p2 = document.createElement('p');
p1.isEqualNode(p2) // true
isSameNode方法返回一個布爾值,表示兩個節(jié)點(diǎn)是否為同一個節(jié)點(diǎn)。
var p1 = document.createElement('p');
var p2 = document.createElement('p');
p1.isSameNode(p2) // false
p1.isSameNode(p1) // true
Node.prototype.normalize()
normalize方法用于清理當(dāng)前節(jié)點(diǎn)內(nèi)部的所有文本節(jié)點(diǎn)(text)。它會去除空的文本節(jié)點(diǎn),并且將毗鄰的文本節(jié)點(diǎn)合并成一個,也就是說不存在空的文本節(jié)點(diǎn),以及毗鄰的文本節(jié)點(diǎn)。
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)有兩個毗鄰的文本子節(jié)點(diǎn)。使用normalize方法之后,兩個文本子節(jié)點(diǎn)被合并成一個。
該方法是Text.splitText的逆方法。
Node.prototype.getRootNode()
getRootNode()方法返回當(dāng)前節(jié)點(diǎn)所在文檔的根節(jié)點(diǎn)document,與ownerDocument屬性的作用相同。
document.body.firstChild.getRootNode() === document
// true
document.body.firstChild.getRootNode() === document.body.firstChild.ownerDocument
// true
該方法可用于document節(jié)點(diǎn)自身,這一點(diǎn)與document.ownerDocument不同。
document.getRootNode() // document
document.ownerDocument // null