querySelector and querySelectorAll vs getElementsByClassName and getElementById in JavaScript
Asked Answered
B

12

266

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?

Blocked answered 17/1, 2013 at 11:0 Comment(2)
querySelector is used for querying a HTML DOM tree which can include html element and its attributes as key elements for querying... you can use regular expression to achieve this.. dojo.query() does the same thingDiatropism
Don't you mean document.querySelectorAll(".myclass")? Using document.querySelector(".myclass") will only return the first element that matches.Leung
D
141

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

The syntax and the browser support.

querySelector is more useful when you want to use more complex selectors.

e.g. All list items descended from an element that is a member of the foo class: .foo li

document.querySelector("#view:_id1:inputText1") it doesn't work. But writing document.getElementById("view:_id1:inputText1") works. Any ideas why?

The : character has special meaning inside a selector. You have to escape it. (The selector escape character has special meaning in a JS string too, so you have to escape that too).

document.querySelector("#view\\:_id1\\:inputText1")
Domenicadomenico answered 17/1, 2013 at 11:1 Comment(3)
It will vary from browser to browser (and from version to version). I'd assume that selector based ones were more expensive (but not in a way that will ever likely to be significant)Domenicadomenico
I support @janaspage's statement. Site is down today also.Cru
And about class selection see also jsperf.com/getelementsbyclassname-vs-queryselectorall/25. Conclusion : one should far more prefer pure javascript than jquery, and the specific functions getElementById and getElementsByClassName. The className selection can be few hundreds times slower without getElementsByClassName.Italicize
J
251

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

A lot of this information can be verified in the specification, a lot of it is from various benchmarks I ran when I wrote it. The spec: https://dom.spec.whatwg.org/

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.
  3. The return types of these calls vary. querySelector and getElementById both return a single element. querySelectorAll and getElementsByName both return NodeLists. The older getElementsByClassName and getElementsByTagName both return HTMLCollections. NodeLists and HTMLCollections are both referred to as collections of elements.
  4. Collections can return "live" or "static" collections respectively. This is NOT reflected in the actual types that they return. getElements* calls return live collections, and querySelectorAll returns a static collection. The way that I understand it, live collections contain references to elements in the DOM, and static collections contain copies of elements. Take a look at @Jan Feldmann's comments below for a different angle as well. I haven't figured out a good way to incorporate it into my answer but it may be a more accurate understanding.

These concepts are summarized in the following table.

Function               | Live? | Type           | Time Complexity
querySelector          |       | Element        |  O(n)
querySelectorAll       |   N   | NodeList       |  O(n)
getElementById         |       | 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, has 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, but if you only wanted one element, then:

    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* call 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.

  • querySelector* and getElementById traverse elements in preorder, depth-first, called "tree order" in the specification. With other getElement* calls it is not clear to me from the specification - they may be the same as tree order, but getElementsByClassName("someClass")[0] may not reliably give the same result in every browser. getElementById("someId") should though, even if you have multiple copies of the same id on your page.

  • I was working on an infinite scroll page when I had to look into this, and I think that is likely to be a common case where performance becomes an issue. Our code had onScroll events with querySelectorAll calls in them. Even if the calls were rate limited, the page would break if you scrolled down far enough, at which point there would be too many calls iterating through too many elements for the browser to keep up. The size of the DOM is relevant in this use case, and so there's a good case for preferring getElement* calls in code that runs on an infinite scroll page.

Jolson answered 22/2, 2019 at 3:26 Comment(16)
By far the most precise answer on this topic. Should be upvoted more.Inequality
very precise should be put in your blog, SashaHereabout
"Changes to static elements are only written back to the DOM after the current script is done executing." - is there a use case that can clarify this? Great answer, btwJeffcott
Very detailed answer and more important, comparison in terms of the complexity. Thanks.Councilor
Nice answer, please provide the sources.Headrail
This should be an official documentation!Georgiageorgian
@Headrail I have added some sources, but a lot of the information is just from benchmarks that would be too long to post all of. If you want to demonstrate any of these points, I would be happy to help.Jolson
I've checked the information against the latest spec and made a couple of corrections - only collections, not elements, are live or static, and there is a traversal order specified for some or all of the APIs. I hope to add some code that demonstrates the live/static distinction soon.Jolson
for...of is perhaps worth benchmarking against the spread operator, since it will work directly on an HTMLCollection rather than requiring a transformation.Stalky
Would anyone be willing to provide some feedback on a thing I've heard somewhere? It was that the JS engine when we call querySelector("#id") dynamically swaps the call with getElementById(); This seems wrong on so many levels but I wonder if there's a chance for that. Tried looking for an answer and this reply is by far the closest to what I am looking for.Whalebone
@ŁukaszFormela That seems to be the case! Testing in Firefox and Chrome, if you run a querySelector a million times, it will be much faster if you are looking for an id than a class. I don't know if that's part of the querySelector specification, or if it's just an optimization that major browsers use.Jolson
I didn't think to test it that way but it's an interesting idea I've been told by my dev-friend. Would love to investigate futher as I belive this information should be pretty accessible. Let me know what you think @SashaKondrashovWhalebone
@ŁukaszFormela I'm not sure what else you want to investigate. If you want to run the benchmark yourself, use console.log(Date.now()); for (let i=0;i<100000;i++) document.querySelector('#anything'); console.log(Date.now()); and you can see it runs nearly instantly, but much slower if you use any other selector (not an id). This directly implies that the call is intercepted and replaced in that specific case.Jolson
Very good post. The part about "live" and "static" collections is a bit misleading, though. "live" AND "static" collections both contain "references" to DOM elements. Say we have a NodeList and an HTMLCollection both containing all elements of class X. If I add a class Y to such an element of class X that is contained in a NodeList and HTMLCollection, than it's updated in both collections. ...Beaconsfield
.... What makes the "live" collections special though, and what you probably mean is, that "live" collections will update the collection itself and what it contains. So if I have a live and a static collection of class X elements, and then add an element of class X to the DOM, then the live collection will contain that new element, the static one will not.Beaconsfield
I would maybe change "Main differences 4." to something like "Both live and static collections contain references to DOM elements. Live collections keep that list of DOM element references updated, though, while the static ones do not"Beaconsfield
D
141

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

The syntax and the browser support.

querySelector is more useful when you want to use more complex selectors.

e.g. All list items descended from an element that is a member of the foo class: .foo li

document.querySelector("#view:_id1:inputText1") it doesn't work. But writing document.getElementById("view:_id1:inputText1") works. Any ideas why?

The : character has special meaning inside a selector. You have to escape it. (The selector escape character has special meaning in a JS string too, so you have to escape that too).

document.querySelector("#view\\:_id1\\:inputText1")
Domenicadomenico answered 17/1, 2013 at 11:1 Comment(3)
It will vary from browser to browser (and from version to version). I'd assume that selector based ones were more expensive (but not in a way that will ever likely to be significant)Domenicadomenico
I support @janaspage's statement. Site is down today also.Cru
And about class selection see also jsperf.com/getelementsbyclassname-vs-queryselectorall/25. Conclusion : one should far more prefer pure javascript than jquery, and the specific functions getElementById and getElementsByClassName. The className selection can be few hundreds times slower without getElementsByClassName.Italicize
L
124

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.

Landlordism answered 5/11, 2014 at 12:49 Comment(2)
+1 for pointing out the live node list distinction. That's an extremely important difference to be aware of depending on how you intend to use the results.Unless
"live" means node added in DOM runtime and can work on that newley added nodeLandlordism
B
104

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:

// storing all the elements with class "blue" using the two methods
var aux1 = document.querySelectorAll(".blue");
var aux2 = document.getElementsByClassName("blue");

// write the number of elements in each array (values match)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);

// change one element's class to "blue"
document.getElementById("div1").className = "blue";

// write the number of elements in each array (values differ)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);
.red { color:red; }
.green { color:green; }
.blue { color:blue; }
<div id="div0" class="blue">Blue</div>
<div id="div1" class="red">Red</div>
<div id="div2" class="green">Green</div>
Brooks answered 29/8, 2016 at 18:58 Comment(4)
Just to mention - All the older DOM apis returning a node list namely document.getElementsByName, document.getElementsByTagNameNS or document.getElementsByTagName will exhibit the same behavior.Cogitate
Some analyses says querySelector takes more time than getElementById, like here dimlucas.com/index.php/2016/09/17/… . What if we take access time into account? Does the live node obtained from getElementById take more time than the static one from querySelector?Noctilucent
@Cogitate I'd mention that these older DOM APIs don't return NodeList objects, they return HTMLCollections.Lietman
@Noctilucent document.getElementById() does not return a live node. It's faster than document.querySelector('#id_here') probably because querySelector will have to parse the CSS selector first.Lietman
F
28

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
Fidler answered 14/5, 2017 at 19:23 Comment(1)
Wow, that's a massive difference, thanks for looking it up. Clearly querySelectorAll needs additional work behind the scenes (including parsing the selector expression, accounting for pseudo elements, etc.), while getElementsByClassName is merely a recursive object traversal.Forearm
G
18

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'
Gap answered 17/1, 2013 at 11:3 Comment(6)
Is :first a CSS selector, now? :first-class, or :first-of-type maybe, but I thought :first was a JavaScript/jQuery/Sizzle addition.Unbiased
@DavidThomas Yes it is, it is part of CSS3. It can be used like this: css-tricks.com/almanac/selectors/f/first-childGap
but :first is, noticeably, not :first-child.Unbiased
"Authors are advised that while the use of pseudo-elements in selectors is permitted, they will not match any elements in the document, and thus would not result in any elements being returned. Therefore, authors are advised to avoid the use of pseudo-elements in selectors that are passed to the methods defined in this specification." w3.org/TR/selectors-api/#grammarPruitt
Also, there's a bug in IE (of course) that causes it to return the root html element instead of an empty element list when selecting pseudo-elements.Pruitt
you missed s in getElementsByClassName.Preclude
P
7

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.

Pageantry answered 17/1, 2013 at 11:3 Comment(3)
This is not necessarily true. For example, querySelectorAll is available in IE8, whereas getElementsByClassName isn't.Worth
querySelectorAll ... basically everything: caniuse.com/#search=querySelectorAllRubella
@Blocked getelementsbyclassname vs querySelectorAll vs jquery select might be helpful.Endocrinology
W
5

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")); 
}
Warlock answered 21/12, 2018 at 6:19 Comment(0)
W
4

Difference between "querySelector" and "querySelectorAll"

//querySelector returns single element
let listsingle = document.querySelector('li');
console.log(listsingle);


//querySelectorAll returns lit/array of elements
let list = document.querySelectorAll('li');
console.log(list);


//Note : output will be visible in Console
<ul>
<li class="test">ffff</li>
<li class="test">vvvv</li>
<li>dddd</li>
<li class="test">ddff</li>
</ul>
Whole answered 1/12, 2018 at 2:34 Comment(0)
E
2

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');
Euphroe answered 24/1, 2019 at 6:32 Comment(0)
T
0

According to my web teacher, querySelector is more versatile, can be applyed to more cases than getElements and above all can be more specific than getElements.

for an Example, while using getElementsByClassName("presentation"), you are restricted to just find an element(s) by class or id name, when you uses the querySelector, you can find an specific or all scpecific html tag(s) that contains a class. Let me give you an example:

let h1 = document.getElementsByClassName("example");
<h1 class="example">Hello</h1>
<p class="example">blablabla</p>

in this case, you'll get a list of all elements that contains the specified class. but supposing that you have a larger code, that makes impossible to work with index, and you just want the h1 tags that contains the example class, it wont be very effective, right?

it is here where querySelectors join in the game

with querySelectors, you can be so much specific and get more detailed results like in this case

let heading = document.querySelectorAll("h1.example");
<h1 class="example">Hello</h1>
    
<p class="example">blablabla</p>

<h1 class="example">World</h1>

<p class="example">blablabla</p>

here, you will get just the h1 tags that contains the example files, instead all of the document tags with that class like using getElements

Tacet answered 8/4 at 19:49 Comment(0)
A
-3

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

Aday answered 3/6, 2019 at 18:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.