Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behavior when iterating over HTMLCollection from getElementsByClassName

I wrote a function to change the class of elements to change their properties. For some reason, only some of the elements have changed. It took me a few hours to find a solution, but it seems odd to me. Perhaps you can explain this to me.

This isn’t working:

function replace(){
  var elements = document.getElementsByClassName('classOne');

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

See the JSFiddle: only every second item is affected; only every second red element changes color to blue.

So I changed the final expression of the for loop to not increment i anymore:

function replace(){
  var elements = document.getElementsByClassName('classOne');

  for (var i = 0; i < elements.length; i) { // Here’s the difference
    elements[i].className = 'classTwo';               
  }
}

This works well! It seems as though push is called and no increment is needed. Is this normal? It is different from the examples I’ve seen.

like image 700
MeNa Avatar asked Mar 22 '13 03:03

MeNa


People also ask

What will you get if you use the getElementsByClassName?

The getElementsByClassName method of Document interface returns an array-like object of all child elements which have all of the given class name(s). When called on the document object, the complete document is searched, including the root node.

What does getElementsByClassName return if not found?

getElementbyId will return an Element object or null if no element with the ID is found. getElementsByClassName will return a live HTMLCollection, possibly of length 0 if no matching elements are found.

What can you do with HTMLCollection?

An HTMLCollection is a collection of HTML elements with methods that allow you to retrieve the elements by their position in the document or by their id or name attribute.


1 Answers

What's going on is an odd side effect. When you reassign className for each element of elements, the element gets removed from the array! (Actually, as @ user2428118 points out, elements is an array-like object, not an array. See this thread for the difference.) This is because it no longer has the classOne class name. When your loop exits (in the second case), the elements array will be empty.

You could write your loop as:

while (elements.length) {
    elements[0].className = 'classTwo'; // removes elements[0] from elements!
}

In your first case, by incrementing i, you are skipping half of the (original) elements that have class classOne.

Excellent question, by the way. Well-researched and clear.

like image 132
Ted Hopp Avatar answered Oct 31 '22 02:10

Ted Hopp