12

I was reading about dom manipulation topic in dev.mozilla and I came across with a statement that suggest us to use querySelector for element selection:

Note that, as with many things in JavaScript, there are many ways to select an element and store a reference to it in a variable. Document.querySelector() is the recommended modern approach...

However, when I made a test I realized getElementsByClassName() is by far the best method for element selection. How come it can be faster than getElementById() ? And why mozilla dev guide still recomends using querySelector() even though it's the slowest one amongs them ?

brainoverflow98
  • 1,138
  • 11
  • 28
  • 8
    Faster than `getElementById` oO? You did something wrong. `getElementById` is just a table lookup and is the definitive winner. However, why do you bother? Do you really have a bottleneck at DOM selection? – Kaiido Jul 23 '19 at 08:02
  • 1
    Note `getElementsByClassName` returns a live node list, whereas `querySelector(All)` and `getElementById` don't. – Andy Jul 23 '19 at 08:05
  • `getElementsByClassName` will return the *same Javascript object* (collection) when invoked again with the same argument. Don't know if the jsperf test takes that into account or not – CertainPerformance Jul 23 '19 at 08:06
  • Obviously it was not my own test I just ran it on jsperf.com :) @Kaiido I was thinking the same thing, and I was shocked when I saw the test results. So does getElementsByClassNamw cache the results ? – brainoverflow98 Jul 23 '19 at 08:11
  • Btw no for now I don't have a DOM bottleneck but it's always better to know before you make that mistake and try to change all your selectors afterwards. – brainoverflow98 Jul 23 '19 at 08:20
  • @brainoverflow98 if you get a bottleneck, you should not change your selectors but you simply should store the element in a variable instead of selecting it so often. – Bergi Jul 23 '19 at 09:06
  • @Bergi Thank you but the question is not about the bottleneck that I have rather it was about how these selectors work internally and how it causes them to give unexpected performance results and Document.querySelector() being the preferred way of accessing DOM :) – brainoverflow98 Jul 23 '19 at 09:23
  • 1
    @brainoverflow98 The answer is that it doesn't really matter. `querySelector` is preferred because of its simplicity and because it doesn't return a live collection – Bergi Jul 23 '19 at 12:14
  • 1
    ``getElementById`` is faster (but only noticeable in performance tests) because since very early Internet-Explorer days the Browser creates global variables for all ID values. So as Kaiido says, in the first comment, ``getElementById`` is just a table lookup. All Browser vendors copied this behavior, many moons ago, because once Microsoft owned 90% of the Browser market, and all Browsers had to support Web applications written on top of this (Microsoft) ID behavior. Today, all Browsers but FireFox still create a totally valid 'IDname' global Array of IDs for duplicates IDs. – Danny '365CSI' Engelman Sep 26 '20 at 10:59

1 Answers1

9

The main advantage of document.querySelector is the ease of use with more complex CSS queries.

const loginFields = document.querySelector("form.login input");
if (loginFields.some((field) => field.value.trim() === "") {
    alert("Please fill in all fields");
}

The alternative would be to use document.getElementsByClassName and Element.getElementsByTagName.

const loginFields = document.getElementsByClassName(".login")[0]
    .getElementsByTagName("input");
if (loginFields.some((field) => field.value.trim() === "") {
    alert("Please fill in all fields");
}

As you may have noticed, you'll often store the return result into a constant/variable. You're (usually) not supposed to call this in a for/while loop.


Regarding the (archived) performance test, it looks quite unfair because all tests except the class test will run two queries.

JSPerf test


So I decided to do some more testing. In the HTML of that page, I added the id "codeOne" to the first element with the class "code".

HTML code

Then I ran four tests.

JavaScript test results

Out of these test results, I can conclude that getElementById using id (691ms) is the clear winner with querySelector using id (749ms) coming in second. This is because ids are easy to look up as browsers copied this feature from Internet Explorer, which was the most dominant browser in the past. You can even use ids as if they are variables.

Javascript test with id variable

Please don't do that because it's less readable. It's also not faster.

When we take a look at the tests using class, we can see a massive performance difference between getElementsByClassName (778ms) and querySelector (1460ms). This is because the former will only look for classNames while the latter will do some extra checks because it can use a complex query like '#id.class[attr="value"]' or "form > input".


The final conclusion is that you can use document.querySelector for one element and document.querySelectorAll for multiple elements just for simplicity as any CSS query will work. The functions getElementById and getElementsByClassName (as well as getElementsByTagName, not included) should always be faster in theory but in practice this shouldn't affect the performance of your website. You (probably) won't call these functions in a loop so you can just store the result in a variable.

User
  • 317
  • 2
  • 8