Loop Over querySelectorAll Matches

You can loop over Arrays really easily in JavaScript with forEach, but unfortunately it's not that simple with the results of a querySelectorAll.

/* Will Not Work */
document.querySelectorAll('.module').forEach(function() {
  
});

That's because what you get back from querySelectorAll isn't an array, it's a (non-live) NodeList.

Here's a quick way to iterate over all the found elements:

var divs = document.querySelectorAll('div');

[].forEach.call(divs, function(div) {
  // do whatever
  div.style.color = "red";
});

Fair warning, Todd Motto explains why this method is a rather hacky, detailing over 10 problems with it.


You could also use a classic for loop:

var divs = document.querySelectorAll('div'), i;

for (i = 0; i < divs.length; ++i) {
  divs[i].style.color = "green";
}

Todd's suggestion is to make your own method:

// forEach method, could be shipped as part of an Object Literal/Module
var forEach = function (array, callback, scope) {
  for (var i = 0; i < array.length; i++) {
    callback.call(scope, i, array[i]); // passes back stuff we need
  }
};

// Usage:
// optionally change the scope as final parameter too, like ECMA5
var myNodeList = document.querySelectorAll('li');
forEach(myNodeList, function (index, value) {
  console.log(index, value); // passes index + value back!
});

There are also for..of loops, but...

/* Be warned, this only works in Firefox */

var divs = document.querySelectorAll('div );

for (var div of divs) {
  div.style.color = "blue";
}

This is pretty intense (probably dangerous and generally not recommended) but you could make NodeList have the same forEach function as Array does, then use it.

NodeList.prototype.forEach = Array.prototype.forEach;

var divs = document.querySelectorAll('div').forEach(function(el) {
  el.style.color = "orange";
})

There is a bit more information in the MDN article.

Comments

  1. Aneece
    Permalink to comment#

    The safer way to do the last version and co-opt the Array method would be to do something like this:

    Array.prototype.forEach.call(document.querySelectorAll('div'), function(div) { console.log(div); })
    

    Of course, you still need to make sure you have a polyfill for Array.forEach if you’re targeting older browsers.

  2. b
    Permalink to comment#

    Hey this saved me some time – I was curious why I couldn’t loop over with forEach. Thanks!

  3. wumble
    Permalink to comment#

    there is a pretty nice function that gives you a array:

    function getDomNodeArray(selector) {
    // get the elements as a DOM collection
    var elemCollection = document.querySelectorAll(selector);

    // coerce the DOM collection into an array
    var elemArray = Array.prototype.slice.apply(elemCollection);

    return elemArray;
    };

    • Mauro Cn
      Permalink to comment#

      That’s exactly what I use. I use an extra optional argument, which is the “context” or the element in which the querySelectorAll() is called (default is document)

      function $$(query, context) {
        return Array.prototype.slice.call(
          (context || document).querySelectorAll(query)
        );
      }
      

      Then usage may be:

      $$('p').forEach(function() {});
      
      var myForm = document.querySelector('#myForm');
      
      // the following can be read as: "all input type checkbox INSIDE myForm"
      var checkedBoxes = $$('input[type="checkbox"]', myForm).filter(function(inp) { return this.checked});
      

      ETC…. It is very powerful yet very simple. It has also a one-line-form:

      function $$(q, c) {return Array.prototype.slice.call((c||document).querySelectorAll(q));}
      

      I just wanted to share this “discovery”….

  4. topek
    Permalink to comment#

    ES2015 variant using spread operator:

    [...document.querySelectorAll('img')].forEach(doStuff);
    
  5. Todd
    Permalink to comment#

    If you’re using ES6, you should also be able to do this:

    Array.from(querySelectorAll('img')).forEach(img => doStuff);

    Clean and simple.

  6. Burak Kalkan
    Permalink to comment#

    Overload the NodeList, then no headache :)

    NodeList.prototype.forEach = Array.prototype.forEach;
    
    document.querySelectorAll('img').forEach(function{
      // code
    });
    
  7. Gabriel Santerre
    Permalink to comment#

    This is should be the fastest way:

    var arr = [];
    for (var i = 0, ref = arr.length = nl.length; i < ref; arr[i] = nl[i++]);
    
    • Gabriel Santerre
      Permalink to comment#

      nl equal to a NodeLIst

    • Mauro Cn
      Permalink to comment#

      A different aproach, a little more straightforward:

      // nl = document.querySelectorAll('blabla');
      for(var arr=[], i=nl.length;i;arr[--i]=nl[i]);
      

      (Tested.)

  8. Corey
    Permalink to comment#

    You can also do

    function toArray(arr) { var a = []; a.push.apply(a, arr); return a; }
    
    var elements = toArray(document.querySelectorAll('div'));
    
    elements.forEach(...)
    
  9. dave
    Permalink to comment#

    or you could just use Array.from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from

    then it would actually be an Array.

    var arr = Array.from(document.querySelectorAll('div'));
    
  10. Mark D
    Permalink to comment#
    [].map.call(myArrayVar, function(myVar){
        // do something here with 'myVar'
    });
    

Leave a Comment

Posting Code

We highly encourage you to post problematic HTML/CSS/JavaScript over on CodePen and include the link in your post. It's much easier to see, understand, and help with when you do that.

Markdown is supported, so you can write inline code like `<div>this</div>` or multiline blocks of code in in triple backtick fences like this:

```
<script>
  function example() {
    element.innerHTML = "<div>code</div>";
  }
</script>
```

We have a pretty good* newsletter.