15

I have a table which contains 3 rows. Each row has the class: .myClass.

I then query for the table rows with document.getElementsByClassName('myClass') and iterate over the elements, changing each row's class to .otherClass.

However,

console.log(document.getElementsByClassName('otherClass'))

only returned one row.

And, when I looked at the DOM, only the first .myClass row had its class changed to .otherClass; the other remained untouched.

How can I change the class of all .myClass rows to .otherClass?

var c = document.getElementsByClassName('myTable')[0];
var x = c.getElementsByClassName('myClass');

for (var i = 0; i < x.length; i++) {
  x[i].className = 'otherClass';
}

x = c.getElementsByClassName('otherClass');

console.log(x);  // only one element
<table class="myTable">
  <tr class="myClass2">
    <td>Content</td>
    <td>Content</td>
  </tr>
  <tr class="myClass">
    <td>Content</td>
    <td>Content</td>
  </tr>
  <tr class="myClass">
    <td>Content</td>
    <td>Content</td>
  </tr>
</table>
royhowie
  • 11,075
  • 14
  • 50
  • 67
Mitre
  • 273
  • 1
  • 3
  • 11

3 Answers3

25

getElementsByClassName, like other HTML collections, is "live", that is, when you assign another class name to its member, it's removed from the collection on the fly and its length gets decremented. That's why your loop runs only once.

var x = document.getElementsByClassName('myClass');
alert("before: " + x.length);
x[0].className='otherClass';  
alert("after: " + x.length);
.myClass { color: black }
.otherClass { color: red }
<b class="myClass">hi</b>
<b class="myClass">hi</b>

Docs:

An HTMLCollection in the HTML DOM is live; it is automatically updated when the underlying document is changed.


To answer in context to your question, you could set the className of the first element until there are none left in the collection:

while(x.length > 0) {
   x[0].className = 'otherClass';  
}
Samuel Liew
  • 76,741
  • 107
  • 159
  • 260
georg
  • 211,518
  • 52
  • 313
  • 390
  • 1
    This is news to me - I never knew the arrays were "live". I assumed (as I guess most people would) that an array created by `.getElementsByClassName` would not be changed by a change to the objects contained with them – freefaller Jul 09 '15 at 08:25
  • 3
    @freefaller it's not exactly an array; it's an [`HTMLCollection`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) – royhowie Jul 09 '15 at 08:30
  • 1
    Yeah i didnt know that also. SO how you work around this if you wanna modify className of items that you got by getElementsByClassName? – Mitre Jul 09 '15 at 08:31
  • @Mitre iterate only while the collection has a length – royhowie Jul 09 '15 at 08:33
  • 1
    Nice that solution with WHILE works. Thank you for yoru answer. – Mitre Jul 09 '15 at 08:33
  • I new this was the issue but couldn't realize that a while loop solves it. duh. I was thinking about passing values, then pulling back with reference ... sometimes you just gotta keep it simple. – user1934286 Jul 31 '20 at 01:13
3

As georg pointed out in his answer, getElementsByClassName returns a "live" collection. That means the array will "update" as the elements change.

To fix your problem, you should use a while loop, iterating while x.length exists, and only changing the first element of the HTMLCollection.

var c = document.getElementsByClassName('myTable')[0];
var x = c.getElementsByClassName('myClass');
while (x && x.length) {
  x[0].className = 'otherClass'
}
var y = c.getElementsByClassName('otherClass'); 
alert(y.length);
.myClass {
  display:block;
  background-color: red;
}
.otherClass {
  display:block;
  background-color:green;
}
<table class="myTable">
<tr class="myClass2">
 <td>Content</td>
 <td>Content</td>
</tr>
<tr class="myClass">
 <td>Content</td>
 <td>Content</td>
</tr>
<tr class="myClass">
 <td>Content</td>
 <td>Content</td>
</tr>
<table>
Community
  • 1
  • 1
royhowie
  • 11,075
  • 14
  • 50
  • 67
1

Georg is right. Elements array is updated on the fly, so you cannot depend on it's length;

Try this code:

var c = document.getElementsByClassName('myTable')[0],
    x = c.getElementsByClassName('myClass');

while (x.length) {
    x[0].className = 'otherClass';
}
var y = c.getElementsByClassName('otherClass');
alert(y.length);

Working fiddle

Roumelis George
  • 6,602
  • 2
  • 16
  • 31