Dmitrii Bundin Dmitrii Bundin - 1 month ago 67
Javascript Question

foreach loop for HTMLCollection elements

I'm trying to set get id of all elements in an HTMLCollection. I wrote the following code:

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


But I got the follwoing output in console:

event1
undefined


which is not what I expected. Why is the
second console output
undefined
but the
first console output
is
event1
?

Answer

Eeek. You can't iterate over a nodeList or HTMLCollection with for/in and when you do iterate, you need to actually retrieve the value from the list, using the index in your iteration.

You should iterate it like this:

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

for/in is meant for iterating the properties of an object. It is not meant for iterating an array or an array-like object which an HTMLCollection is. I just tried this in Chrome and iterating it the way you were iterating it will retrieve the items in the list (indexes 0, 1, 2, etc...), but also will retrieve the length and item properties. The for/in iteration simply won't work for an HTMLCollection.


See http://jsfiddle.net/jfriend00/FzZ2H/ for why you can't iterate an HTMLCollection with for/in.

In Firefox, your for/in iteration would return these items (all the iterable properties of the object):

0
1
2
item
namedItem
@@iterator
length

Hopefully, now you can see why you want to use for (var i = 0; i < list.length; i++) instead so you just get 0, 1 and 2 in your iteration.


Update for ES6 in 2015

Added to ES6 is Array.from() that will convert an array-like structure to an actual array. That allows one to enumerate a list directly like this:

"use strict";

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

Working demo (in Firefox, Chrome and Edge as of April 2016): https://jsfiddle.net/jfriend00/8ar4xn2s/


Update for ES6 in 2016

You can now use the ES6 for/of construct with a NodeList and an HTMLCollection by just adding this to your code:

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

Then, you can do:

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

This works in the current version of Chrome, Firefox and Edge.

Working demo: http://jsfiddle.net/jfriend00/joy06u4e/.