JavaScript DOM 模型概述

2021-04-08 11:15 更新

來自《JavaScript 標準參考教程(alpha)》,by 阮一峰

目錄

基本概念

DOM

DOM是JavaScript操作網(wǎng)頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將網(wǎng)頁轉(zhuǎn)為一個JavaScript對象,從而可以用腳本進行各種操作(比如增刪內(nèi)容)。

瀏覽器會根據(jù)DOM模型,將結(jié)構(gòu)化文檔(比如HTML和XML)解析成一系列的節(jié)點,再由這些節(jié)點組成一個樹狀結(jié)構(gòu)(DOM Tree)。所有的節(jié)點和最終的樹狀結(jié)構(gòu),都有規(guī)范的對外接口。所以,DOM可以理解成網(wǎng)頁的編程接口。DOM有自己的國際標準,目前的通用版本是DOM 3,下一代版本DOM 4正在擬定中。

嚴格地說,DOM不屬于JavaScript,但是操作DOM是JavaScript最常見的任務(wù),而JavaScript也是最常用于DOM操作的語言。本章介紹的就是JavaScript對DOM標準的實現(xiàn)和用法。

節(jié)點

DOM的最小組成單位叫做節(jié)點(node)。文檔的樹形結(jié)構(gòu)(DOM樹),就是由各種不同類型的節(jié)點組成。每個節(jié)點可以看作是文檔樹的一片葉子。

節(jié)點的類型有七種。

  • Document:整個文檔樹的頂層節(jié)點
  • DocumentTypedoctype標簽(比如<!DOCTYPE html>
  • Element:網(wǎng)頁的各種HTML標簽(比如<body>、<a>等)
  • Attribute:網(wǎng)頁元素的屬性(比如class="right"
  • Text:標簽之間或標簽包含的文本
  • Comment:注釋
  • DocumentFragment:文檔的片段

這七種節(jié)點都屬于瀏覽器原生提供的節(jié)點對象的派生對象,具有一些共同的屬性和方法。

節(jié)點樹

一個文檔的所有節(jié)點,按照所在的層級,可以抽象成一種樹狀結(jié)構(gòu)。這種樹狀結(jié)構(gòu)就是DOM。

最頂層的節(jié)點就是document節(jié)點,它代表了整個文檔。文檔里面最高一層的HTML標簽,一般是<html>,它構(gòu)成樹結(jié)構(gòu)的根節(jié)點(root node),其他HTML標簽節(jié)點都是它的下級。

除了根節(jié)點以外,其他節(jié)點對于周圍的節(jié)點都存在三種關(guān)系。

  • 父節(jié)點關(guān)系(parentNode):直接的那個上級節(jié)點
  • 子節(jié)點關(guān)系(childNodes):直接的下級節(jié)點
  • 同級節(jié)點關(guān)系(sibling):擁有同一個父節(jié)點的節(jié)點

DOM提供操作接口,用來獲取三種關(guān)系的節(jié)點。其中,子節(jié)點接口包括firstChild(第一個子節(jié)點)和lastChild(最后一個子節(jié)點)等屬性,同級節(jié)點接口包括nextSibling(緊鄰在后的那個同級節(jié)點)和previousSibling(緊鄰在前的那個同級節(jié)點)屬性。

特征相關(guān)的屬性

所有節(jié)點對象都是瀏覽器內(nèi)置的Node對象的實例,繼承了Node屬性和方法。這是所有節(jié)點的共同特征。

以下屬性與節(jié)點對象本身的特征相關(guān)。

Node.nodeName,Node.nodeType

nodeName屬性返回節(jié)點的名稱,nodeType屬性返回節(jié)點類型的常數(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é)點為例,它的nodeName屬性等于#documentnodeType屬性等于9。

document.nodeName // "#document"
document.nodeType // 9

如果是一個<p>節(jié)點,它的nodeNamePnodeType是1。文本節(jié)點的nodeName#text,nodeType是3。

通常來說,使用nodeType屬性確定一個節(jié)點的類型,比較方便。

document.querySelector('a').nodeType === 1
// true

document.querySelector('a').nodeType === Node.ELEMENT_NODE
// true

上面兩種寫法是等價的。

Node.nodeValue

Node.nodeValue屬性返回一個字符串,表示當前節(jié)點本身的文本值,該屬性可讀寫。

由于只有Text節(jié)點、Comment節(jié)點、XML文檔的CDATA節(jié)點有文本值,因此只有這三類節(jié)點的nodeValue可以返回結(jié)果,其他類型的節(jié)點一律返回null。同樣的,也只有這三類節(jié)點可以設(shè)置nodeValue屬性的值。對于那些返回null的節(jié)點,設(shè)置nodeValue屬性是無效的。

Node.textContent

Node.textContent屬性返回當前節(jié)點和它的所有后代節(jié)點的文本內(nèi)容。

// HTML代碼為
// <div id="divA">This is <span>some</span> text</div>

document.getElementById('divA').textContent
// This is some text

textContent屬性自動忽略當前節(jié)點內(nèi)部的HTML標簽,返回所有文本內(nèi)容。

該屬性是可讀寫的,設(shè)置該屬性的值,會用一個新的文本節(jié)點,替換所有原來的子節(jié)點。它還有一個好處,就是自動對HTML標簽轉(zhuǎn)義。這很適合用于用戶提供的內(nèi)容。

document.getElementById('foo').textContent = '<p>GoodBye!</p>';

上面代碼在插入文本時,會將<p>標簽解釋為文本,而不會當作標簽處理。

對于Text節(jié)點和Comment節(jié)點,該屬性的值與nodeValue屬性相同。對于其他類型的節(jié)點,該屬性會將每個子節(jié)點的內(nèi)容連接在一起返回,但是不包括Comment節(jié)點。如果一個節(jié)點沒有子節(jié)點,則返回空字符串。

document節(jié)點和doctype節(jié)點的textContent屬性為null。如果要讀取整個文檔的內(nèi)容,可以使用document.documentElement.textContent。

Node.baseURI

Node.baseURI屬性返回一個字符串,表示當前網(wǎng)頁的絕對路徑。如果無法取到這個值,則返回null。瀏覽器根據(jù)這個屬性,計算網(wǎng)頁上的相對路徑的URL。該屬性為只讀。

// 當前網(wǎng)頁的網(wǎng)址為
// http://www.example.com/index.html
document.baseURI
// "http://www.example.com/index.html"

不同節(jié)點都可以調(diào)用這個屬性(比如document.baseURIelement.baseURI),通常它們的值是相同的。

該屬性的值一般由當前網(wǎng)址的URL(即window.location屬性)決定,但是可以使用HTML的<base>標簽,改變該屬性的值。

<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>標簽設(shè)置的值。

相關(guān)節(jié)點的屬性

以下屬性返回當前節(jié)點的相關(guān)節(jié)點。

Node.ownerDocument

Node.ownerDocument屬性返回當前節(jié)點所在的頂層文檔對象,即document對象。

var d = p.ownerDocument;
d === document // true

document對象本身的ownerDocument屬性,返回null。

Node.nextSibling

Node.nextSibling屬性返回緊跟在當前節(jié)點后面的第一個同級節(jié)點。如果當前節(jié)點后面沒有同級節(jié)點,則返回null。注意,該屬性還包括文本節(jié)點和評論節(jié)點。因此如果當前節(jié)點后面有空格,該屬性會返回一個文本節(jié)點,內(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é)點的所有子節(jié)點。

下面兩個表達式指向同一個節(jié)點。

document.childNodes[0].childNodes[1]
document.firstChild.firstChild.nextSibling

Node.previousSibling

previousSibling屬性返回當前節(jié)點前面的、距離最近的一個同級節(jié)點。如果當前節(jié)點前面沒有同級節(jié)點,則返回null。

// html代碼如下
// <a><b1 id="b1"/><b2 id="b2"/></a>

document.getElementById("b1").previousSibling // null
document.getElementById("b2").previousSibling.id // "b1"

對于當前節(jié)點前面有空格,則previousSibling屬性會返回一個內(nèi)容為空格的文本節(jié)點。

Node.parentNode

parentNode屬性返回當前節(jié)點的父節(jié)點。對于一個節(jié)點來說,它的父節(jié)點只可能是三種類型:element節(jié)點、document節(jié)點和documentfragment節(jié)點。

下面代碼是如何從父節(jié)點移除指定節(jié)點。

if (node.parentNode) {
  node.parentNode.removeChild(node);
}

對于document節(jié)點和documentfragment節(jié)點,它們的父節(jié)點都是null。另外,對于那些生成后還沒插入DOM樹的節(jié)點,父節(jié)點也是null。

Node.parentElement

parentElement屬性返回當前節(jié)點的父Element節(jié)點。如果當前節(jié)點沒有父節(jié)點,或者父節(jié)點類型不是Element節(jié)點,則返回null。

if (node.parentElement) {
  node.parentElement.style.color = "red";
}

上面代碼設(shè)置指定節(jié)點的父Element節(jié)點的CSS屬性。

在IE瀏覽器中,只有Element節(jié)點才有該屬性,其他瀏覽器則是所有類型的節(jié)點都有該屬性。

Node.childNodes

childNodes屬性返回一個NodeList集合,成員包括當前節(jié)點的所有子節(jié)點。注意,除了HTML元素節(jié)點,該屬性返回的還包括Text節(jié)點和Comment節(jié)點。如果當前節(jié)點不包括任何子節(jié)點,則返回一個空的NodeList集合。由于NodeList對象是一個動態(tài)集合,一旦子節(jié)點發(fā)生變化,立刻會反映在返回結(jié)果之中。

var ulElementChildNodes = document.querySelector('ul').childNodes;

Node.firstChild,Node.lastChild

firstChild屬性返回當前節(jié)點的第一個子節(jié)點,如果當前節(jié)點沒有子節(jié)點,則返回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元素的第一個子節(jié)點是span元素。

注意,firstChild返回的除了HTML元素子節(jié)點,還可能是文本節(jié)點或評論節(jié)點。

<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元素之間有空白字符,這導致firstChild返回的是文本節(jié)點。

Node.lastChild屬性返回當前節(jié)點的最后一個子節(jié)點,如果當前節(jié)點沒有子節(jié)點,則返回null。

節(jié)點對象的方法

Node.appendChild()

Node.appendChild方法接受一個節(jié)點對象作為參數(shù),將其作為最后一個子節(jié)點,插入當前節(jié)點。

var p = document.createElement('p');
document.body.appendChild(p);

如果參數(shù)節(jié)點是DOM中已經(jīng)存在的節(jié)點,appendChild方法會將其從原來的位置,移動到新位置。

Node.hasChildNodes()

Node.hasChildNodes方法返回一個布爾值,表示當前節(jié)點是否有子節(jié)點。

var foo = document.getElementById("foo");

if (foo.hasChildNodes()) {
  foo.removeChild(foo.childNodes[0]);
}

上面代碼表示,如果foo節(jié)點有子節(jié)點,就移除第一個子節(jié)點。

hasChildNodes方法結(jié)合firstChild屬性和nextSibling屬性,可以遍歷當前節(jié)點的所有后代節(jié)點。

function DOMComb(parent, callback) {
  if (parent.hasChildNodes()) {
    for (var node = parent.firstChild; node; node = node.nextSibling) {
      DOMComb(node, callback);
    }
  }
  callback.call(parent);
}

上面代碼的DOMComb函數(shù)的第一個參數(shù)是某個指定的節(jié)點,第二個參數(shù)是回調(diào)函數(shù)。這個回調(diào)函數(shù)會依次作用于指定節(jié)點,以及指定節(jié)點的所有后代節(jié)點。

function printContent() {
  if (this.nodeValue) {
    console.log(this.nodeValue);
  }
}

DOMComb(document.body, printContent);

Node.cloneNode()

Node.cloneNode方法用于克隆一個節(jié)點。它接受一個布爾值作為參數(shù),表示是否同時克隆子節(jié)點,默認是false,即不克隆子節(jié)點。

var cloneUL = document.querySelector('ul').cloneNode(true);

需要注意的是,克隆一個節(jié)點,會拷貝該節(jié)點的所有屬性,但是會喪失addEventListener方法和on-屬性(即node.onclick = fn),添加在這個節(jié)點上的事件回調(diào)函數(shù)。

克隆一個節(jié)點之后,DOM樹有可能出現(xiàn)兩個有相同ID屬性(即id="xxx")的HTML元素,這時應(yīng)該修改其中一個HTML元素的ID屬性。

Node.insertBefore()

Node.insertBefore方法用于將某個節(jié)點插入當前節(jié)點的指定位置。它接受兩個參數(shù),第一個參數(shù)是所要插入的節(jié)點,第二個參數(shù)是當前節(jié)點的一個子節(jié)點,新的節(jié)點將插在這個節(jié)點的前面。該方法返回被插入的新節(jié)點。

var text1 = document.createTextNode('1');
var li = document.createElement('li');
li.appendChild(text1);

var ul = document.querySelector('ul');
ul.insertBefore(li, ul.firstChild);

上面代碼使用當前節(jié)點的firstChild屬性,在<ul>節(jié)點的最前面插入一個新建的<li>節(jié)點,新節(jié)點變成第一個子節(jié)點。

parentElement.insertBefore(newElement, parentElement.firstChild);

上面代碼中,如果當前節(jié)點沒有任何子節(jié)點,parentElement.firstChild會返回null,則新節(jié)點會成為當前節(jié)點的唯一子節(jié)點。

如果insertBefore方法的第二個參數(shù)為null,則新節(jié)點將插在當前節(jié)點的最后位置,即變成最后一個子節(jié)點。

注意,如果所要插入的節(jié)點是當前DOM現(xiàn)有的節(jié)點,則該節(jié)點將從原有的位置移除,插入新的位置。

由于不存在insertAfter方法,如果要插在當前節(jié)點的某個子節(jié)點后面,可以用insertBefore方法結(jié)合nextSibling屬性模擬。

parentDiv.insertBefore(s1, s2.nextSibling);

上面代碼可以將s1節(jié)點,插在s2節(jié)點的后面。如果s2是當前節(jié)點的最后一個子節(jié)點,則s2.nextSibling返回null,這時s1節(jié)點會插在當前節(jié)點的最后,變成當前節(jié)點的最后一個子節(jié)點,等于緊跟在s2的后面。

Node.removeChild()

Node.removeChild方法接受一個子節(jié)點作為參數(shù),用于從當前節(jié)點移除該子節(jié)點。它返回被移除的子節(jié)點。

var divA = document.getElementById('A');
divA.parentNode.removeChild(divA);

上面代碼是如何移除一個指定節(jié)點。

注意,這個方法是在父節(jié)點上調(diào)用的,不是在被移除的節(jié)點上調(diào)用的。

下面是如何移除當前節(jié)點的所有子節(jié)點。

var element = document.getElementById('top');
while (element.firstChild) {
  element.removeChild(element.firstChild);
}

被移除的節(jié)點依然存在于內(nèi)存之中,但不再是DOM的一部分。所以,一個節(jié)點移除以后,依然可以使用它,比如插入到另一個節(jié)點下面。

Node.replaceChild()

Node.replaceChild方法用于將一個新的節(jié)點,替換當前節(jié)點的某一個子節(jié)點。它接受兩個參數(shù),第一個參數(shù)是用來替換的新節(jié)點,第二個參數(shù)將要被替換走的子節(jié)點。它返回被替換走的那個節(jié)點。

replacedNode = parentNode.replaceChild(newChild, oldChild);

下面是一個例子。

var divA = document.getElementById('A');
var newSpan = document.createElement('span');
newSpan.textContent = 'Hello World!';
divA.parentNode.replaceChild(newSpan, divA);

上面代碼是如何替換指定節(jié)點。

Node.contains()

Node.contains方法接受一個節(jié)點作為參數(shù),返回一個布爾值,表示參數(shù)節(jié)點是否為當前節(jié)點的后代節(jié)點。

document.body.contains(node)

上面代碼檢查某個節(jié)點,是否包含在當前文檔之中。

注意,如果將當前節(jié)點傳入contains方法,會返回true。雖然從意義上說,一個節(jié)點不應(yīng)該包含自身。

nodeA.contains(nodeA) // true

Node.compareDocumentPosition()

compareDocumentPosition方法的用法,與contains方法完全一致,返回一個7個比特位的二進制值,表示參數(shù)節(jié)點與當前節(jié)點的關(guān)系。

二進制值 數(shù)值 含義
000000 0 兩個節(jié)點相同
000001 1 兩個節(jié)點不在同一個文檔(即有一個節(jié)點不在當前文檔)
000010 2 參數(shù)節(jié)點在當前節(jié)點的前面
000100 4 參數(shù)節(jié)點在當前節(jié)點的后面
001000 8 參數(shù)節(jié)點包含當前節(jié)點
010000 16 當前節(jié)點包含參數(shù)節(jié)點
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é)點div包含節(jié)點input,而且節(jié)點input在節(jié)點div的后面,所以第一個compareDocumentPosition方法返回20(二進制010100),第二個compareDocumentPosition方法返回10(二進制001010)。

由于compareDocumentPosition返回值的含義,定義在每一個比特位上,所以如果要檢查某一種特定的含義,就需要使用比特位運算符。

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(又稱掩碼)進行與運算(&),得到一個布爾值,表示head是否在body前面。

在這個方法的基礎(chǔ)上,可以部署一些特定的函數(shù),檢查節(jié)點的位置。

Node.prototype.before = function (arg) {
  return !!(this.compareDocumentPosition(arg) & 2)
}

nodeA.before(nodeB)

上面代碼在Node對象上部署了一個before方法,返回一個布爾值,表示參數(shù)節(jié)點是否在當前節(jié)點的前面。

Node.isEqualNode()

isEqualNode方法返回一個布爾值,用于檢查兩個節(jié)點是否相等。所謂相等的節(jié)點,指的是兩個節(jié)點的類型相同、屬性相同、子節(jié)點相同。

var targetEl = document.getElementById("targetEl");
var firstDiv = document.getElementsByTagName("div")[0];

targetEl.isEqualNode(firstDiv)

Node.normalize()

normailize方法用于清理當前節(jié)點內(nèi)部的所有Text節(jié)點。它會去除空的文本節(jié)點,并且將毗鄰的文本節(jié)點合并成一個。

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é)點有兩個Text子節(jié)點。使用normalize方法之后,兩個Text子節(jié)點被合并成一個。

該方法是Text.splitText的逆方法,可以查看《Text節(jié)點》章節(jié),了解更多內(nèi)容。

NodeList對象,HTMLCollection對象

節(jié)點都是單個對象,有時會需要一種數(shù)據(jù)結(jié)構(gòu),能夠容納多個節(jié)點。DOM提供兩種集合對象,用于實現(xiàn)這種節(jié)點的集合:NodeListHTMLCollection。

這兩個對象都是構(gòu)造函數(shù)。

typeof NodeList // "function"
typeof HTMLCollection // "function"

但是,一般不把它們當作函數(shù)使用,甚至都沒有直接使用它們的場合。主要是許多DOM屬性和方法,返回的結(jié)果是NodeList實例或HTMLCollection實例,所以一般只使用它們的實例。

NodeList對象

NodeList實例對象是一個類似數(shù)組的對象,它的成員是節(jié)點對象。Node.childNodes、document.querySelectorAll()返回的都是NodeList實例對象。

document.childNodes instanceof NodeList // true

NodeList實例對象可能是動態(tài)集合,也可能是靜態(tài)集合。所謂動態(tài)集合就是一個活的集合,DOM樹刪除或新增一個相關(guān)節(jié)點,都會立刻反映在NodeList接口之中。Node.childNodes返回的,就是一個動態(tài)集合。

var parent = document.getElementById('parent');
parent.childNodes.length // 2
parent.appendChild(document.createElement('div'));
parent.childNodes.length // 3

上面代碼中,parent.childNodes返回的是一個NodeList實例對象。當parent節(jié)點新增一個子節(jié)點以后,該對象的成員個數(shù)就增加了1。

document.querySelectorAll方法返回的是一個靜態(tài)集合。DOM內(nèi)部的變化,并不會實時反映在該方法的返回結(jié)果之中。

NodeList接口實例對象提供length屬性和數(shù)字索引,因此可以像數(shù)組那樣,使用數(shù)字索引取出每個節(jié)點,但是它本身并不是數(shù)組,不能使用poppush之類數(shù)組特有的方法。

// 數(shù)組的繼承鏈
myArray --> Array.prototype --> Object.prototype --> null

// NodeList的繼承鏈
myNodeList --> NodeList.prototype --> Object.prototype --> null

從上面的繼承鏈可以看到,NodeList實例對象并不繼承Array.prototype,因此不具有數(shù)組的方法。如果要在NodeList實例對象使用數(shù)組方法,可以將NodeList實例轉(zhuǎn)為真正的數(shù)組。

var div_list = document.querySelectorAll('div');
var div_array = Array.prototype.slice.call(div_list);

注意,采用上面的方法將NodeList實例轉(zhuǎn)為真正的數(shù)組以后,div_array就是一個靜態(tài)集合了,不再能動態(tài)反映DOM的變化。

另一種方法是通過call方法,間接在NodeList實例上使用數(shù)組方法。

var forEach = Array.prototype.forEach;

forEach.call(element.childNodes, function(child){
  child.parentNode.style.color = '#0F0';
});

上面代碼讓數(shù)組的forEach方法在NodeList實例對象上調(diào)用。注意,Chrome瀏覽器在NodeList.prototype上部署了forEach方法,所以可以直接使用,但它是非標準的。

遍歷NodeList實例對象的首選方法,是使用for循環(huán)。

for (var i = 0; i < myNodeList.length; ++i) {
  var item = myNodeList[i];
}

不要使用for...in循環(huán)去遍歷NodeList實例對象,因為for...in循環(huán)會將非數(shù)字索引的length屬性和下面要講到的item方法,也遍歷進去,而且不保證各個成員遍歷的順序。

ES6新增的for...of循環(huán),也可以正確遍歷NodeList實例對象。

var list = document.querySelectorAll('input[type=checkbox]');
for (var item of list) {
  item.checked = true;
}

NodeList實例對象的item方法,接受一個數(shù)字索引作為參數(shù),返回該索引對應(yīng)的成員。如果取不到成員,或者索引不合法,則返回null。

nodeItem = nodeList.item(index)

// 實例
var divs = document.getElementsByTagName("div");
var secondDiv = divs.item(1);

上面代碼中,由于數(shù)字索引從零開始計數(shù),所以取出第二個成員,要使用數(shù)字索引1

所有類似數(shù)組的對象,都可以使用方括號運算符取出成員,所以一般情況下,都是使用下面的寫法,而不使用item方法。

nodeItem = nodeList[index]

HTMLCollection對象

HTMLCollection實例對象與NodeList實例對象類似,也是節(jié)點的集合,返回一個類似數(shù)組的對象。document.links、document.forms、document.images等屬性,返回的都是HTMLCollection實例對象。

HTMLCollectionNodeList的區(qū)別有以下幾點。

(1)HTMLCollection實例對象的成員只能是Element節(jié)點,NodeList實例對象的成員可以包含其他節(jié)點。

(2)HTMLCollection實例對象都是動態(tài)集合,節(jié)點的變化會實時反映在集合中。NodeList實例對象可以是靜態(tài)集合。

(3)HTMLCollection實例對象可以用id屬性或name屬性引用節(jié)點元素,NodeList只能使用數(shù)字索引引用。

HTMLCollection實例的item方法,可以根據(jù)成員的位置參數(shù)(從0開始),返回該成員。如果取不到成員或數(shù)字索引不合法,則返回null。

var c = document.images;
var img1 = c.item(1);

// 等價于下面的寫法
var img1 = c[1];

HTMLCollection實例的namedItem方法根據(jù)成員的ID屬性或name屬性,返回該成員。如果沒有對應(yīng)的成員,則返回null。這個方法是NodeList實例不具有的。

// HTML代碼為
// <form id="myForm"></form>
var elem = document.forms.namedItem('myForm');
// 等價于下面的寫法
var elem = document.forms['myForm'];

由于item方法和namedItem方法,都可以用方括號運算符代替,所以建議一律使用方括號運算符。

ParentNode接口,ChildNode接口

不同的節(jié)點除了繼承Node接口以外,還會繼承其他接口。ParentNode接口用于獲取當前節(jié)點的Element子節(jié)點,ChildNode接口用于處理當前節(jié)點的子節(jié)點(包含但不限于Element子節(jié)點)。

ParentNode接口

ParentNode接口用于獲取Element子節(jié)點。Element節(jié)點、Document節(jié)點和DocumentFragment節(jié)點,部署了ParentNode接口。凡是這三類節(jié)點,都具有以下四個屬性,用于獲取Element子節(jié)點。

(1)children

children屬性返回一個動態(tài)的HTMLCollection集合,由當前節(jié)點的所有Element子節(jié)點組成。

下面代碼遍歷指定節(jié)點的所有Element子節(jié)點。

if (el.children.length) {
  for (var i = 0; i < el.children.length; i++) {
    // ...
  }
}

(2)firstElementChild

firstElementChild屬性返回當前節(jié)點的第一個Element子節(jié)點,如果不存在任何Element子節(jié)點,則返回null。

document.firstElementChild.nodeName
// "HTML"

上面代碼中,document節(jié)點的第一個Element子節(jié)點是<HTML>。

(3)lastElementChild

lastElementChild屬性返回當前節(jié)點的最后一個Element子節(jié)點,如果不存在任何Element子節(jié)點,則返回null。

document.lastElementChild.nodeName
// "HTML"

上面代碼中,document節(jié)點的最后一個Element子節(jié)點是<HTML>。

(4)childElementCount

childElementCount屬性返回當前節(jié)點的所有Element子節(jié)點的數(shù)目。

ChildNode 接口

ChildNode接口用于處理子節(jié)點(包含但不限于Element子節(jié)點)。Element節(jié)點、DocumentType節(jié)點和CharacterData接口,部署了ChildNode接口。凡是這三類節(jié)點(接口),都可以使用下面四個方法。

(1)remove()

remove方法用于移除當前節(jié)點。

el.remove()

上面方法在DOM中移除了el節(jié)點。注意,調(diào)用這個方法的節(jié)點,是被移除的節(jié)點本身,而不是它的父節(jié)點。

(2)before()

before方法用于在當前節(jié)點的前面,插入一個同級節(jié)點。如果參數(shù)是節(jié)點對象,插入DOM的就是該節(jié)點對象;如果參數(shù)是文本,插入DOM的就是參數(shù)對應(yīng)的文本節(jié)點。

(3)after()

after方法用于在當前節(jié)點的后面,插入一個同級節(jié)點。如果參數(shù)是節(jié)點對象,插入DOM的就是該節(jié)點對象;如果參數(shù)是文本,插入DOM的就是參數(shù)對應(yīng)的文本節(jié)點。

(4)replaceWith()

replaceWith方法使用參數(shù)指定的節(jié)點,替換當前節(jié)點。如果參數(shù)是節(jié)點對象,替換當前節(jié)點的就是該節(jié)點對象;如果參數(shù)是文本,替換當前節(jié)點的就是參數(shù)對應(yīng)的文本節(jié)點。

參考鏈接

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號