Let's say I have the following list:
<ol>
<li>Cookies <ol>
<li>Coffee</li>
<li>Milk</li>
<li class="test1">Chocolate </li>
</ol>
and I perform this selection at the end of my html
var nodes = document.querySelectorAll('li:first-of-type');
When I tried in Chrome nodes.forEach
it gave me an error. When I looked at the value it looked like an array. I actually was able to navigate it using a regular for like:
for(var i=0;i<nodes.length;i++){
nodes[i].onclick= function(){ alert('Hello!'); };
}
So, what is the actual returned type of document.querySelectorAll
? why array methods did not work?
So, it looks like an array, can workaround it to make it work like an array but it is not an array?
The querySelectorAll method returns an array-like object called a node list. These data structures are referred to as “Array-like”, because they appear as an array, but can not be used with array methods like map and forEach .
querySelector() will return the first element that matches the specified group of selectors. If no match is found 'null' is returned.
The querySelectorAll() methods on the Document , DocumentFragment , and Element interfaces must return a NodeList containing all of the matching Element nodes within the subtrees of the context node, in document order.
The querySelectorAll() method in HTML is used to return a collection of an element's child elements that match a specified CSS selector(s), as a static NodeList object. The NodeList object represents a collection of nodes. The nodes can be accessed by index numbers.
The type of the result is a NodeList.
Since it is an Array-like object, you can run the map
, forEach
and other Array.prototype functions on it like this:
var result = document.querySelectorAll('a');
Array.prototype.map.call(result, function(t){ return t; })
The map
, forEach
, any
and other functions in the Array prototype work on Array-like objects. For example, let's define an object literal with numerical indexes (0,1) and a length property:
var arrayLike = { '0': 'a', '1': 'b', length: 2};
The forEach method, applied to the arrayLike
object will like on a real Array.
Array.prototype.forEach.call(arrayLike, function(x){ console.log(x) } ); //prints a and b
In the documentation, it says that :
[the returned list of elements] is a non-live
NodeList
of element objects.
NodeList
is different from an array (entirely-different prototype-chain) and the documentation also tells you why you cannot use forEach
:
Why can't I use
forEach
ormap
on aNodeList
?
NodeList
are used very much like arrays and it would be tempting to useArray.prototype
methods on them, however they don't have those methods.JavaScript has an inheritance mechanism based on prototypes for both built–in objects (like
Array
s) and host objects (likeNodeList
s).Array
instances inherit array methods (such asforEach
ormap
) because their prototype chain looks like the following:
myArray --> Array.prototype --> Object.prototype --> null
(The prototype chain of an object can be obtained by calling Object.getPrototypeOf several times.)
forEach
,map
and the likes are own properties of theArray.prototype
object.Unlike arrays,
NodeList
prototype chain looks like the following:
myNodeList --> NodeList.prototype --> Object.prototype --> null
NodeList.prototype
contains the item method, but none of theArray.prototype
methods, so they cannot be used onNodeLists
.
However, there are workarounds. Again from the documentation, you can use the Array.prototype.forEach
and Array.prototype.map
methods directly like so:
var forEach = Array.prototype.forEach;
var divs = document.getElementsByTagName( 'div' );
var firstDiv = divs[ 0 ];
forEach.call(firstDiv.childNodes, function( divChild ){
divChild.parentNode.style.color = '#0F0';
});
UPDATE:
It seems NodeLists
can now be accessed like an array* in this
updated documentation:
Accessing the matches
Once the NodeList of matching elements is returned, you can examine it just like any array. If the array is empty (that is, its length property is 0), then no matches were found.
Otherwise, you can simply use standard array notation to access the contents of the list. You can use any common looping statement, such as:
var highlightedItems = userList.querySelectorAll(".highlighted");
highlightedItems.forEach(function(userItem) {
deleteUser(userItem);
});
*While you can now use forEach
, other methods like map
are not available.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With