Table of contents
If you’re a jQuery user, you’re probably used to doing this:
A lot. If you’ve never heard of NodeLists, or haven’t learned about them but are using jQuery on a daily basis, then you need to learn what you’re really dealing with underneath for many reasons.
What is a NodeList?
What’s iteration? This means looping over your collection of elements, which you can then do something with each individual element’s value or index. Looping over a NodeList is the exact same as an Array when using a regular for loop:
So that should work great when it comes to a NodeList, they’re pretty similar. Take the following example:
“What happened? Why is my code broken? Waaaahhh?” the recent jQuery convert says.
You can’t manipulate NodeLists the same way you can an Array.
Arrays come with a bunch of awesomely inherited prototypal methods, things like splice(), push(), join(), indexOf() and many more. When our collections are NodeLists, we miss out on all this goodness. Check out MDN for a comprehensive list of methods.
Which means we cannot remove an item from a NodeList like you’d simply expect:
What isn’t a NodeList?
A NodeList isn’t an Array (applause).
NodeLists are actually really interesting collections of Nodes, and are separate to their close cousin Arrays for a few good reasons, they can contain what we call live Nodes.
If I had the following HTML (3 divs):
And ran a document.getElementsByTagName() method, this will return a live collection:
If I were to do the following and insert a new div element into the page:
As if by magic, our nodes collection has automatically updated. I’m sure you can see the use of that, so you might not always want to convert a NodeList to Array.
Converting NodeLists to Arrays
The plan of attack here really does vary depending entirely on your browser support and use case for that particular NodeList/Array.
If you need IE8 and below support, the easiest way of converting a NodeList to an Array is pushing each element from a NodeList into a new Array:
And you’re all done. It’s a nice and simple process. I absolutely love this method, as it still keeps your original NodeList reference if you need it, for instance keeping a tab on your live NodeList collection. Please note though, using document.querySelectorAll() returns a static NodeList, not live, therefore it won’t automatically update. However, document.getElementsByTagName() will keep a live record, but getting elements by their tag name is slowly dying. I personally would’ve liked to have seen live Nodes in querySelectorAll.
Moving swiftly forward, you’d be interested (maybe) to know that some performance/speed tests were done and the quickest method (apparently) of converting a NodeList to Array is:
Check out some of the other NodeList to Array perf tests.
If you’re fortunate enough to not care about IE8 and below, then you can use a neat trick to convert your NodeList instantly using Array.prototype.slice.call():
Accessing the Prototype Object here, we grab the slice() method, and pass our NodeList into it. This API then internally converts it to an Array using the slice() method (which returns a new Array). It cleverly pushes each Node into a new Array, yay!
Now we can access all the Array methods and use the forEach() method as intended:
And no more TypeErrors, everything is good.
We can shorten this entire declaration however using an empty Array, which has access to the Prototype methods:
… But I wouldn’t advise it, this can cause issues with other libraries, even though it’s sexier and shorter, use the long version and you’ll be writing more bulletproof code.
ECMAScript 6 Array.from()
The new ECMAScript 6 Harmony standard introduces the
Array.from method which makes Array-like Objects (such as the NodeList) and other iterable Objects (such as an
String) to Array conversion a breeze.
More on the Array.from method.
Looping through NodeLists on-the-fly
For some time I thought it was pretty cool to do this, which takes the Prototypal methods one step further:
Using the forEach() method and using call, again, this iterates over the NodeList is an Array fashion, almost converting it on the fly but never changing the original reference.
As above, we can use the shorthand empty array reference like so, but we’ve established that’s not a good idea:
As promised, polyfill(s) for you to drop in:
Dropping in the above will run a quick feature detect on the forEach method and patch the browser functionality for you, which means you can do this and it’ll work in every browser:
Hooray for ECMAScript 5!
I particularly don’t like iterating over the NodeList on the fly, my advice would be to always convert your NodeLists and then you’ll never have any issues at a later date or with other parts of your scripts. Again, the method that you choose to manipulate over your NodeLists is project and script dependent, so learn what each method does and make your decision wisely :)