DOM是JavaScript中重要部分之一,在DOM中有一個動態(tài)集合。
這個動態(tài)集合包含節(jié)點的集合(NodeList)、元素屬性的集合(NamedNodeMap)和HTML元素的集合(HTMLCollection)。這三個對象都是類數(shù)組(Array-like),具有像數(shù)組一樣的特性
類數(shù)組:比如arguments
- 具有:指向對象元素的數(shù)字索引下標以及l(fā)ength屬性告訴我們對象的元素個數(shù)
- 不具有:不具有諸如push()、forEach()以及indexOf()等數(shù)組對象具有的方法
將類數(shù)組對象轉換為數(shù)組
//es5
let arr = Array.prototype.slice.call(arguments);
//es6
let arr = Array.from(arguments);
//還可以使用ES6中的擴展運算符...將某些數(shù)據(jù)結構轉換成數(shù)組,這種數(shù)據(jù)結構必須有遍歷器接口。
let arr = [...arguments];
NodeList集合
- NodeList實例是一個類似數(shù)組的對象,它的成員是節(jié)點對象。通過以下方法可以得到NodeList實例。
Node.childNodes
document.querySelectorAll()、
HTMLCollection
- 一個節(jié)點對象的集合,只能包含元素節(jié)點(element),不能包含其他類型的節(jié)點。
- HTMLCollection的集合可以通過getElementsByTagName()、getElementsByName()、document.getElementsByClassName()、document.anchors、document.forms、document.images和documnet.links等方式來獲取。
NameNodeMap集合
DOM中的Element節(jié)點是唯一擁有attributes屬性的一種節(jié)點類型。而attributes屬性中就包含NamedNodeMap集合。NamedNodeMap集合的元素擁有nodeName和nodeValue屬性,分別表示元素節(jié)點名稱和值。
三者的異同
共同點
三者都具有l(wèi)ength屬性
三者都有item()方法
三者都是動態(tài)的,如果對NodeList和HTMLCollection中的元素進行操作都會直接反映到DOM中,因此如果一次性直接在集合中進行DOM操作,開銷非常大
不同之處:
NodeList里面包含了所有的節(jié)點類型
HTMLCollection里面只包含元素節(jié)點
NamedNodeMap里面包含了Attribute的集合,例如id、title、class等,集合中的每一個元素都是attr類型
三者所提供的方法也有不同,例如HTMLCollection中提供了namedItem(),而NodeList和NamedNodeMap兩個集合中沒有namedItem()方法
動態(tài)NodeList和靜態(tài)NodeList
- getElementsByTagName()方法返回一個動態(tài)(live)的HTMLCollection,
- 而querySelectorAll()返回的是一個靜態(tài)(static)的NodeList
動態(tài)集合
DOM中的NodeList和NamedNodeMap對象是動態(tài)的;也就是說,對底層文檔結構的修改會動態(tài)地反映到相關的集合NodeList和NamedNodeMap中。例如,如果先獲取了某個元素(Element)的子元素的動態(tài)集合NodeList對象,然后又在其他地方順序添加更多子元素到這個DOM父元素中(可以說添加、修改、刪除子元素等操作),這些更改將自動反射到NodeList,不需要手動進行其他調用。同樣地,對DOM樹上某個Node節(jié)點的修改,也會實時影響引用了該節(jié)點的NodeList和NamedNodeMap對象。
靜態(tài)集合
querySelectorAll()方法返回的NodeList對象必須是靜態(tài)的,而不能是動態(tài)的。后續(xù)對底層document的更改不能影響到返回的這個NodeList對象。這意味著返回的對象將包含在創(chuàng)建列表那一刻匹配的所有元素節(jié)點
<html>
<head>
<title>DOM Tree</title>
</head>
<body>
<div id="box">
<div>Title</div>
<div class="item">Item1</div>
<div class="item">Item2</div>
<div class="item">Item3</div>
<div class="item">Item4</div>
<div class="item">Item5</div>
</div>
</body>
</html>
let box = document.getElementById('box')
let liveNodeList1 = document.getElementsByTagName('div')
console.log(liveNodeList1)
let liveNodeList2 = document.getElementsByClassName('.item')
console.log(liveNodeList2)
let liveNodeList3 = document.querySelectorAll('.item')
console.log(liveNodeList3)
let children = box.childNodes;
console.log(children, children.length)

為什么動態(tài)集合比靜態(tài)集合更快
為什么說getElementsByTagName()在所有瀏覽器上都比auerySelectorAll()要快好多倍。
- 讓 querySelectorAll() 和 getElementsByTagName() 具有相同的參數(shù)和行為, 但有很大的不同點。 在前一種情況下, 返回的 NodeList 就是方法被調用時刻的文檔狀態(tài)的快照, 而后者總是會隨時根據(jù)document的狀態(tài)而更新
- 使用getElementsByTagName()方法我們得到的結果就像是一個對象的索引,而通過querySelectorAll()方法我們得到的是一個對象的克隆;所以當這個對象數(shù)據(jù)量非常大的時候,顯然克隆這個對象所需要花費的時間是很長的
let box = document.getElementById('box')
let liveNodeList = document.getElementsByTagName('div')
console.log(liveNodeList, liveNodeList.length)
let newEle = document.createElement('div')
newEle.textContent = '新創(chuàng)建的div元素'
box.appendChild(newEle)
console.log(liveNodeList, liveNodeList.length)

let box = document.getElementById('box')
let liveNodeList = document.querySelectorAll('div')
console.log(liveNodeList, liveNodeList.length)
let newEle = document.createElement('div')
newEle.textContent = '新創(chuàng)建的div元素'
box.appendChild(newEle)
console.log(liveNodeList, liveNodeList.length)
liveNodeList = document.querySelectorAll('div')
console.log(liveNodeList, liveNodeList.length)
