针对原问题,您在错误地使用 for/in
。 在您的代码中,key
是索引。 因此,要从伪数组中获取值,您需要执行 list[key]
,要获取id,则需要执行list[key].id
。 但是,首先就不应该使用for/in
来做这件事。
摘要(2018年12月添加)
永远不要使用for/in
来迭代 nodeList 或 HTMLCollection。 避免使用它的理由如下所述。
所有现代浏览器的最新版本(Safari、Firefox、Chrome、Edge)都支持对 DOM 列表(例如 nodeList
或 HTMLCollection
)进行for/of
迭代。
以下是一个例子:
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);
}
为什么不应该使用 for/in
for/in
用于迭代对象的属性。这意味着它将返回对象所有可迭代的属性。虽然它看起来可以用于数组(返回数组元素或伪数组元素),但它也可能返回对象的其他非预期的类似数组元素的属性。并且,注意了,一个 HTMLCollection
或 nodeList
对象都可以有其他属性,这些属性会在 for/in
迭代中被返回。我刚试过了,在 Chrome 中以你正在迭代的方式进行迭代,将检索列表中的项目(索引 0、1、2 等...),但也将检索到 length
和 item
属性。对于 HTMLCollection,for/in
迭代根本不起作用。
请参见 http://jsfiddle.net/jfriend00/FzZ2H/ 了解为什么不能使用 for/in
迭代 HTMLCollection。
在 Firefox 中,你的 for/in
迭代将返回这些项目(对象的所有可迭代属性):
0
1
2
item
namedItem
@@iterator
length
希望现在你能够理解为什么要使用 for (var i = 0; i < list.length; i++)
,这样你在迭代中就只会得到 0
、1
和 2
。
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/
ES6的更新(2016年)
现在您可以通过将以下内容添加到代码中,使用ES6 for/of结构来操作NodeList
和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月,Chrome v54和Firefox v50已经内置了Symbol.iterator
支持,因此下面的代码本身可以工作。但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中,对于document.querySelectorAll()
返回的nodeList
可以正常使用ES6语法的for/of
迭代器,但是对于document.getElementsByClassName()
返回的HTMLCollection
,需要手动分配迭代器才能在Edge中使用它。不明白为什么他们会修复一种集合类型而不修复另一种集合类型。但是,您现在可以在Edge的当前版本中使用ES6 for/of
语法迭代document.querySelectorAll()
的结果。
我还更新了上面的jsFiddle,以便单独测试HTMLCollection
和nodeList
,并将输出捕获在jsFiddle本身中。
2018年3月的第四次更新:ES6
根据mesqueeeb的说法,Safari也内置了对Symbol.iterator
的支持,因此您可以使用for(let item of list)
迭代器来迭代document.getElementsByClassName()
或document.querySelectorAll()
。
2018年4月的第五次更新:ES6
显然,Edge 18将于2018年秋季支持使用for/of
迭代器迭代HTMLCollection
。
2018年11月的第六次更新:ES6
我可以确认,在包含在2018年秋季Windows更新中的Microsoft Edge v18中,现在可以使用for/of
迭代器迭代HTMLCollection
和nodeList
。
因此,现代浏览器都原生支持对HTMLCollection和NodeList对象使用for/of
迭代器。
getElementsByClassName
返回的是带有该 CSS 类的节点的实时集合。因此,如果您在循环中操作正在迭代的节点的class
属性,则该集合可能会发生变化。在这种情况下,除了Array.from(...)。forEach
之外,大多数构造方式都将失效。Array.from
将进行对象克隆并创建一个单独的对象,然后进行迭代。 - RBT