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
?
Summary (added in Dec 2018)
Do not ever use
for/in
to iterate a nodeList or an HTMLCollection. The reasons to avoid it are described below.All recent versions of modern browsers (Safari, Firefox, Chrome, Edge) all support
for/of
iteration on DOM lists suchnodeList
orHTMLCollection
.Here's an example:
To include older browsers (including things like IE), this will work everywhere:
Explanation For Why You Should Not Use
for/in
for/in
is meant for iterating the properties of an object. That means it will return all iterable properties of an object. While it may appear to work for an array (returning array elements or pseudo-array elements), it can also return other properties of the object that are not what you are expecting from the array-like elements. And, guess what, anHTMLCollection
ornodeList
object can both have other properties that will will be returned with afor/in
iteration. 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 thelength
anditem
properties. Thefor/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):Hopefully, now you can see why you want to use
for (var i = 0; i < list.length; i++)
instead so you just get0
,1
and2
in your iteration.Following below is an evolution of how browsers have evolved through the time period 2015-2018 giving you additional ways to iterate. None of these are now needed in modern browsers since you can use the options described above.
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: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 anHTMLCollection
by just adding this to your code:Then, you can do:
This works in the current version of Chrome, Firefox and Edge. This works because it attaches the Array iterator to both the NodeList and HTMLCollection prototypes so that when for/of iterates them, it uses the Array iterator to iterate them.
Working demo: http://jsfiddle.net/jfriend00/joy06u4e/.
Second Update for ES6 in Dec 2016
As of Dec 2016,
Symbol.iterator
support has been built-in to Chrome v54 and Firefox v50 so the code below works by itself. It is not yet built-in for Edge.Working demo (in Chrome and Firefox): http://jsfiddle.net/jfriend00/3ddpz8sp/
Third Update for ES6 in Dec 2017
As of Dec. 2017, this capability works in Edge 41.16299.15.0 for a
nodeList
as indocument.querySelectorAll()
, but not anHTMLCollection
as indocument.getElementsByClassName()
so you have to manually assign the iterator to use it in Edge for anHTMLCollection
. It is a total mystery why they'd fix one collection type, but not the other. But, you can at least use the result ofdocument.querySelectorAll()
with ES6for/of
syntax in current versions of Edge now.I've also updated the above jsFiddle so it tests both
HTMLCollection
andnodeList
separately and captures the output in the jsFiddle itself.Fourth Update for ES6 in Mar 2018
Per mesqueeeb,
Symbol.iterator
support has been built-in to Safari too, so you can usefor (let item of list)
for eitherdocument.getElementsByClassName()
ordocument.querySelectorAll()
.Fifth Update for ES6 in Apr 2018
Apparently support for iterating an
HTMLCollection
withfor/of
will be coming to Edge 18 in Fall 2018.Sixth Update for ES6 in Nov 2018
I can confirm that with Microsoft Edge v18 (that is included in the Fall 2018 Windows Update), you can now iterate both an HTMLCollection and a NodeList with for/of in Edge.
So, now all modern browsers contain native support for
for/of
iteration of both the HTMLCollection and NodeList objects.You want to change it to
you can add this two lines:
HTMLCollection is return by getElementsByClassName and getElementsByTagName
NodeList is return by querySelectorAll
Like this you can do a forEach:
Alternative to
Array.from
is to useArray.prototype.forEach.call
forEach:
Array.prototype.forEach.call(htmlCollection, i => { console.log(i) });
map:
Array.prototype.map.call(htmlCollection, i => { console.log(i) });
ect...
On Edge
You can't use
for
/in
onNodeList
s orHTMLCollection
s. However, you can use someArray.prototype
methods, as long as you.call()
them and pass in theNodeList
orHTMLCollection
asthis
.So consider the following as an alternative to jfriend00's
for
loop:There's a good article on MDN that covers this technique. Note their warning about browser compatibility though:
So while this approach is convenient, a
for
loop may be the most browser-compatible solution.Update (Aug 30, 2014): Eventually you'll be able to use ES6
for
/of
!It's already supported in recent versions of Chrome and Firefox.