最近在看《Javascript高級程序設計》的時候,看到了這樣一句話:“理解NodeList和HTMLCollection,是從整體上透徹理解DOM的關鍵所在。”,所以覺得應該寫一篇關於NodeList和HTMLCollection的博客來好好了解和總結下這方面的知識點。
NodeList
NodeList是一個節點的集合(既可以包含元素和其他節點),在DOM中,節點的類型總共有12種,通過判斷節點的nodeType來判斷節點的類型。
我們可以通過Node.childNodes和document.querySelectAll() (返回NodeList的接口有很多,這里不一一列舉,下同)來獲取到一個NodeList對象。
NodeList對象有個length屬性和item()方法,length表示所獲得的NodeList對象的節點個數,這里還是要強調的是節點,而item()可以傳入一個索引來訪問Nodelist中相應索引的元素。
1 <body> 2 <div id="node"> 3 文本節點 4 <!-- 注釋節點 --> 5 <span>node1</span> 6 <span>node2</span> 7 <span>node3</span> 8 </div> 9 </body> 10 <script> 11 var node = document.getElementById('node'), 12 nodeLists = node.childNodes 13 console.log(nodeLists.length) // 輸出為9 14 </script>
上面的HTML代碼中,“文本節點”和父節點子節點的空格(連着的文本)算做一個文本節點,然后是一個注釋節點和注釋節點和元素節點之間的空格(換行會產生空格,空格算做文本節點)的文本節點,緊接着的是一個元素節點和元素節點之間的換行的文本節點,三個元素節點和元素節點間的兩個文本節點,最后是最后得元素節點和父元素之間的空格產生的文本節點,總共是9個節點。
NodeList對象的一大特點是它返回的內容是動態的(live),也就是說我們上面代碼獲取nodeLists是類似於“指針”的東西,所以在下面代碼中我們在獲取了nodeLists之后再向node中插入一個創建的span標簽后,發現獲取到了nodeLists.length變為10了,但是querySelectorAll這個接口返回的nodeList對象比較特殊,它是個靜態(static)的對象。而且是元素的集合。
1 <body> 2 <div id="node"> 3 文本節點 4 <!-- 注釋節點 --> 5 <span>node1</span> 6 <span>node2</span> 7 <span>node3</span> 8 </div> 9 </body> 10 <script> 11 var node = document.getElementById('node') 12 var nodeLists = node.childNodes 13 var queryNodes = node.querySelectorAll('span') 14 node.appendChild(document.createElement('span')) 15 console.log(nodeLists.length) // 輸出為10 16 console.log(queryNodes.length) //輸出為3 17 </script>
HTMLCollection
HTMLCollection是元素集合,它和NodeList很像,有length屬性來表示HTMLCollection對象的長度,也可以通過elements.item()傳入元素索引來訪問。當時它還有一個nameItem()方法,可以返回集合中name屬性和id屬性值得元素。HTMLDocument 接口的許多屬性都是 HTMLCollection 對象,它提供了訪問諸如表單、圖像和鏈接等文檔元素的便捷方式,比如document.images和document.forms的屬性都是HTMLCollection對象。
1 <body> 2 <img src="test.png" id="image1"> 3 <img src="test.png" id="image2"> 4 <img src="test.png" id="image3"> 5 <img src="test.png" id="image4"> 6 <img src="test.png" id="image5"> 7 <img src="test.png" id="image6"> 8 </body> 9 <script> 10 console.log(document.images.namedItem('image1')) //<img src="test.png" id="image1"> 11 </script>
HTMLCollection的集合和NodeList對象一樣也是動態的,他們獲取的都是節點或元素集合的一個引用。
HTMLCollection和NodeList 實時性
前面都說到了它們連個對象都不是歷史文檔狀態的一個靜態快照,而是實時性的,這個是一個非常令人驚訝的特性,它們能隨着文檔的改變而改變,這個是很值得我們注意的,我們在平常使用一些DOM 接口來返回一些DOM集合的時候,常常會忽視掉這些。
HTMLCollection和NodeList的實時性非常有用,但是,我們有時要迭代一個NodeList或HTMLCollection對象的時候,我們通常會選擇生成當前對象的一個快照或靜態副本:
1 var staticLists = Array.prototype.slice.call(nodeListorHtmlCollection, 0)
這樣的話,我們就可以放心的對當前的DOM集合做一些刪減和插入操作,這個在DOM密集操作的時候很有用。
還有MDN上面提到了一個將NodeList轉化為Array的DOM擴展原型的方法(在IE6/7中存在危險:http://perfectionkills.com/whats-wrong-with-extending-the-dom/):
var arrayMethods = Object.getOwnPropertyNames( Array.prototype ); arrayMethods.forEach( attachArrayMethodsToNodeList ); function attachArrayMethodsToNodeList(methodName) { if(methodName !== "length") { NodeList.prototype[methodName] = Array.prototype[methodName]; } }; var divs = document.getElementsByTagName( 'div' ); var firstDiv = divs[ 0 ]; firstDiv.childNodes.forEach(function( divChild ){ divChild.parentNode.style.color = '#0F0'; });
結語
DOM最初設計是為了解析XML而設計的,之后沿用到HTML上。我們可以把DOM分為兩部分 core 和 html,Core 部分提供最基礎的 XML 解析API說明,HTML 部分專為 HTML 中的 DOM 解析添加其特有的 API。NodeList接口是在core中體現的,HTMLCollection則是在html部分,不同瀏覽器也會實現它們的不同接口,廠商聯盟性質的規范組織出現會讓這些更加規范,也不出現之前返回的是NodeList對象,但是卻是靜態的。
這篇文章很多思想都是自己在平時和網上了一些博客中了解到了,其中加了很多自己的組織和理解,目的在於梳理下一些比較深入的知識點,如果寫的有疏漏和錯誤之處,還請指出。