对于HTMLCollection元素的循环

335 javascript dom

我正在尝试设置所有元素的get id HTMLCollectionOf.我写了以下代码:

var list = document.getElementsByClassName("events");
console.log(list[0].id);
for (key in list) {
    console.log(key.id);
}
Run Code Online (Sandbox Code Playgroud)

但我在控制台中得到以下输出:

event1
undefined
Run Code Online (Sandbox Code Playgroud)

这不是我的预期.为什么第二个控制台输出undefined但第一个控制台输出是event1

jfr*_*d00 715

摘要(2018年12月添加)

不要for/in用于迭代nodeList或HTMLCollection.避免它的原因如下所述.

所有最近的现代浏览器版本(Safari浏览器,火狐,Chrome,边缘)都支持keyDOM的迭代列出了这样list[key]list[key].id.

这是一个例子:

var list = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}
Run Code Online (Sandbox Code Playgroud)

要包含旧浏览器(包括IE之类的东西),这将适用于所有地方:

var list= document.getElementsByClassName("events");
for (var i = 0; i < list.length; i++) {
    console.log(list[i].id); //second console output
}
Run Code Online (Sandbox Code Playgroud)

解释为什么你不应该使用 for/in

for/in用于迭代对象的属性.这意味着它将返回对象的所有可迭代属性.虽然它似乎适用于数组(返回数组元素或伪数组元素),但它也可以返回对象的其他属性,这些属性不是您期望的类数组元素.并且,猜猜是什么,一个for/of或一个nodeList对象都可以具有将通过HTMLCollection迭代返回的其他属性.我只是在Chrome中尝试过这个并按照你迭代它的方式迭代它将检索列表中的项目(索引0,1,2等等),但也会检索for/infor/in属性.该HTMLCollection迭代根本不会对的HTMLCollection工作.


请参阅http://jsfiddle.net/jfriend00/FzZ2H/,了解为什么你不能迭代HTMLCollection nodeList.

在Firefox中,您的for/in迭代将返回这些项(对象的所有可迭代属性):

0
1
2
item
namedItem
@@iterator
length
Run Code Online (Sandbox Code Playgroud)

希望现在你可以看到你想要使用的原因,length以便你得到item,for/infor/in在你的迭代中.


以下是浏览器在2015-2018期间的演变过程的演变,为您提供了其他迭代方法.现在浏览器中现在都不需要这些,因为您可以使用上述选项.

2015年更新ES6

添加到ES6是for/in将类似数组的结构转换为实际数组.这允许人们直接枚举列表,如下所示:

"use strict";

Array.from(document.getElementsByClassName("events")).forEach(function(item) {
   console.log(item.id);
});
Run Code Online (Sandbox Code Playgroud)

工作演示(截至2016年4月的Firefox,Chrome和Edge):https://jsfiddle.net/jfriend00/8ar4xn2s/


2016年ES6更新

您现在可以将ES6 for/of construct与a for (var i = 0; i < list.length; i++)和a一起使用0,只需将其添加到您的代码中:

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Run Code Online (Sandbox Code Playgroud)

然后,你可以这样做:

var list = document.getElementsByClassName("events");
for (var item of list) {
    console.log(item.id);
}
Run Code Online (Sandbox Code Playgroud)

这适用于当前版本的Chrome,Firefox和Edge.这是因为它将Array迭代器附加到NodeList和HTMLCollection原型,因此当for/of迭代它们时,它使用Array迭代器来迭代它们.

工作演示:http://jsfiddle.net/jfriend00/joy06u4e/.


2016年12月ES6的第二次更新

截至2016年12月,1Chrome v54和Firefox v50已内置支持,因此下面的代码可单独使用.Edge尚未内置.

var list = document.getElementsByClassName("events");
for (let item of list) {
    console.log(item.id);
}
Run Code Online (Sandbox Code Playgroud)

工作演示(在Chrome和Firefox中):http://jsfiddle.net/jfriend00/3ddpz8sp/

2017年12月ES6的第三次更新

截至2017年12月,此功能在Edge 41.16299.15.0中适用2于in Array.from(),但不适用于此类NodeList,HTMLCollection因此您必须手动分配迭代器以在Edge中使用它Symbol.iterator.他们为什么要修复一种集合类型而不是另一种集合类型,这完全是个谜.但是,您现在至少可以在当前版本的Edge 中nodeList使用ES6 document.querySelectorAll()语法的结果.

我还更新了上面的jsFiddle,所以它分别测试HTMLCollection和检测document.getElementsByClassName()jsFiddle本身的输出.

ES8于2018年3月的第四次更新

每mesqueeeb,HTMLCollection支持已经内置Safari浏览器,所以你可以使用document.querySelectorAll()的两种for/ofHTMLCollection.

2018年4月ES6的第五次更新

显然,对于迭代的支持nodeListSymbol.iterator将现身秋2018年边缘18.

2018年11月ES6第六次更新

我可以通过Microsoft Edge v18(包含在2018年秋季Windows Update中)确认,现在您可以使用Edge中的for/of迭代HTMLCollection和NodeList.

因此,现在所有现代浏览器都包含for (let item of list)对HTMLCollection和NodeList对象的迭代的本机支持.

  • 添加了有关ES6中`of`迭代器的信息. (5认同)
  • ES6示例在Chrome 49上不起作用:Uncaught TypeError:list [Symbol.iterator]不是函数 (2认同)
  • 感谢您所做的出色更新。只是出于兴趣,你知道他们是否会在规范中添加 HTMLCollection 应该包含 Symbol.iterator 吗?我知道所有浏览器都在这样做,但据我所知,规范中没有提及它,并且 Typescript 定义是根据规范构建的,而不是实现。 (2认同)

eva*_*phy 76

你不能在s或s 上使用for/ .但是,你可以使用一些方法,只要它们并传入或作为.inNodeListHTMLCollectionArray.prototype.call()NodeListHTMLCollectionthis

因此,请考虑以下作为jfriend00 for循环的替代方法:

var list= document.getElementsByClassName("events");
[].forEach.call(list, function(el) {
    console.log(el.id);
});
Run Code Online (Sandbox Code Playgroud)

MDN上有一篇很好的文章介绍了这种技术.请注意他们对浏览器兼容性的警告:

[...]传递主机对象(如a NodeList)和 this本机方法(例如forEach)不能保证在所有浏览器中都能正常工作,并且已知在某些浏览器中失败.

因此,虽然这种方法很方便,但for循环可能是最兼容浏览器的解决方案.

更新(2014年8月30日):最终你将能够使用ES6 for/of!

var list = document.getElementsByClassName("events");
for (el of list)
  console.log(el.id);
Run Code Online (Sandbox Code Playgroud)

最新版本的Chrome和Firefox已经支持它.


mid*_*ido 50

在ES6中,你可以做类似的事情[...collection],或者Array.from(collection),

let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn) 
//or
Array.from(collection).forEach(someFn)
Run Code Online (Sandbox Code Playgroud)


小智 14

你可以添加这两行:

HTMLCollection.prototype.forEach = Array.prototype.forEach;
NodeList.prototype.forEach = Array.prototype.forEach;
Run Code Online (Sandbox Code Playgroud)

HTMLCollectiongetElementsByClassNamegetElementsByTagName返回

NodeListquerySelectorAll返回

像这样你可以做一个forEach:

var selections = document.getElementsByClassName('myClass');

/* alternative :
var selections = document.querySelectorAll('.myClass');
*/

selections.forEach(function(element, i){
//do your stuffs
});
Run Code Online (Sandbox Code Playgroud)

  • 请注意,[`NodeList` 已经有 `forEach()`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach)。 (6认同)
  • 问题是,该解决方案在IE11上不起作用!好的解决方案。 (2认同)

ruf*_*fin 8

for如果您使用的是 IE9 或更高版本,则没有理由使用 es6 功能来逃避循环。

在 ES5 中,有两个不错的选择。首先,你可以像埃文提到ArrayforEach那样“借用” 。

但更好的是...

使用Object.keys(),它确实具有forEach和 过滤器以自动“拥有属性”。

也就是说,Object.keys本质上相当于for... in用 a做a HasOwnProperty,但要平滑得多。

var eventNodes = document.getElementsByClassName("events");
Object.keys(eventNodes).forEach(function (key) {
    console.log(eventNodes[key].id);
});
Run Code Online (Sandbox Code Playgroud)


小智 7

我在IE 11Firefox 49中使用forEach时遇到了问题

我找到了这样的解决方法

Array.prototype.slice.call(document.getElementsByClassName("events")).forEach(function (key) {
        console.log(key.id);
    }
Run Code Online (Sandbox Code Playgroud)


Mar*_*rcG 5

自2016年3月起,在Chrome 49.0中,该版本for...of适用于HTMLCollection

this.headers = this.getElementsByTagName("header");

for (var header of this.headers) {
    console.log(header); 
}
Run Code Online (Sandbox Code Playgroud)

请参阅此处的文档

但是,只有使用之前应用以下变通办法时它才有效for...of

HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Run Code Online (Sandbox Code Playgroud)

同样是必要的使用for...ofNodeList

NamedNodeMap.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Run Code Online (Sandbox Code Playgroud)

我相信/希望for...of上述解决方法很快就会起作用。未解决的问题在这里:

https://bugs.chromium.org/p/chromium/issues/detail?id=401699

更新:请参见下面的Expenzor注释:截至2016年4月,此问题已得到修复。您无需添加HTMLCollection.prototype [Symbol.iterator] = Array.prototype [Symbol.iterator]; 遍历带有... of的HTMLCollection

  • 自2016年4月起,此问题已得到修复。您无需添加HTMLCollection.prototype [Symbol.iterator] = Array.prototype [Symbol.iterator];即可使用for.of遍历HTMLCollection。 `。 (4认同)

hol*_*erd 5

替代方法Array.from是使用Array.prototype.forEach.call

每个: Array.prototype.forEach.call(htmlCollection, i => { console.log(i) });

地图: Array.prototype.map.call(htmlCollection, i => { console.log(i) });

等等...