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 转换为数组