for of loop querySelectorAll
Asked Answered
T

9

17

Mozilla states that "for of loops will loop over NodeList objects correctly". (source: https://developer.mozilla.org/en-US/docs/Web/API/NodeList) However, this doesn't work in Chrome 43. Is this incorrect documentation or a browser bug?

The copied example code used on a page with checkboxes:

var list = document.querySelectorAll( 'input[type=checkbox]' );
for (var item of list) {
  item.checked = true;
}
Thermocline answered 15/6, 2015 at 1:31 Comment(4)
Could we have a bit more context? Can you give us an example or anything?Unlash
I’d like to know what exactly doesn’t work in Chrome. Does it throw a syntax error? Does it check none of the checkboxes?Unlash
for..of loops only support objects that are implemented as iterators, containing a Symbol.iterator key/method. Currently, in Chrome, console.log(Symbol.iterator in list); // false.Hecklau
@Xufox it throws "Uncaught TypeError: undefined is not a function"Othella
T
11

Edit: This is shipping in Chrome 51.

Jake Archibald posted a simple fix:

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

And for of loops.

Thermocline answered 19/9, 2015 at 17:30 Comment(2)
Same applies to others like HTMLCollection.Tucson
It is inherently dangerous to alter environment and this suggestion is doing exactly that. Better just cast querySelectorAll to Array in place: for (let element of [...document.querySelectorAll('selector')]) {...}Minx
L
6

The docs are correct, but I wouldn't call this a bug. Rather it's a "not yet implemented feature".

There is no standard for this, and there is still active discussion on how the DOM should integrate with ES6. Notice that it is clear that querySelectorAll should return something iterable which can be used in a for of loop (as the common expectation demands), but it's not clear how that should happen (Let NodeList implement the Iterable interface? Let some Elements collection subclass Array?).

Landholder answered 15/6, 2015 at 1:49 Comment(0)
C
3

You can use Array.from

let _list = document.querySelectorAll('input[type=checkbox]');

let list = Array.from(_list);

for (let item of list) {
  //... just like an array
  item.checked = true
}

or more shortly

let list = document.querySelectorAll('input[type=checkbox]');

for (let item of Array.from(list)) {
  item.checked = true
}

Important note Array.from was introduced in Chrome 45 source.

Culp answered 14/12, 2015 at 0:7 Comment(3)
How is that better? You need to call Array.from every time.Thermocline
Yes, it's true that you have to call Array.from every single time (and maybe include a polyfill for that), but with this method you aren't trying to extend the DOM.Culp
What does this have to do with extending the DOM?Thermocline
T
2

Since I've successfully used for..of in Gecko to iterate NodeLists, it seems this is a browser bug, or at least a browser lack.

Actual working code from a userscript I currently use:

let llnk = document.querySelectorAll("div#threadlist a.threadtitle_unread");
for (let lnk of llnk) {
    //...
}

(This also uses let, but that's another story.)

Tenerife answered 15/6, 2015 at 1:40 Comment(0)
B
1

Try utilizing Array.prototype.entries()

var list = [].entries.call(document.querySelectorAll("input[type=checkbox]"));

for (item of list) {
  item[1].checked = true;
};
<input type="checkbox" /><input type="checkbox" />

You could also use Array.prototype.values()

var list = [].values.call(document.querySelectorAll("input[type=checkbox]"));

for (item of list) {
  item.checked = true;
};
<input type="checkbox" /><input type="checkbox" />
Bev answered 15/6, 2015 at 3:38 Comment(0)
F
1

Here's yet another solution for the modern age:

[...document.querySelectorAll("input[type=checkbox]")].forEach(node => {
     node.textContent = "foo";
});

This takes advantage of the spread operator which is supported in Google Chrome 46+, Firefox 16+, and Edge, and just for fun the arrow function.

Further answered 1/9, 2016 at 18:46 Comment(4)
What is the purpose of reconstructing the array?Thermocline
The result of querySelectorAll is a NodeList, not an array. By converting it we can easily iterate over it.Further
What difference does it make now? They are both iterable.Thermocline
As other answers have pointed out, unless you assign the iterator of an Array to a NodeList, some browsers will give you problems (hence your original question). I was just offering an alternative clean syntactical way to do a forEach iteration instead of using from or call.Further
D
0

This is what I do, for a different approach

Array.prototype.forEach.call(document.querySelectorAll("input[type=checkbox]"),function(ele,idx)
{
    ele.checked = true;
}

good from IE9 and above

Deuteragonist answered 14/12, 2015 at 0:15 Comment(0)
S
0

Native Symbol.iterator support for NodeList was added to the WHATWG's DOM spec in 2014.

Unfortunately, Chrome 51 is the first version of Chrome to support it, and its Beta has only just been released at the time of writing this answer. Also, there's no support in any version of Internet Explorer or Edge.

To add Symbol.iterator support for NodeList in all browsers to your code, just use the following polyfill :

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
Smuggle answered 29/4, 2016 at 22:39 Comment(0)
P
0

I had this problem. Turns out mine was caused by calling Promise.all() with parameters instead of an array. For example:

Before: Promise.all(p1, p2)

After: Promise.all([p1, p2])

Hope this helps someone.

Pedestal answered 17/8, 2017 at 18:1 Comment(1)
What does this have to do with NodeList?Manganate

© 2022 - 2024 — McMap. All rights reserved.