106

In my JSFiddle, I’m simply trying to iterate over an array of elements. The array is non-empty, as the log statements prove. Yet the call to forEach gives me the (not so helpful) “Uncaught TypeError: undefined is not a function” error.

I must be doing something stupid; what am I doing wrong?

My code:

var arr = document.getElementsByClassName('myClass');
console.log(arr);
console.log(arr[0]);
arr.forEach(function(v, i, a) {
  console.log(v);
});
.myClass {
  background-color: #FF0000;
}
<div class="myClass">Hello</div>

7
  • 8
    arr is not an array, but a HTMLCollection. It doesn't have the same methods as an array. developer.mozilla.org/en-US/docs/Web/API/… . Here's a SO post about it even: stackoverflow.com/questions/13433799/… Commented Jun 17, 2014 at 14:23
  • Something like [1,2,3].forEach(function(v,i,a) { console.log(v); }); is fine. What's the difference between this and the array in my example? Commented Jun 17, 2014 at 14:24
  • You don't have an array in your example. What makes you think it's an array? Commented Jun 17, 2014 at 14:25
  • 3
    @Jer: As arr instanceof Array will result in false it cannot avail of any prototype methods of the Array object such as Array.prototype.forEach(). arr is a HTMLCollection and an array like object (but does not inherit from or instantiate Array). Hence your standard for loop will work as that simply iterates through index of the object and is not a prototype of Array. Commented Jun 17, 2014 at 14:32
  • 1
    @Jer—you should look into the differences between built–in and host objects. The former conform to ECMA-262, the later only as much as the host wishes. The DOM has many objects that allow access to members by index (document.images, document.forms, form.elements, select.options, etc.), mostly based on the NodeList interface. Commented Jun 17, 2014 at 14:38

3 Answers 3

183

That's because document.getElementsByClassName returns a HTMLCollection, not an array.

Fortunately it's an "array-like" object (which explains why it's logged as if it was an object and why you can iterate with a standard for loop), so you can do this :

[].forEach.call(document.getElementsByClassName('myClass'), function(v,i,a) {})

With ES6 (on modern browsers or with Babel), you may also use Array.from which builds arrays from array-like objects:

Array.from(document.getElementsByClassName('myClass')).forEach( v=> {})

or spread the array-like object into an array:

[...document.getElementsByClassName('myClass')].forEach( v=> {})
Sign up to request clarification or add additional context in comments.

12 Comments

@Jer arguments is one. jQuery objects are another. You could make one yourself: var a = {"0": "str1", "1": "str2", length: 2}
Here we go with old browsers again… passing a host object to a native method will fail in IE 8 and lower. Maybe no one cares, but some might. ;-) Oh, it doesn't support getElementsByClassName either, but querySelectorAll('.myClass') should work. I'm still waiting for iterators to be added to the NodeList API. :-(
@Jer: As a side-note if you intend to break out of the loop for any reason Array.prototype.forEach won't let you do that. If you have that requirement later use standard for loop or if you want to use the array object use Array.prototype.every or Array.prototype.some (note though that every/some are not supported in IE8 or less)
@Ian You need splice for the object to be "array-like". Compare the logs here : jsbin.com/sigut/1/edit
@Ian TBH the definition of "array-like" is very fuzzy and depends on the use. Sometimes I doesn't include splice in that definition but when I want to be more "array-like" to be able to use map, filter, and so on, then I include it. Simple iteration using forEach doesn't need splice.
|
11

Try this it should work :

<html>
  <head>
    <style type="text/css">
    </style>
  </head>
  <body>
   <div class="myClass">Hello</div>
   <div class="myClass">Hello</div>

<script type="text/javascript">
    var arr = document.getElementsByClassName('myClass');
    console.log(arr);
    console.log(arr[0]);
    arr = [].slice.call(arr); //I have converted the HTML Collection an array
    arr.forEach(function(v,i,a) {
        console.log(v);
    });
</script>


<style type="text/css">
    .myClass {
    background-color: #FF0000;
}
</style>

  </body>
</html>

Comments

0

in the event that you want to access the ID of each element of a specific class you can do the following:

    Array.from(document.getElementsByClassName('myClass')).forEach(function(element) {
        console.log(element.id);
    });

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.