Essentially, a NodeList
is what you get when you call any method such as elem.getElemetsByTagName()
, elem.querySelectorAll()
and so on.
We should note here that NodeLists
are not exactly part of the JavaScript but they are instead part of the DOM APIs
the browsers provide through JavaScript.
var myList = document.querySelectorAll('.story-item');
console.log(myList)
[
<div class="story-item">…</div>
,
<div class="story-item">…</div>
,
[…]
,
<div class="story-item">..</div>
,
]
// basic array actions
console.log(myList.length) // 7
console.log(myList[2]) // <div class="story-item">..</div>
So far, myList
has been talking and walking like an array so we can probably assume that it’s an array of some sorts. However, it all goes to hell when you try to call any of the basic array methods
:
myList.slice(2) // indexed from 0
TypeError: Result of expression 'myList.slice' [undefined] is not a function.
Wait, what happened? Well, this is where the between NodeLists
and arrays in JavaScript start to surface. Let’s see what is distinguish array
and NodeList
:
console.log(myList.constructor.prototype) // "[object NodeListConstructor]"
var surelyArray = ['foo', 'bar'];
console.log(surelyArray.constructor.prototype) //"function Array() { [native code] }"
So those two elements, myList
and surelyArray
are definitely constructed by different constructors so it’s no wonder that they don’t share the same methods.
While arrays
are essentially a collection of elements held in memory and are part of the JavaScript, NodeLists
are live references to actual DOM elements.
Let’s see a quick way to convert a NodeList
into an array:
//borrowing the slice() method from the Array’s prototype
var myArray = Array.prototype.slice.call(myList, 0);
console.log(myArray.constructor.prototype) //"function Array() { [native code] }"
//call pop method
myArray.pop() //<div class="story-item">…</div>
//Internet Explorer 9 cannot handle calling slice() on NodeLists
var myIEArray = [];
for (var i = 0; i < myList.length; ++i) { myIEArray.push(myList[i]); }
console.log(myIEArray.constructor.prototype) //"function Array() { [native code] }"
ES6 approach:
// Spread operator
[...elements].forEach(callback);
// Array.from()
Array.from(elements).forEach(callback);
// for...of statement
for (var div of elements) { callback(div) };
One thing is worth mentioning though; when you convert your NodeList
into an array
, you are no longer dealing with a live NodeList
but instead an array of DOM nodes.