[javascript] querySelector and querySelectorAll vs getElementsByClassName and getElementById in JavaScript

I would like to know what exactly is the difference between querySelector and querySelectorAll against getElementsByClassName and getElementById?

From this link I could gather that with querySelector I can write document.querySelector(".myclass") to get elements with class myclass and document.querySelector("#myid") to get element with ID myid. But I can already do that getElementsByClassName and getElementById. Which one should be preferred?

Also I work in XPages where the ID is dynamically generated with colon and looks like this view:_id1:inputText1. So when I write document.querySelector("#view:_id1:inputText1") it doesn't work. But writing document.getElementById("view:_id1:inputText1") works. Any ideas why?

This question is related to javascript

The answer is


querySelector is of w3c Selector API

getElementBy is of w3c DOM API

IMO the most notable difference is that the return type of querySelectorAll is a static node list and for getElementsBy it's a live node list. Therefore the looping in demo 2 never ends because lis is live and updates itself during each iteration.

// Demo 1 correct
var ul = document.querySelectorAll('ul')[0],
    lis = ul.querySelectorAll("li");
for(var i = 0; i < lis.length ; i++){
    ul.appendChild(document.createElement("li"));
}

// Demo 2 wrong
var ul = document.getElementsByTagName('ul')[0], 
    lis = ul.getElementsByTagName("li"); 
for(var i = 0; i < lis.length ; i++){
    ul.appendChild(document.createElement("li")); 
}

querySelector and querySelectorAll are a relatively new APIs, whereas getElementById and getElementsByClassName have been with us for a lot longer. That means that what you use will mostly depend on which browsers you need to support.

As for the :, it has a special meaning so you have to escape it if you have to use it as a part of a ID/class name.


For this answer, I refer to querySelector and querySelectorAll as querySelector* and to getElementById, getElementsByClassName, getElementsByTagName, and getElementsByName as getElement*.

Main Differences

  1. querySelector* is more flexible, as you can pass it any CSS3 selector, not just simple ones for id, tag, or class.
  2. The performance of querySelector changes with the size of the DOM that it is invoked on.* To be precise, querySelector* calls run in O(n) time and getElement* calls run in O(1) time, where n is the total number of all children of the element or document it is invoked on. This fact seems to be the least well-known, so I am bolding it.
  3. getElement* calls return direct references to the DOM, whereas querySelector* internally makes copies of the selected elements before returning references to them. These are referred to as "live" and "static" elements respectively. This is NOT strictly related to the types that they return. There is no way I know of to tell if an element is live or static programmatically, as it depends on whether the element was copied at some point, and is not an intrinsic property of the data. Changes to live elements apply immediately - changing a live element changes it directly in the DOM, and therefore the very next line of JS can see that change, and it propagates to any other live elements referencing that element immediately. Changes to static elements are only written back to the DOM after the current script is done executing. These extra copy and write steps have some small, and generally negligible, effect on performance.
  4. The return types of these calls vary. querySelector and getElementById both return a single element. querySelectorAll and getElementsByName both return NodeLists, being newer functions that were added after HTMLCollection went out of fashion. The older getElementsByClassName and getElementsByTagName both return HTMLCollections. Again, this is essentially irrelevant to whether the elements are live or static.

These concepts are summarized in the following table.

Function               | Live? | Type           | Time Complexity
querySelector          |   N   | Element        |  O(n)
querySelectorAll       |   N   | NodeList       |  O(n)
getElementById         |   Y   | Element        |  O(1)
getElementsByClassName |   Y   | HTMLCollection |  O(1)
getElementsByTagName   |   Y   | HTMLCollection |  O(1)
getElementsByName      |   Y   | NodeList       |  O(1)

Details, Tips, and Examples

  • HTMLCollections are not as array-like as NodeLists and do not support .forEach(). I find the spread operator useful to work around this:

    [...document.getElementsByClassName("someClass")].forEach()

  • Every element, and the global document, have access to all of these functions except for getElementById and getElementsByName, which are only implemented on document.

  • Chaining getElement* calls instead of using querySelector* will improve performance, especially on very large DOMs. Even on small DOMs and/or with very long chains, it is generally faster. However, unless you know you need the performance, the readability of querySelector* should be preferred. querySelectorAll is often harder to rewrite, because you must select elements from the NodeList or HTMLCollection at every step. For example, the following code does not work:

    document.getElementsByClassName("someClass").getElementsByTagName("div")

    because you can only use getElements* on single elements, not collections. For example:

    document.querySelector("#someId .someClass div")

    could be written as:

    document.getElementById("someId").getElementsByClassName("someClass")[0].getElementsByTagName("div")[0]

    Note the use of [0] to get just the first element of the collection at each step that returns a collection, resulting in one element at the end just like with querySelector.

  • Since all elements have access to both querySelector* and getElement* calls, you can make chains using both calls, which can be useful if you want some performance gain, but cannot avoid a querySelector that can not be written in terms of the getElement* calls.

  • Though it is generally easy to tell if a selector can be written using only getElement* calls, there is one case that may not be obvious:

    document.querySelectorAll(".class1.class2")

    can be rewritten as

    document.getElementsByClassName("class1 class2")

  • Using getElement* on a static element fetched with querySelector* will result in an element that is live with respect to the static subset of the DOM copied by querySelector, but not live with respect to the full document DOM... this is where the simple live/static interpretation of elements begins to fall apart. You should probably avoid situations where you have to worry about this, but if you do, remember that querySelector* calls copy elements they find before returning references to them, but getElement* calls fetch direct references without copying.

  • Neither API specifies which element should be selected first if there are multiple matches.

  • Because querySelector* iterates through the DOM until it finds a match (see Main Difference #2), the above also implies that you cannot rely on the position of an element you are looking for in the DOM to guarantee that it is found quickly - the browser may iterate through the DOM backwards, forwards, depth first, breadth first, or otherwise. getElement* will still find elements in roughly the same amount of time regardless of their placement.


collecting from Mozilla Documentation:

The NodeSelector interface This specification adds two new methods to any objects implementing the Document, DocumentFragment, or Element interfaces:

querySelector

Returns the first matching Element node within the node's subtree. If no matching node is found, null is returned.

querySelectorAll

Returns a NodeList containing all matching Element nodes within the node's subtree, or an empty NodeList if no matches are found.

and

Note: The NodeList returned by querySelectorAll() is not live, which means that changes in the DOM are not reflected in the collection. This is different from other DOM querying methods that return live node lists.


querySelector can be a complete CSS(3)-Selector with IDs and Classes and Pseudo-Classes together like this:

'#id.class:pseudo'

// or

'tag #id .class .class.class'

with getElementsByClassName you can just define a class

'class'

with getElementById you can just define an id

'id'

I came to this page purely to find out the better method to use in terms of performance - i.e. which is faster:

querySelector / querySelectorAll or getElementsByClassName

and I found this: https://jsperf.com/getelementsbyclassname-vs-queryselectorall/18

It runs a test on the 2 x examples above, plus it chucks in a test for jQuery's equivalent selector as well. my test results were as follows:

getElementsByClassName = 1,138,018 operations / sec - <<< clear winner
querySelectorAll = 39,033 operations / sec
jquery select = 381,648 operations / sec

About the differences, there is an important one in the results between querySelectorAll and getElementsByClassName: the return value is different. querySelectorAll will return a static collection, while getElementsByClassName returns a live collection. This could lead to confusion if you store the results in a variable for later use:

  • A variable generated with querySelectorAll will contain the elements that fulfilled the selector at the moment the method was called.
  • A variable generated with getElementsByClassName will contain the elements that fulfilled the selector when it is used (that may be different from the moment the method was called).

For example, notice how even if you haven't reassigned the variables aux1 and aux2, they contain different values after updating the classes:

_x000D_
_x000D_
// storing all the elements with class "blue" using the two methods_x000D_
var aux1 = document.querySelectorAll(".blue");_x000D_
var aux2 = document.getElementsByClassName("blue");_x000D_
_x000D_
// write the number of elements in each array (values match)_x000D_
console.log("Number of elements with querySelectorAll = " + aux1.length);_x000D_
console.log("Number of elements with getElementsByClassName = " + aux2.length);_x000D_
_x000D_
// change one element's class to "blue"_x000D_
document.getElementById("div1").className = "blue";_x000D_
_x000D_
// write the number of elements in each array (values differ)_x000D_
console.log("Number of elements with querySelectorAll = " + aux1.length);_x000D_
console.log("Number of elements with getElementsByClassName = " + aux2.length);
_x000D_
.red { color:red; }_x000D_
.green { color:green; }_x000D_
.blue { color:blue; }
_x000D_
<div id="div0" class="blue">Blue</div>_x000D_
<div id="div1" class="red">Red</div>_x000D_
<div id="div2" class="green">Green</div>
_x000D_
_x000D_
_x000D_


look at this

https://codepen.io/bagdaulet/pen/bzdKjL

getElementById fastest than querySelector on 25%

jquery is slowest

var q = time_my_script(function() {

    for (i = 0; i < 1000000; i++) {
         var w = document.querySelector('#ll');
    }

});

console.log('querySelector: '+q+'ms');

The main difference between querySelector and getlementbyID(Claassname,Tagname etc) is if there is more than one elements which satifies the condition querySelector will return only one output whereas getElementBy* will return all the elements.

Lets consider an example to make it more clear.

 <nav id="primary" class="menu">
                            <a class="link" href="#">For Business</a>
                            <a class="link" href="#">Become an Instructor</a>
                            <a class="link" href="#">Mobile Applications</a>
                            <a class="link" href="#">Support</a>
                            <a class="link" href="#">Help</a>
   </nav> 

Below code will explain the difference

**QUERY SELECTOR**
document.querySelector('.link'); // Output : For Business (element)

document.querySelectorAll('.link'); //Out All the element with class link

**GET ELEMENT**
document.getElementsByClassName('link') // Output : will return all the element with a class "link" but whereas in query selector it will return only one element which encounters first.

Inshort if we want to select single element go for queryslector or if we want multiple element go for getElement


Difference between "querySelector" and "querySelectorAll"

_x000D_
_x000D_
//querySelector returns single element_x000D_
let listsingle = document.querySelector('li');_x000D_
console.log(listsingle);_x000D_
_x000D_
_x000D_
//querySelectorAll returns lit/array of elements_x000D_
let list = document.querySelectorAll('li');_x000D_
console.log(list);_x000D_
_x000D_
_x000D_
//Note : output will be visible in Console
_x000D_
<ul>_x000D_
<li class="test">ffff</li>_x000D_
<li class="test">vvvv</li>_x000D_
<li>dddd</li>_x000D_
<li class="test">ddff</li>_x000D_
</ul>
_x000D_
_x000D_
_x000D_