HTMLCollection 元素的 For 循环

IT技术 javascript dom
2021-01-31 17:13:11

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

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

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

event1
undefined

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

6个回答

在回答原始问题时,您使用for/in不正确。在您的代码中,key是索引。因此,要从伪数组中获取值,您必须执行list[key]并获取 id,您必须执行list[key].id. 但是,您首先不应该这样做for/in

总结(2018 年 12 月新增)

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

所有最新版本的现代浏览器(Safari、Firefox、Chrome、Edge)都支持for/ofDOM 列表的迭代,例如nodeListHTMLCollection.

下面是一个例子:

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


请参阅http://jsfiddle.net/jfriend00/FzZ2H/了解为什么您不能使用for/in.

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

0
1
2
item
namedItem
@@iterator
length

希望现在您可以明白为什么要使用它,for (var i = 0; i < list.length; i++)以便在您的迭代中获得0,12


浏览器对 NodeList 和 HTMLCollection 迭代支持的演变

以下是浏览器在 2015 年至 2018 年期间如何演变的演变,为您提供了其他迭代方式。现代浏览器现在不需要这些,因为您可以使用上述选项。

2015 年 ES6 更新

添加到 ES6 的是Array.from()将类似数组的结构转换为实际数组。这允许我们像这样直接枚举列表:

"use strict";

Array.from(document.getElementsByClassName("events")).forEach(function(item) {
   console.log(item.id);
});

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


2016 年 ES6 更新

您现在可以通过将以下内容添加到您的代码中来使用带有 aNodeList和 an的 ES6 for/of 构造HTMLCollection

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

然后,你可以这样做:

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

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

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


2016 年 12 月 ES6 的第二次更新

截至 2016 年 12 月,Symbol.iteratorChrome v54 和 Firefox v50 已内置支持,因此以下代码可以自行运行。它尚未内置于 Edge 中。

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

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

2017 年 12 月 ES6 的第三次更新

截至 2017 年 12 月,此功能在 Edge 41.16299.15.0 中适用于 a nodeListas in document.querySelectorAll(),但不适用于HTMLCollectionas in,document.getElementsByClassName()因此您必须手动分配迭代器以在 Edge 中为HTMLCollection. 为什么他们会修复一种集合类型,而不是另一种,这完全是个谜。但是,您现在至少可以在Edge 的当前版本中document.querySelectorAll()使用 ES6for/of语法的结果

我还更新了上述的jsfiddle所以它同时测试HTMLCollectionnodeList分开,并捕获的的jsfiddle本身的输出。

2018 年 3 月 ES6 的第四次更新

根据 mesqueeeb,Symbol.iteratorSafari 也内置了支持,因此您可以将其for (let item of list)用于document.getElementsByClassName()document.querySelectorAll().

2018 年 4 月 ES6 第五次更新

显然,Edge 18 将在 2018 年秋季支持迭代HTMLCollectionwith for/of

2018 年 11 月 ES6 的第六次更新

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

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

感谢您所做的重大更新。只是出于兴趣,你知道他们是否会在规范中添加 HTMLCollection 应该有 Symbol.iterator 吗?我知道所有浏览器都在这样做,但据我所知,规范没有提及它,并且 Typescript 定义是根据规范构建的,而不是实现。
2021-03-14 17:13:11
@WORMSS - 我不知道。但是,如果我猜想,LivingStandard 文档中的这个注释HTMLCollection 是我们无法摆脱网络的历史产物。虽然开发人员当然欢迎继续使用它,但新的 API 标准设计者不应该使用它(在 IDL 中使用 sequence<T> 代替)听起来不太可能得到增强,因为他们不想鼓励你不再使用实时集合 API - 可能是因为如果您在修改 DOM 的同时迭代实时集合,很容易创建错误。
2021-03-29 17:13:11

您不能s 或s使用for/ 但是,您可以使用某些方法,只要您将它们传入作为.inNodeListHTMLCollectionArray.prototype.call()NodeListHTMLCollectionthis

因此,请考虑将以下内容作为jfriend00for循环的替代方案

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

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

[...] 将宿主对象(如NodeListthis传递给本机方法(如forEach)并不能保证在所有浏览器中都有效,并且已知在某些浏览器中会失败。

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

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

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

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

我正在寻找 ES2015 解决方案,所以感谢您确认它for ... of有效。
2021-03-13 17:13:11
非常好!我使用这种技术从<select multiple>. 例子:[].map.call(multiSelect.selectedOptions, function(option) { return option.value; })
2021-03-20 17:13:11

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

let someCollection = document.querySelectorAll(someSelector)
[...someCollection].forEach(someFn) 
//or
Array.from(collection).forEach(someFn)

例如:-

    navDoms = document.getElementsByClassName('nav-container');
    Array.from(navDoms).forEach(function(navDom){
     //implement function operations
    });
@DanielM 猜猜我所做的是,浅克隆一个类似数组的结构
2021-03-25 17:13:11
我明白了,谢谢——现在我找到了我正在寻找的文档:developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
2021-04-01 17:13:11
我总是使用它,比 Array.from 更容易入眼,我只是想知道它是否有相当大的性能或内存缺陷。例如,如果我需要迭代表格行的单元格,我使用 a[...row.cells].forEach而不是执行 arow.querySelectorAll('td')
2021-04-05 17:13:11

您可以添加这两行:

HTMLCollection.prototype.forEach = Array.prototype.forEach;
NodeList.prototype.forEach = Array.prototype.forEach;

HTMLCollectiongetElementsByClassNamegetElementsByTagName返回

NodeListquerySelectorAll返回

像这样你可以做一个 forEach:

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

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

selections.forEach(function(element, i){
//do your stuffs
});
2021-03-10 17:13:11
这个答案似乎很有效。有什么收获?
2021-03-16 17:13:11
我很好奇浏览器对此的支持。看起来很棒。
2021-03-28 17:13:11
问题是这个解决方案在 IE11 上不起作用!不过很好的解决方案。
2021-04-05 17:13:11

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

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

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

等等...