遍历childNodes


83

我试图像这样遍历childNodes:

var children = element.childNodes;
children.forEach(function(item){
    console.log(item);
});

但是,Uncaught TypeError: undefined is not a function由于forEach功能而输出。我也尝试使用children代替,childNodes但没有任何改变。

有人知道发生了什么吗?

Answers:


121

该变量children是一个NodeList实例,并且NodeLists不是true Array,因此它们不继承该forEach方法。

还有一些浏览器实际上支持它 nodeList.forEach


ES5

您可以使用slicefromArray将转换NodeList为适当的Array

var array = Array.prototype.slice.call(children);

您也可以简单地使用as上下文call来调用forEach和传递它NodeList

[].forEach.call(children, function(child) {});


ES6

您可以使用from方法将转换NodeListArray

var array = Array.from(children);

或者,您也可以像这样使用传播语法...

let array = [ ...children ];


可以使用一种hack,NodeList.prototype.forEach = Array.prototype.forEach然后您可以将其forEach与任何hack一起使用,NodeList而不必每次都进行转换。

NodeList.prototype.forEach = Array.prototype.forEach
var children = element.childNodes;
children.forEach(function(item){
    console.log(item);
});

请参阅深入了解NodeLists,Arrays,转换NodeLists和了解DOM,以获得良好的解释和其他实现方法。


如何将NodeList转换为纯数组?
user3828771 2014年

更新了示例,但阅读了我发布的链接,它解释了所有问题:)
GillesC 2014年

2
或者,你可以这样做:[].forEach.call(element.childNodes, child => console.log(child))
XåpplI'-I0llwlg'I -

2
更加酷炫的es6方法:let items = [ ...children ]将其转换为数组
2016年

2
将Array方法应用于NodeLists有一个主要陷阱:NodeLists(例如node.childNodes)是活动列表,如果在循环中操作DOM,NodeList可能会发生变化,这意味着对forEach()的回调不会被调用列表中的每个元素-或列表中原始元素数量过多-导致无法预测的结果。最好在循环遍历之前将NodeList转换成数组。
stephband,2013年

30

我参加聚会很晚,但是由于element.lastChild.nextSibling === null,以下内容对我来说似乎是最简单的选择:

for(var child=element.firstChild; child!==null; child=child.nextSibling) {
    console.log(child);
}

1
最直接的选择是使用常规的“ for”循环。但是您的选择很有趣。
基里尔·雷兹尼科夫

我这样是最好的,打算实现相同..逻辑并且不需要转换
Ujjwal辛格

23

这是使用for-in循环的方法。

var children = element.childNodes;

for(child in children){
    console.log(children[child]);
}

14
您忘记了检查:if(children.hasOwnProperty(child)){//在此处编码}否则您将遍历不需要的道具,例如“ length”等!
基里尔·雷兹尼科夫

7
更好的是:使用for ... of ...,不过是ES6语法。
Jespertheend

4

尝试for循环。forEach因为它是节点的集合,所以会产生错误nodelist

否则这应该将节点列表转换为数组

function toArray(obj) {
  var array = [];
  for (var i = 0; i < obj.length; i++) { 
    array[i] = obj[i];
  }
return array;
}

或者你可以用这个

var array = Array.prototype.slice.call(obj);

4

无法拒绝使用添加其他方法childElementCount。它返回给定父级的子元素节点数,因此您可以对其进行循环。

for(var i=0, len = parent.childElementCount ; i < len; ++i){
    ... do something with parent.children[i]
    }

3
const results = Array.from(myNodeList.values()).map(parser_item);

NodeList不是Array, 但是NodeList.values()返回一个Array迭代器,因此可以将其转换为Array。


2

试试这个[逆序遍历]:

var childs = document.getElementById('parent').childNodes;
var len = childs.length;
if(len --) do {
    console.log('node: ', childs[len]);
} while(len --);

或[顺序遍历]

var childs = document.getElementById('parent').childNodes;
var len = childs.length, i = -1;
if(++i < len) do {
    console.log('node: ', childs[i]);
} while(++i < len);

简单的for循环比while循环更具可读性。作者不要求反向/逆序遍历。
基里尔·列兹尼科夫

2

这是在上迭代的功能性ES6方法NodeList。此方法使用Array的方式forEach如下:

Array.prototype.forEach.call(element.childNodes, f)

f迭代器函数在哪里,它接收子节点作为它的第一个参数,而接收索引作为第二个参数。

如果您需要多次遍历NodeLists,则可以基于此创建一个小的函数实用程序方法:

const forEach = f => x => Array.prototype.forEach.call(x, f);

// For example, to log all child nodes
forEach((item) => { console.log(item); })(element.childNodes)

// The functional forEach is handy as you can easily created curried functions
const logChildren = forEach((childNode) => { console.log(childNode); })
logChildren(elementA.childNodes)
logChildren(elementB.childNodes)

(您可以对map()和其他Array函数执行相同的技巧。)


0

如果您做很多这样的事情,那么可能需要自己定义函数。

if (typeof NodeList.prototype.forEach == "undefined"){
    NodeList.prototype.forEach = function (cb){
        for (var i=0; i < this.length; i++) {
            var node = this[i];
            cb( node, i );
        }
    };
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.