前兩天我在重溫js dom編程的時候,看到了獲取dom元素這一章,而後看到了getElementsByTagName()和getElementsByClassName(),以後又瞭解到了現代瀏覽器新出的一個DOM API--querySelectorAll().以個人性格,看到這些方法以後我確定是想了解一下它們的不一樣點啦,因此我就翻閱資料,就看到了stackoverflow上面的一個問題css
var temp = document.querySelectorAll(".class"); for (var i=0, max=temp.length; i<max; i++) { temp[i].className = "new_class"; } var temp = document.getElementsByClassName("class"); for (var i=0, max=temp.length; i<max; i++) { temp[i].className = "new_class"; }
運行上述這兩段代碼,假如獲取到的temp的長度都爲3,那麼第一段代碼能將三個元素的className所有更改成new_class",而第二段代碼只能講第一個元素和第三個元素的className更改成"new_class".這裏面的緣由就是動態nodelist和靜態nodelist的區別。
而後我又翻閱資料查找什麼是動態nodelist,什麼是靜態nodelist。因而乎,就有了下面的長篇大論。html
在不一樣版本的瀏覽器中,若是調用獲取多元素的DOM方法(getElement...()),有的會獲得NodeList(多爲舊瀏覽器),有的會獲得HTMLCollection(多爲新瀏覽器)。使用Node Interface的方法,如childNodes,獲得的一般是NodeList,而使用其餘Interface的方法,又有可能獲得HTMLCollection。而NamedNodeMap又和前面二者返回的東西類型也不相同,因此有必要了解一下這三者的區別。node
1. 三者的相同點git
1.1 三者都具備length屬性github
1.2 三者都有item()方法編程
1.3 三個集合都是"動態的",若是對NodeList和HTMLCollection中的元素進行操做都會直接反映到DOM中,所以若是一次性直接在集合中進行DOM操做的話,開銷很是大。(這會在講解動態的時候詳細解釋)瀏覽器
2. 三者的不一樣點緩存
2.1 nodeList裏面包含了全部的節點類型,好比元素節點,文本節點等app
2.2 HTMLCollection裏面只包含元素節點dom
<div> <!-- Comment --> <p>This is Some Text</p> </div>
上面這段代碼,若是做爲NodeList返回,那麼瀏覽器最多會給這個列表5個元素
1.一個
<div>
和註釋間的斷行和空格(或tab)做爲text node(沒錯,標籤之間的空白符號也能夠被解析爲text node2.註釋做爲comment node
3.註釋和
<p>
之間的斷行和空格(或tab)做爲text node,p做爲element4.
</p>
和</div>
之間的斷行和空格(或tab)做爲text node
可是若是是做爲HTMLCollection返回的話,那麼就一個<p>
元素這麼簡單
2.3 NamedNodeMap裏面包含了"Attribute"的集合,例如id,title,name等,集合中的每個元素都是attr類型。
2.4 三個集合所提供的方法也不相同,例如HTMLCollection中提供了namedItem(),而其它兩個集合就沒有提供這個方法
擴展點:
item和namedItem均可以經過[]的縮寫進行調用,有的瀏覽器還支持用()的縮寫進行調用(也就是能夠list[index],list[key]或者list(index),list(key)),以及直接用dot notation調用namedItem(好比list.key)
IE8及如下版本瀏覽器中,註釋屬於HTMLCommentElement,算做Element,所以會出如今HTMLCollection裏
咱們能夠用alert/console.log(document.getElement...)打印出來看下返回的是什麼類型的集合,下面這個連接中講的也算詳細,能夠參考下:http://www.jb51.net/article/2...
ps:以上知識點參考連接:
http://www.cnblogs.com/joyeec...,
http://stackoverflow.com/ques...,
http://stackoverflow.com/ques...
上面咱們說到NodeList,HTMLCollection以及NamedNodeMap都是動態的。也就是說,對底層文檔結構的修改會動態地反映到相關的結合NodeList,HTMLCollection以及NamedNodeMap中。例如:若是先獲取了某個元素的子元素的動態集合NodeList對象,而後又在其餘地方對這個元素進行操做(添加,修改,刪除子元素等操做),這些更改將自動反射到NodeList中,不須要手動進行操做。
由於getElementsByTagName(全部getElement...方法都會返回動態NodeList)方法返回的是一個動態集合,因此只要document發生變化,就會自動更新對應的元素。所以,下面的代碼是一個死循環:
var divs = document.getElementsByTagName("div"); var i=0; while(i < divs.length){ document.body.appendChild(document.createElement("div")); i++; }
死循環的緣由是每次循環都會從新計算divs.length.每次迭代都會添加一個新的<div>
,因此每次i++,對應的divs.length也在增長,因此i永遠比divs.length小,循環終止條件也就永遠不會觸發。
解決上述代碼死循環的辦法能夠是用一個變量存儲divs.length或者改用querySelectorAll():
var divs = document.getElementsByTagName("div"); var i=0,len = divs.length; while(i < len){ document.body.appendChild(document.createElement("div")); i++; }
你可能會以爲這種動態集合是個壞主意, 但經過動態集合能夠保證某些使用很是廣泛的對象在各類狀況下都是同一個,並且動態NodeList比靜態NodeList快不少不少(下面解釋緣由)
querySelectorAll()和querySelector()方法返回的是一個靜態的NodeList,所謂靜態NodeList就是對底層document的更改不會影響到返回的這個NodeList對象.此時返回的NodeList只是querySelectorAll()方法被調用時的文檔狀態的快照。因此下面的代碼不會是死循環:
var divs = document.querySelectorAll("div"); var i=0; while(i < divs.length){ document.body.appendChild(document.createElement("div")); i++; }
在這種狀況下沒有死循環, divs.length的值永遠不會改變, 因此只要不知足循環條件, 就退出循環。
我在某篇文章中看到有人測試了一下getElementsByTagName()比querySelectorAll()快好多倍。
緣由是:動態NodeList對象在瀏覽器中能夠更快地被建立並返回,由於他們不須要預先獲取全部的信息,而靜態NodeList對象從一開始就須要取得並封裝全部相關的數據。兩種對象類型的建立方式是徹底不一樣的。
DynamicNodeList(動態NodeList)對象經過在cache緩存中註冊它的存在並建立。從本質上講,建立一個新的DynamicNodeList是很是輕量級的,由於不須要作任何的前期工做。每次訪問 DynamicNodeList 時, 必須查詢 document 的變化, length 屬性 以及 item() 方法證實了這一點
相比之下,StaticNodeList對象實例由另一個文件建立,而後循環填充全部的數據。在document中執行靜態查詢的前期成本相比DynamicNodeList要顯著提升不少倍。
若是真正的查看WebKit的源碼,你會發現他爲 querySelectorAll() 明確地 建立一個返回對象 ,在其中又使用一個循環來獲取每個結果,並建立最終返回的一個 NodeList.
getElementsTagName()方法速度比querySelectorAll()方法快的根本緣由在於動態NodeList和靜態NodeList對象不一樣。在獲取NodeList時不須要執行不少前期處理操做的動態列表總比獲取返回以前完成各類處理的靜態NodeList要快不少。哪一個方法更好用仍是看你的需求。若是不須要獲取快照,就使用getElement...方法;若是須要靜態快照結果,或者須要使用更復雜的css查詢,則能夠考慮querySelectAll()方法
/**經過querySelectorAll()獲取到的元素集合temp是靜態的快照,因此temp長度不會變化,max始終爲3,因此經過for循環3個對應元素的class名字都被改爲"new_class"**/ var temp = document.querySelectorAll(".class"); for (var i=0, max=temp.length; i<max; i++) { temp[i].className = "new_class"; } /**經過getElementsByClassName()獲取到的元素集合temp是動態的,因此咱們對元素任何的更改都會直接反映到對應的NodeList中; 剛開始temp長度爲3,也就是max爲3,這裏i=0的時候,更改了temp[0]的className爲"new_class",因此temp的長度立刻發生變化,max變爲2; 繼續循環,i=1的時候,temp[1]其實是沒變化前的temp[2]。此時又更改了temp[1]的className爲"new_class",因此temp的長度又發生變化,max變爲1; 繼續循環,i=2的時候,不知足條件,循環結束; 因此temp[0],temp[2]的className都變爲"new_class",而temp[1]沒改變**/ var temp = document.getElementsByClassName("class"); for (var i=0, max=temp.length; i<max; i++) { temp[i].className = "new_class"; }
參考資料連接: