NodeList
对象是一个节点的集合,是由 Node.childNodes
和 document.querySelectorAll
返回的.
属性
length
NodeList 对象
中包含的节点个数.
方法
item ( idx )
- 返回NodeList对象中指定索引的节点,如果索引越界,则
返回null
.等价的写法是nodeList[idx], 不过这种情况下越界访问将返回undefined.
描述
一个“有时实时”的集合
大多数情况下,NodeList
对象都是个实时集合
。意思是说,如果文档中的节点树发生变化,则已经存在的 NodeList 对象也可能会变化。例如,Node.childNodes
是实时的:
var parent = document.getElementById('parent'); var child_nodes = parent.childNodes; console.log(child_nodes.length); // 如果假设结果是“2” parent.appendChild(document.createElement('div')); console.log(child_nodes.length); // 此时的输出是“3”
在另一些情况下,NodeList
是一个静态集合,也就意味着随后对文档对象模型的任何改动都不会影响集合的内容。document.querySelectorAll
返回一个静态的 NodeList
。
特别是当你选择如何遍历 NodeList
中所有项,或缓存列表长度的时候,最好牢记这种区分。
为什么 NodeList 不是数组?
NodeList
对象在某些方面和数组非常相似,看上去可以直接使用从 Array.prototype
上继承的方法。然而,NodeList 没有这些类似数组的方法。
JavaScript 的继承机制是基于原型的。数组元素之所以有一些数组方法(比如 forEach
和 map)
,是因为它的原型链上有这些方法,如下:
myArray --> Array.prototype --> Object.prototype --> null (想要获取一个对象的原型链,可以连续的调用 Object.getPrototypeOf,直到原型链尽头).
forEach
, map
这些方式其实是 Array.prototype
这个对象的方法。
和数组不一样,NodeList
的原型链是这样的:
myNodeList --> NodeList.prototype --> Object.prototype --> null
NodeList.prototype
只有一个 item
方法,没有 Array.prototype
上的那些方法,所以 NodeList
对象用不了它们。
解决办法
一个解决办法就是把 Array.prototype
上的方法添加到 NodeList.prototype
上。可是,要注意扩展DOM对象的原型是非常危险的,尤其是在旧版本的Internet Explorer (6,7,8)中。
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 对象原型的解决办法:
var forEach = Array.prototype.forEach; var divs = document.getElementsByTagName( 'div' ); var firstDiv = divs[ 0 ]; forEach.call(firstDiv.childNodes, function( divChild ){ divChild.parentNode.style.color = '#0F0'; });
请注意,在上面的代码中,将某个宿主对象 (如 NodeList
) 作为 this
传递给原生方法 (如 forEach) 不能保证在所有浏览器中工作,已知在一些浏览器中会失败。
例子
遍历一个 NodeList
对象中的所有的节点可以使用如下代码:
for (var i = 0; i < myNodeList.length; ++i) { var item = myNodeList[i]; // 调用 myNodeList.item(i) 是没有必要的 }
不要尝试使用 for...in
或者 for each...in
来遍历一个NodeList
对象中的元素,因为如果你把上述两个属性也看成 elemnt
对象的话,NodeList
对象中的 length
和 item
属性也会被遍历出来,这可能会导致你的脚本运行出错。此外,for...in
不能保证访问这些属性的顺序。
在支持 for...of 的浏览器中(比如 Firefox 13 及以后版本)for...of
循环将会正确的遍历 NodeList 对象:
var list = document.querySelectorAll( 'input[type=checkbox]' ); for (var item of list) { item.checked = true; }
将 NodeList 转换为 Array
有时候用类似数组的方法来处理 NodeList
里的内容会更加方便。这里有一种技术为了将 NodeList
对象转换为数组:
var div_list = document.querySelectorAll('div'); // 返回 NodeList var div_array = Array.prototype.slice.call(div_list); // 将 NodeList 转换为数组 //ES6 - Array.from(); var div_array_from = Array.from(div_list); //将 NodeList 转换为数组