NodeList

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

 

规范

文档标签和贡献者