NodeList object is finally an Iterable

Can’t be more excited about the new feature currently available in Chrome Beta (currently 51), which is nothing else but the built-in support for NodeList iteration 🎉

It might be so obvious for people coming from outside the web world that whenever you select a list of UI elements, let’s say this one:

and you want to do some action on each of them, you might expect something like this:

to output this in the console:

'www.medium.com'
'www.google.com'
'www.github.com'

but instead what you get is…😤😟

Uncaught TypeError: elements.forEach is not a function

Explanation

This has been a headache for many web developers a lot of times, since when you get them returned from, for instance, document.querySelectorAll method, what you get back is a collection…

NodeList objects are collections of nodes

The explanation is actually really simple, and it relays in the Prototype inheritance that Javascript uses, the problem here is that NodeList object is not an Array and it doesn’t contain the forEach method in his prototype, actually it doesn’t contain any of the array methods. Similar thing happen with the arguments object, which again, is not an Array and doesn’t contain such methods… anyways there’s no excuse for you to still use the arguments object since the rest parameter syntax allows you to represent an indefinite number of arguments as an array, which is actually what you need 😼.

Array method lookup

myArray → Array.prototype → Object.prototype → null

NodeList method lookup

myNodeList → NodeList.prototype → Object.prototype → null

Symbol.iterator

The good news is that Chrome has introduced the following changes to NodeList, check it out in the chrome-status page.

Adds `Symbol.iterator` to any DOM interface containing an indexed property getter, and a “length” property.

This means that thanks to the Symbol.iterator, now any object that implements that protocol can be iterated like it was an array. Just open the browser console and type:

Array.prototype[Symbol.iterator]
String.prototype[Symbol.iterator]
NodeList.prototype[Symbol.iterator]

you will quickly realize that all of them are iterable objects now.

It also introduces the ability to use the spread operator with NodeList objects:

and the for of statement which invokes a custom iteration hook with statements to be executed for the value of each distinct property:

Workarounds, workarounds, workarounds…

Now let’s go back to the reality for a second..

Array Slice

Probably the most famous one is just “cast” the NodeList into an Array, I say “cast” because what is actually happening is that you are just invoking the slice method with a different context, this will basically just generate a new array instance.

jQuery each

The javascript standard library, AKA jQuery, already solved this problem years ago in a nice way, all you need to do is to use the each method. Note that index is the first argument instead of the item.

Array.from

This is also a nice one, Array.from method creates a new Array instance from an array-like or iterable object. The bad news is again current browser support…

Extending prototype

This has being considered a bad practice for long time, personally I also don’t like adding new methods to the native objects. But! In this case this is my favourite solution since makes a lot of sense to make NodeList iterables as default and is what you will expect whenever you retrieve items from the DOM.

Conclusion

This might seem like a small thing for most devs but I think it’s actually important that browsers follow this direction, making things behave like developers expect and fixing unexpected behaviours even if they are simple things like this.


You can follow me on Twitter or Github @zzarcon