The title of this post is what I originally googled. Here's what got me there:
I was working on displaying local times for the event listings on dev.to/events (haven't made a PR yet). To do this, I added a class to all elements with a timestamp, like this:
<span class="utc-time"><%= event.starts_at %></span>
I wanted to grab all the timestamps on the page, loop through them, and update their innerHTML to reflect the local time. I usually use for statements when I need to loop stuff, but I decided to try the .forEach function.
var timestamps = document.getElementsByClassName("utc-time");
timestamps.forEach(function(timestamp) {
localTime = updateLocalTime(timestamps[i].innerHTML);
timestamps[i].innerHTML = localTime;
});
I got this error:
Eventually, I realized that timestamps was not an array, it was a NodeList and at the top of mdn documentation, it clearly states:
Although NodeList is not an Array, it is possible to iterate on it using
forEach(). It can also be converted to an Array usingArray.from().However some older browsers have not yet implemented
NodeList.forEach()norArray.from(). But those limitations can be circumvented by usingArray.prototype.forEach()(more in this document).
I probably should have googled "How to loop through a NodeList" for specificity. Anyway, so then I wrote this:
Array.prototype.forEach.call(timestamps, function (timestamp) {
localTime = updateLocalTime(timestamp.innerHTML);
timestamp.innerHTML = localTime;
});
And it worked! But when I showed it to @maestromac, he told me that a simple for statement would have worked. And would probably be a bit safer. So I went back to what I was most familiar with:
for (var i = 0; i < timestamps.length; i++) {
localTime = updateLocalTime(timestamps[i].innerHTML);
timestamps[i].innerHTML = localTime
}
At least I learned something about NodeLists today ¯_(ツ)_/¯

Top comments (24)
Ah this is a classic rites of passage in DOM unintutiveness.
If only
NodeListinherited fromArray...There are some methods that don't make much sense in relation to
NodeLists. Mostly mutating methods likepush,sortorsplice.The others are fine, though. I'd love to have
maporfilter😞use the "…" spread operator, which works for both
getElementsByClassName()andquerySelectorAll()so you can port easily later.I think you can also do:
Haven't tested it though 😊
Well, in the mdn documentation it had mentioned this:
So I assumed that if forEach wasn't supported, Array.from wouldn't be either?
I just tested it and it works on Chrome for me.
It might not be compatible with many browsers though, as you mentioned.
You don't have to go far - if you have Windows and IE11 installed, that doesn't support either 😉
The MDN documentation also has an Array.from polyfill:
developer.mozilla.org/en-US/docs/W...
A small correction: you used
document.getElementsByClassNamewhich does not return aNodeListbut aHTMLCollection. Now, the former does haveforEachdefined - but it's pretty much the only array method that has been added to its prototype so far.But it's only a relatively recent addition, so older browsers don't support it - fortunately, the
Array#forEachtrick works pretty well, down to sufficiently old Internet Explorer versions (probably6? 5.5?The heck am I saying, that could work forslice, butforEachwas added only in IE9...).A
HTMLCollectionis a totally different beast... and something that should be avoided in general. It's a live collection that gets updated when the DOM changes. Quite heavy when it comes to memory consumption and CPU usage.Conclusion: use
document.querySelectorAllinstead (which returns aNodeList).I don't agree with that ¯\_(ツ)_/¯
It's true that every browser supports
for(duh!), but experience proved that something that iterates over a collection for us is simpler as it doesn't force us to take care of a variable for counting, while the (relatively) complex - although well-known - syntax offoris prone to mistakes.Mostly caused by distraction and/or boredom 😁
A best practice is to convert a
NodeListto a normalArray, so you can use the built-in forEach/map functions.One ES5 way is to create a helper function, for example on the NodeList prototype:
Another popular way is to create a jQuery-ish helper function that wraps the conversion to array. This is also used in Lea Verou's bliss.js library:
The modern ES7+ way is to use the
...operator:Wait, is this true?
I seldom use
selectElementsByClassName, but according to mdn it returns aHTMLCollection. And the mdn doc clearly says it hasn't gotforEach.But for
NodeList, you should be able to doforEach. If you dodocument.querySelectorAll('.className'), you will get anNodeListand you should be able to doforEach. See here.Howeverrrrr, since most older browsers won't have
NodeList.prototype.forEachdefined, it is probably safer to do what you suggestedArray.prototype.forEach.call(elements, ...)or just[].forEach.call(elements, ...). A more "es6" way would probably beArray.from(elements).forEach(...).Orrrrr, you could do it with for-loops as you suggested. "es6" introduced this amazing
for-ofloop, it could loop through most list-like things. So the following would work as well.Of course, to safely use ES6 features you probably want your polyfills + babel set up to support old browsers.
I just want to make a few points.
forloop in years.querySelectorAll.getElementsByClassNamereturns anHTMLCollection, which is why you were getting the error.NodeLists have aforEachmethod andHTMLCollections do not.My initial instinct on circumventing this is the following:
HTMLCollections are array-like. They areiterableand have alengthproperty. Theslicefunction can convert array-like objects intoArrays.thisis bound to our array-like object.sliceiterates overthisusing thelengthproperty since no other arguments were given. All the elements are returned in a newArraythen we can callforEachmethod on ourArrayBut looking at your initial answer:
Is a good solution too if not better. timestamps is bound to
thisthenforEachmethod iterates over the array-like object by using thelengthproperty. My solution would have looped through the array-like object once then the newly created array versus this solution which is once.Although it's a very good point that you can use a for-loop in this case, your own solution can be simplified to [].forEach.call, which would also work in case you'd need other array operations such as map, filter or reduce.
Hi @jess and anyone who may enter for the foreach on dom elements, or NodeList Iteration class.
I recommend a gist that I found using a lot in my current web Proyect. Iterate over a NodeList