4

I have the following problem.

I generate a list with all the elements with a specific class. After that i'm looping throug them to replace the class.

As you can see here: https://jsfiddle.net/nafxLoLz/ only one of the 2 elements get replaced. The console.log clearly shows that the loop only goes through once. If I comment the 7 javascript code out. (See the fiddle) the loop works fine.

What did I do wrong?

Code:

var list = document.getElementsByClassName("default");
console.log(list.length);
for(var i = 0; i < list.length; i++)
{
  console.log(i);
  list[i].classList.add("red");
  list[i].classList.remove("default");
}
BlueFire
  • 45
  • 1
  • 5

3 Answers3

7

getElementsByClassName gets a live nodeList of all the elements with the class default, the nodeList updates as the elements change.

When you remove the class default, the length of the live nodeList changes, so when you start iterating at the start, for every element that has the class removed, the length decreases by 1.

When you get halfway, you've changed half the elements, and you're half way in the loop, and the length is just half, so it stops.

The solution is to iteratte in reverse

var list = document.getElementsByClassName("default");

for(var i=list.length; i--;) {
  list[i].classList.add("red");
  list[i].classList.remove("default");
}

or get a non-live nodeList using querySelectorAll

var list = document.querySelectorAll(".default");
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • Darn, beat me to it. I would (personally) usually prefer a while loop though: `while (list.length > 0)` – Alfred Xing Nov 24 '16 at 19:18
  • 1
    @AlfredXing - a `while` loop works too in this case, but it requires a change that decreases the length, which in this case there is, as the class is removed. I've made it a habit to always iterate nodeLists in reverse with a `for` loop, that way it always work, regardless if you have "breaking" changes to the nodeList or not. – adeneo Nov 24 '16 at 19:20
2

That's because the array-like structure returned by getElementsByClassName is "live", that is, if it doesn't satisfy having that specified class, it gets removed from the array automatically.

The first run was caused by the first item. It gets a red class and removes the default class. The removal of default causes it to get removed from list automatically. Once your loop advances, index is 1 but now there's only 1 item in the list.

Joseph
  • 117,725
  • 30
  • 181
  • 234
1

Try adding another class name to the squares that wont be removed, just to help identify them:

<div class="square default">
</div>
<div class="square default">
</div>

And use 'square' as the class selector:

var list = document.getElementsByClassName("square");
console.log(list.length);
for(var i = 0; i < list.length; i++)
{
  console.log(i);
  list[i].classList.add("red");
  list[i].classList.remove("default");
}

Here is a jsfiddle to demonstrate: https://jsfiddle.net/nafxLoLz/1/

Blue Boy
  • 620
  • 1
  • 6
  • 13