前两天我在重温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"; }
参考资料连接: