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 .
querySelectorAll() The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors.
Definition and Usage The querySelector() method returns the first element that matches a CSS selector. To return all matches (not only the first), use the querySelectorAll() instead. Both querySelector() and querySelectorAll() throw a SYNTAX_ERR exception if the selector(s) is invalid.
In modern JavaScript, the simplest and easiest way to convert a NodeList object to an array is by using the Array. from() method: // create a `NodeList` object const divs = document. querySelectorAll('div'); // convert `NodeList` to an array const divsArr = Array.
You can use ES2015 (ES6) spread operator:
[...document.querySelectorAll('div')]
will convert StaticNodeList to Array of items.
Here is an example on how to use it.
[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div>
<div>Text 2</div>
I believe it to be a philosophical decision of the W3C. The design of the W3C DOM [spec] is quite orthogonal to the design of JavaScript, as the DOM is meant to be platform and language neutral.
Decisions like "getElementsByFoo()
returns an ordered NodeList
" or "querySelectorAll()
returns a StaticNodeList
" are very much intentional, so that implementations don't have to worry about aligning their returned data structure based on language-dependent implementations (like .map
being available on Arrays in JavaScript and Ruby, but not on Lists in C#).
The W3C aim low: they'll say a NodeList
should contain a readonly .length
property of type unsigned long because they believe every implementation can at least support that, but they won't say explicitly that the []
index operator should be overloaded to support getting positional elements, because they don't want to stymie some poor little language that comes along that wants to implement getElementsByFoo()
but cannot support operator overloading. It's a prevalent philosophy present throughout much of the spec.
John Resig has voiced a similar option as yours, to which he adds:
My argument isn't so much that
NodeIterator
isn't very DOM-like it's that it isn't very JavaScript-like. It doesn't take advantage of the features present in the JavaScript language and use them to the best of its ability...
I do somewhat empathize. If the DOM was written specifically with JavaScript features in mind it would be a lot less awkward and more intuitive to use. At the same time I do understand the W3C's design decisions.
I don't know why it returns a node list instead of an array, maybe because like getElementsByTagName it will update the result when you update the DOM. Anyway a very simple method to transform that result in a simple array is:
Array.prototype.slice.call(document.querySelectorAll(...));
and then you can do:
Array.prototype.slice.call(document.querySelectorAll(...)).map(...);
Just to add to what Crescent said,
if it's just one function you want, you can do something like NodeList.prototype.map = Array.prototype.map
Don't do this! It's not at all guaranteed to work.
No JavaScript or DOM/BOM standard specifies that the NodeList
constructor-function even exists as a global/window
property, or that the NodeList
returned by querySelectorAll
will inherit from it, or that its prototype is writable, or that the function Array.prototype.map
will actually work on a NodeList.
A NodeList is allowed to be a ‘host object’ (and is one, in IE and some older browsers). The Array
methods are defined as being allowed to operate on any JavaScript ‘native object’ that exposes numeric and length
properties, but they're not required to work on host objects (and in IE, they don't).
It's annoying that you don't get all the array methods on DOM lists (all of them, not just StaticNodeList), but there's no reliable way round it. You'll have to convert every DOM list you get back to an Array manually:
Array.fromList= function(list) {
var array= new Array(list.length);
for (var i= 0, n= list.length; i<n; i++)
array[i]= list[i];
return array;
};
Array.fromList(element.childNodes).forEach(function() {
...
});
Array.from(document.querySelectorAll(...)).map(...)
Not available on IE11 though https://caniuse.com/mdn-javascript_builtins_array_from
I think you can simply do following
Array.prototype.map.call(document.querySelectorAll(...), function(...){...});
It works perfect for me
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