我试图在HTMLCollectionOf
中设置所有元素的获取ID。我写了以下代码:
var list = document.getElementsByClassName("events");
console.log(list[0].id);
for (key in list) {
console.log(key.id);
}
但是我在控制台中得到了以下输出:
event1
undefined
这不是我的预期。为什么第二个控制台输出undefined
但第一个控制台输出是event1
?
答案 0 :(得分:608)
摘要(2018年12月添加)
不要使用for/in
来迭代nodeList或HTMLCollection。避免它的原因如下所述。
所有最新版本的现代浏览器(Safari,Firefox,Chrome,Edge)都支持for/of
或nodeList
等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
用于迭代对象的属性。这意味着它将返回对象的所有可迭代属性。虽然它似乎适用于数组(返回数组元素或伪数组元素),但它也可以返回对象的其他属性,这些属性不是您期望的类数组元素。而且,猜测一下,HTMLCollection
或nodeList
对象都可以具有其他属性,这些属性将通过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++)
,0
和1
。
以下是浏览器在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/of
和HTMLCollection
并捕获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)
您无法在for
或in
上使用NodeList
/ HTMLCollection
。但是,您可以使用一些Array.prototype
方法,只要您.call()
方法,并将NodeList
或HTMLCollection
传递为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);
}
但只有在使用for...of
之前应用以下解决方法时才有效:
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
将for...of
与NodeList
:
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
答案 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>