0

After creating an array with document.getElementsByClassName(), I want to change the className property of each element. Individual elements can be accessed properly by using literal subscripts. However, when using a for loop, such as:

for (i = 0; i < myElements.length; i += 1) { ... } 

I'm getting only alternate elements, which seems to defy what the for loop is doing. And after the for executes the processed elements are no longer in the array, which is also unintended.

I have loaded a test page at http://www.mwpnz.com/classes/page.htm (This include extensive debug code to see what is happening) Thank you.

clearlight
  • 12,255
  • 11
  • 57
  • 75
Peter F
  • 11
  • 2
  • In the future, particularly or an example that small, show us the full code inside the answer, and particularly the contents of the for-loop!! Had you done that a much broader spectrum of people would have seen the problem and been able to help you. Your debugging example was odd, and didn't really just show us the most essential thing we need to know. – clearlight Jan 13 '17 at 17:45

4 Answers4

2

It sounds like the changes you're making are removing the class that you searched for.

getElementsByClassName doesn't return an array, it returns an HTMLCollection. And the collection is a live NodeList, which means that it automatically tracks changes in the DOM -- if you add/remove an element that matches the class, or add/remove the class name, the collection updates itself.

What this means is that if you remove the class from one of the elements while you're looping, it will be removed from the collection, and the indexes of all the following elements will shift down. Then when you increment the index, you'll skip over the element that shifted into the place that you just operated on.

The simple solution is to convert the collection to an array first. In ES6 you can use Array.from().

var elementArray = Array.from(nodeList);

For older browsers, use

var elementArray = [].slice.call(nodeList);

See Fastest way to convert JavaScript NodeList to Array?

Community
  • 1
  • 1
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

.getElementsByClassName returns a live nodeList, i.e. if you select the elements that have A className and then remove the A className from one of the elements then the .length property of the collection is changed.

Check this example:

enter image description here

You can convert the nodeList to a regular array by using the following code:

var nodeArr = Array.prototype.slice.call( myElements );
Siah
  • 341
  • 2
  • 5
  • I hadn't realised that the selection expression of the nodelist was retained and re-evaluated, so great to learn that. A while (nodelist.length > 0) change element[0] avoids creating the array, although probably isn't as efficient. – Peter F Jan 13 '17 at 18:53
1

save a copy of myElements, and iterate over the new array. Such as:

myElements = document.getElementsByClassName("v1");
newArray = Array.from(myElements);
Nhi Tran
  • 11
  • 3
0

With the assistance of the above information, this is now working fine, using

myElements = document.getElementsByClassName("v1");
while (myElements.length > 0) {
  // this will remove  myElements[0]  from the myElements nodelist
  myElements[0].className = myElements[0].className.replace("v1","vh1");
}
Peter F
  • 11
  • 2