Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getElementsByClassName vs querySelectorAll

Tags:

javascript

if I use

var temp = document.querySelectorAll(".class");
for (var i=0, max=temp.length; i<max; i++) { 
 temp[i].className = "new_class";
}

everything works fine. All nodes change their classes. But, with gEBCN:

var temp = document.getElementsByClassName("class");
for (var i=0, max=temp.length; i<max; i++) { 
 temp[i].className = "new_class";
}  

I get error. Code jumps out of the loop at some point, not finishing the job with msg "can't set className of null".
I understand that this is static vs live nodelist problem (I think), but since gEBCN is much faster and I need to traverse through huge list of nodes (tree), I would really like to use getElementsByClassName.
Is there anything I can do to stick with gEBCN and not being forced to use querySelectorAll?

like image 220
Wolf War Avatar asked Sep 25 '14 20:09

Wolf War


People also ask

Is getElementsByClassName faster than querySelectorAll?

querySelectorAll is much faster than getElementsByTagName and getElementsByClassName when accessing a member of the collection because of the differences in how live and non-live collections are stored. But again, getElementsByTagName and getElementsByClassName are not slow.

What is the difference between querySelectorAll and Getelementsby *?

The difference is in versatility and browser support. @Salman_A querySelectorAll is new and hence supported by new browsers. But getElementsByTagName is old and hence has great support.

Should I use querySelectorAll?

You should opt to use the querySelector method if you need to select elements using more complex rules that are easily represented using a CSS selector. If you want to select an element by its ID, using getElementById is a good choice.

What does querySelectorAll mean?

Definition and Usage The querySelectorAll() method returns all elements that matches a CSS selector(s). The querySelectorAll() method returns a NodeList. The querySelectorAll() method throws a SYNTAX_ERR exception if the selector(s) is invalid.


2 Answers

That's because HTMLCollection returned by getElementsByClassName is live.

That means that if you add "class" to some element's classList, it will magically appear in temp.

The oposite is also true: if you remove the "class" class of an element inside temp, it will no longer be there.

Therefore, changing the classes reindexes the collection and changes its length. So the problem is that you iterate it catching its length beforehand, and without taking into account the changes of the indices.

To avoid this problem, you can:

  • Use a non live collection. For example,

    var temp = document.querySelectorAll(".class");
    
  • Convert the live HTMLCollection to an array. For example, with one of these

    temp = [].slice.call(temp);
    temp = Array.from(temp); // EcmaScript 6
    
  • Iterate backwards. For example, see @Quentin's answer.

  • Take into account the changes of the indices. For example,

    for (var i=0; i<temp.length; ++i) { 
     temp[i].className = "new_class";
     --i; // Subtract 1 each time you remove an element from the collection
    }
    
    while(temp.length) { 
     temp[0].className = "new_class";
    }
    
like image 71
Oriol Avatar answered Oct 13 '22 23:10

Oriol


Loop over the list backwards, then elements will vanish from the end (where you aren't looking any more).

for (var i = temp.length - 1; i >= 0; i--) { 
  temp[i].className = "new_class";
}  

Note, however, that IE 8 supports querySelectorAll but not getElementsByClassName, so you might want to prefer querySelectorAll for better browser support.


Alternatively, don't remove the existing class:

for (var i=0, max=temp.length; i<max; i++) {  
  temp[i].className += " new_class";
}  
like image 42
Quentin Avatar answered Oct 13 '22 21:10

Quentin