Iterating over result of getElementsByClassName using Array.forEach

JavascriptForeachGetelementsbyclassname

Javascript Problem Overview


I want to iterate over some DOM elements, I'm doing this:

document.getElementsByClassName( "myclass" ).forEach( function(element, index, array) {
  //do stuff
});

but I get an error:

> document.getElementsByClassName("myclass").forEach is not a function

I am using Firefox 3 so I know that both getElementsByClassName and Array.forEach are present. This works fine:

[2, 5, 9].forEach( function(element, index, array) {
  //do stuff
});

Is the result of getElementsByClassName an Array? If not, what is it?

Javascript Solutions


Solution 1 - Javascript

No, it's not an array. As specified in DOM4, it's an HTMLCollection (in modern browsers, at least. Older browsers returned a NodeList).

In all modern browsers (pretty much anything other IE <= 8), you can call Array's forEach method, passing it the list of elements (be it HTMLCollection or NodeList) as the this value:

var els = document.getElementsByClassName("myclass");

Array.prototype.forEach.call(els, function(el) {
    // Do stuff here
    console.log(el.tagName);
});

// Or
[].forEach.call(els, function (el) {...});

If you're in the happy position of being able to use ES6 (i.e. you can safely ignore Internet Explorer or you're using an ES5 transpiler), you can use Array.from:

Array.from(els).forEach((el) => {
    // Do stuff here
    console.log(el.tagName);
});

Solution 2 - Javascript

You can use Array.from to convert collection to array, which is way cleaner than Array.prototype.forEach.call:

Array.from(document.getElementsByClassName("myclass")).forEach(
    function(element, index, array) {
        // do stuff
    }
);

In older browsers which don't support Array.from, you need to use something like Babel.


ES6 also adds this syntax:

[...document.getElementsByClassName("myclass")].forEach(
    (element, index, array) => {
        // do stuff
    }
);

Rest destructuring with ... works on all array-like objects, not only arrays themselves, then good old array syntax is used to construct an array from the values.


While the alternative function querySelectorAll (which kinda makes getElementsByClassName obsolete) returns a collection which does have forEach natively, other methods like map or filter are missing, so this syntax is still useful:

[...document.querySelectorAll(".myclass")].map(
    (element, index, array) => {
        // do stuff
    }
);

[...document.querySelectorAll(".myclass")].map(element => element.innerHTML);

Solution 3 - Javascript

Or you can use querySelectorAll which returns NodeList:

document.querySelectorAll('.myclass').forEach(...)

Supported by modern browsers (including Edge, but not IE):
Can I use querySelectorAll
NodeList.prototype.forEach()

MDN: Document.querySelectorAll()

Solution 4 - Javascript

Edit: Although the return type has changed in new versions of HTML (see Tim Down's updated answer), the code below still works.

As others have said, it's a NodeList. Here's a complete, working example you can try:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script>
            function findTheOddOnes()
            {
                var theOddOnes = document.getElementsByClassName("odd");
                for(var i=0; i<theOddOnes.length; i++)
                {
                    alert(theOddOnes[i].innerHTML);
                }
            }
        </script>
    </head>
    <body>
        <h1>getElementsByClassName Test</h1>
        <p class="odd">This is an odd para.</p>
        <p>This is an even para.</p>
        <p class="odd">This one is also odd.</p>
        <p>This one is not odd.</p>
        <form>
            <input type="button" value="Find the odd ones..." onclick="findTheOddOnes()">
        </form>
    </body>
</html>

This works in IE 9, FF 5, Safari 5, and Chrome 12 on Win 7.

Solution 5 - Javascript

The result of getElementsByClassName() is not an Array, but an array-like object. Specifically it's called an HTMLCollection, not to be confused with NodeList (which has it's own forEach() method).

One simple way with ES2015 to convert an array-like object for use with Array.prototype.forEach() that hasn't been mentioned yet is to use the spread operator or spread syntax:

const elementsArray = document.getElementsByClassName('myclass');

[...elementsArray].forEach((element, index, array) => {
    // do something
});

Solution 6 - Javascript

getElementsByClassName returns HTMLCollection in modern browsers.

which is array-like object similar to arguments which is iteratable by for...of loop see below what MDN doc is saying about it:

> The for...of statement creates a loop iterating over iterable objects, > including: built-in String, Array, Array-like objects (e.g., arguments > or NodeList), TypedArray, Map, Set, and user-defined iterables. It > invokes a custom iteration hook with statements to be executed for the > value of each distinct property of the object.

Javascript Example

for (const element of document.getElementsByClassName("classname")){
   element.style.display="none";
}

Typescript Example

let elements = document.getElementsByClassName('classname');
let i;

for (i = 0; i < elements.length; i++) {

  if (elements[i] instanceof HTMLElement) {
    elements[i].style.display = "none";
  }

}

Solution 7 - Javascript

> Is the result of getElementsByClassName an Array?

No

> If not, what is it?

As with all DOM methods that return multiple elements, it is a NodeList, see https://developer.mozilla.org/en/DOM/document.getElementsByClassName

Solution 8 - Javascript

As already said, getElementsByClassName returns a HTMLCollection, which is defined as

[Exposed=Window]
interface HTMLCollection {
readonly attribute unsigned long https://www.w3.org/TR/dom/#dom-htmlcollection-length">length</a>;;
getter https://www.w3.org/TR/dom/#element">Element</a>;? https://www.w3.org/TR/dom/#dom-htmlcollection-item">item</a>(unsigned long index);
getter https://www.w3.org/TR/dom/#element">Element</a>;? https://www.w3.org/TR/dom/#dom-htmlcollection-nameditem">namedItem</a>(DOMString name);
};

Previously, some browsers returned a NodeList instead.

[Exposed=Window]
interface NodeList {
getter https://www.w3.org/TR/dom/#node">Node</a>;? https://www.w3.org/TR/dom/#dom-nodelist-item">item</a>(unsigned long index);
readonly attribute unsigned long https://www.w3.org/TR/dom/#dom-nodelist-length">length</a>;;
iterable<https://www.w3.org/TR/dom/#node">Node</a>&gt;;
};

The difference is important, because DOM4 now defines NodeLists as iterable.

According to Web IDL draft,

> Objects implementing an interface that is declared to be iterable > support being iterated over to obtain a sequence of values. > > > Note: In the ECMAScript language binding, an interface that is > > iterable will have “entries”, “forEach”, “keys”, “values” and > > @@iterator properties on its interface prototype object.

That means that, if you want to use forEach, you can use a DOM method which returns a NodeList, like querySelectorAll.

document.querySelectorAll(".myclass").forEach(function(element, index, array) {
  // do stuff
});

Note this is not widely supported yet. Also see forEach method of Node.childNodes?

Solution 9 - Javascript

This is the safer way:

var elements = document.getElementsByClassName("myclass");
for (var i = 0; i < elements.length; i++) myFunction(elements[i]);

Solution 10 - Javascript

Here is a test I created on jsperf: https://jsperf.com/vanillajs-loop-through-elements-of-class

The most perfomant version in Chrome and Firefox is the good old for loop in combination with document.getElementsByClassName:

var elements = document.getElementsByClassName('testClass'), elLength = elements.length;
for (var i = 0; i < elLength; i++) {
    elements.item(i).textContent = 'Tested';
};

In Safari this variant is the winner:

var elements = document.querySelectorAll('.testClass');
elements.forEach((element) => {
    element.textContent = 'Tested';
});

If you want the most perfomant variant for all browsers it might be this one:

var elements = document.getElementsByClassName('testClass');
Array.from(elements).map(
    (element) => {
        return element.textContent = 'Tested';
    }
);

Solution 11 - Javascript

It does not return an Array, it returns a NodeList.

Solution 12 - Javascript

Use this code to add forEach method to HTMLCollection

/**
 *
 * @type {Function}
 */
HTMLCollection.prototype.forEach = HTMLCollection.prototype.forEach ||
    function (callBack) {
        for (let i = 0; i < this.length; i++) {
            callBack(this[i])
        }
    };

Then your code will work:

document.getElementsByClassName( "myclass" ).forEach( function(element, index, array) {
  //do stuff
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSteve ClaridgeView Question on Stackoverflow
Solution 1 - JavascriptTim DownView Answer on Stackoverflow
Solution 2 - JavascriptAthariView Answer on Stackoverflow
Solution 3 - Javascripticl7126View Answer on Stackoverflow
Solution 4 - Javascriptjames.garrissView Answer on Stackoverflow
Solution 5 - JavascriptKloptikusView Answer on Stackoverflow
Solution 6 - JavascriptHaritsinh GohilView Answer on Stackoverflow
Solution 7 - JavascriptQuentinView Answer on Stackoverflow
Solution 8 - JavascriptOriolView Answer on Stackoverflow
Solution 9 - JavascriptgildniyView Answer on Stackoverflow
Solution 10 - JavascriptStefanSLView Answer on Stackoverflow
Solution 11 - Javascriptreko_tView Answer on Stackoverflow
Solution 12 - JavascriptАндрейView Answer on Stackoverflow