对于HTMLCollection元素的循环

时间:2014-03-31 05:40:04

标签: javascript dom

我试图在HTMLCollectionOf中设置所有元素的获取ID。我写了以下代码:

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

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

event1
undefined

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

13 个答案:

答案 0 :(得分:608)

摘要(2018年12月添加)

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

所有最新版本的现代浏览器(Safari,Firefox,Chrome,Edge)都支持for/ofnodeList等DOM列表上的HTMLCollection次迭代。

以下是一个例子:

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

要包含较旧的浏览器(包括IE之类的东西),这可以在任何地方使用:

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

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

for/in用于迭代对象的属性。这意味着它将返回对象的所有可迭代属性。虽然它似乎适用于数组(返回数组元素或伪数组元素),但它也可以返回对象的其他属性,这些属性不是您期望的类数组元素。而且,猜测一下,HTMLCollectionnodeList对象都可以具有其他属性,这些属性将通过for/in次迭代返回。我只是在Chrome中尝试过这个并按照你的迭代方式迭代它将检索列表中的项目(索引0,1,2等等),但也会检索length和{{1属性。 item迭代只是不能用于HTMLCollection。


请参阅http://jsfiddle.net/jfriend00/FzZ2H/了解为何您可以使用for/in迭代HTMLCollection。

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

for/in

希望您现在可以看到为什么要使用0 1 2 item namedItem @@iterator length ,这样您就可以在迭代中获得for (var i = 0; i < list.length; i++)01


以下是浏览器在2015-2018这段时间内如何演变的演变,为您提供了额外的迭代方法。现在浏览器现在都不需要这些,因为您可以使用上述选项。

2015年ES6更新

添加到ES6的是2,它将类似数组的结构转换为实际的数组。这允许人们直接枚举列表:

Array.from()

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


2016年ES6更新

现在,您只需将{6}添加到您的代码中,就可以使用带有"use strict"; Array.from(document.getElementsByClassName("events")).forEach(function(item) { console.log(item.id); }); NodeList的ES6 for / of构造:

HTMLCollection

然后,你可以这样做:

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

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

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


2016年12月ES6的第二次更新

截至2016年12月,Chrome v54和Firefox v50已内置var list = document.getElementsByClassName("events"); for (var item of list) { console.log(item.id); } 支持,因此以下代码可自行运行。它尚未内置于Edge。

Symbol.iterator

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

2017年12月ES6第三次更新

截至2017年12月,此功能在Edge 41.16299.15.0中适用于var list = document.getElementsByClassName("events"); for (let item of list) { console.log(item.id); } nodeList,但不适用于document.querySelectorAll() HTMLCollection,因此您有手动分配迭代器以在Edge中使用它document.getElementsByClassName()。他们为什么要修复一种集合类型而不是另一种集合类型,这完全是个谜。但是,您现在至少可以在当前版本的Edge中使用HTMLCollection和ES6 document.querySelectorAll()语法的结果。

我还更新了上面的jsFiddle,因此它分别测试for/ofHTMLCollection并捕获jsFiddle本身的输出。

2018年3月ES6第四次更新

每个mesqueeeb,nodeList支持也已内置到Safari中,因此您可以Symbol.iterator使用for (let item of list)document.getElementsByClassName()

2018年4月ES6第五次更新

显然,支持使用document.querySelectorAll()迭代HTMLCollection将于2018年秋季到达Edge 18。

2018年11月ES6第六次更新

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

因此,现在所有现代浏览器都包含对HTMLCollection和NodeList对象的for/of迭代的本机支持。

答案 1 :(得分:70)

您无法在forin上使用NodeList / HTMLCollection。但是,您可以使用一些Array.prototype方法,只要您.call()方法,并将NodeListHTMLCollection传递为this

因此,请考虑以下内容作为jfriend00's for loop的替代方法:

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

有一个很好的article on MDN涵盖了这项技术。请注意他们关于浏览器兼容性的警告:

  

[...]将主机对象(如NodeList)传递给   this到本机方法(例如forEach)并不能保证能够正常工作   所有浏览器都知道在某些浏览器中失败。

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

更新(2014年8月30日):最终您可以使用ES6 for/of

var list = document.getElementsByClassName("events");
for (el of list)
  console.log(el.id);

最近版本的Chrome和Firefox已经支持它。

答案 2 :(得分:42)

在ES6中,您可以执行[...collection]Array.from(collection)

之类的操作
let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn) 
//or
Array.from(collection).forEach(someFn)

答案 3 :(得分:11)

你可以添加这两行:

HTMLCollection.prototype.forEach = Array.prototype.forEach;
NodeList.prototype.forEach = Array.prototype.forEach;
通过 getElementsByClassName getElementsByTagName

返回

HTMLCollection

NodeList querySelectorAll

返回

像这样你可以做一个forEach:

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

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

selections.forEach(function(element, i){
//do your stuffs
});

答案 4 :(得分:6)

我在 IE 11 Firefox 49

中使用forEach时出现问题

我找到了像这样的解决方法

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

答案 5 :(得分:4)

截至2016年3月,在Chrome 49.0中,for...of适用于HTMLCollection

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

for (var header of this.headers) {
    console.log(header); 
}

请参阅here the documentation

但只有在使用for...of 之前应用以下解决方法时才有效:

HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

for...ofNodeList

一起使用也是必要的
NamedNodeMap.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];

我相信/希望for...of很快就可以在没有上述解决方法的情况下工作。公开问题在这里:

  

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

更新:请参阅下面的Expenzor评论:截至2016年4月已修复此问题。您无需添加HTMLCollection.prototype [Symbol.iterator] = Array.prototype [Symbol .iterator];使用for

迭代HTMLCollection

答案 6 :(得分:3)

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

<强>的forEach: Array.prototype.forEach.call(htmlCollection, i => { console.log(i) });

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

等...

答案 7 :(得分:2)

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

在ES5中,有两个不错的选择。首先,您可以“借用”Array的{​​{1}}作为evan mentions

但更好......

使用forEach 拥有Object.keys()

Object.keys基本上相当于使用forEach执行for... in,但是更加平滑。

HasOwnProperty

答案 8 :(得分:1)

边缘

if(!NodeList.prototype.forEach) {
  NodeList.prototype.forEach = function(fn, scope) {
    for(var i = 0, len = this.length; i < len; ++i) {
      fn.call(scope, this[i], i, this);
    }
  }
}

答案 9 :(得分:0)

您想将其更改为

var list= document.getElementsByClassName("events");
console.log(list[0].id); //first console output
for (key in list){
    console.log(list[key].id); //second console output
}

答案 10 :(得分:0)

如果使用ES的旧版本(例如ES5),则可以使用as any

for (let element of elementsToIterate as any) {
      console.log(element);
}

答案 11 :(得分:0)

我经常使用的简便解决方法

let list = document.getElementsByClassName("events");
let listArr = Array.from(list)

此后,您可以在选择项上运行任何所需的Array方法

listArr.map(item => console.log(item.id))
listArr.forEach(item => console.log(item.id))
listArr.reverse()

答案 12 :(得分:0)

你也可以这样做:

 let elements = document.getElementsByClassName("classname");
 for(let index in  elements) {
   if(index <= elements.length) {
        console.log(elements[index]);
  }
}

let elements = document.getElementsByClassName("classname");
for (let index in elements) {
  if (index <= elements.length) {
    console.log(elements[index]);
  }
}
<div class="classname"> element 1 </div>
<div class="classname"> element 2 </div>
<div class="classname"> element 3 </div>

 let elements = document.getElementsByClassName("classname");
 for(let ele of  elements) {
        console.log(ele);
}

let elements = document.getElementsByClassName("classname");
for (let ele of elements) {
  console.log(ele);
}
<div class="classname"> element 1 </div>
<div class="classname"> element 2 </div>
<div class="classname"> element 3 </div>
<div class="classname"> element 4 </div>