146
91
chrismorgan
Related: one of my favourite code golfing tricks is named access on the Window object <https://html.spec.whatwg.org/multipage/window-object.html#na...>:

  <div id=result></div>
  <script>
      document.getElementById("result").textContent = "Why do it this way—";
      document.querySelector("result").textContent = "—or even this way—";
      result.textContent = "—when you can do it this way?";
  </script>
Edit: adding another similar test to this page, window[`test${i}`] is taking roughly twice as long as document.querySelector(`#test${i}`) in Firefox, but only half as long in Chromium—which is still a bit slower than document.getElementById(`test${i}`) in Chromium, and than window[`test${i}`] in Firefox.
simlevesque
Edit: seems like I'm wrong.

They are both completely different and almost no one mentions how they differ in these comparison blog articles.

querySelector return a static node while getElementById returns a live node. If the element returned by getElementById is deleted in the DOM, the variable becomes unavailable while for querySelector you get a snapshot of the node that lives on.

If you use both of them the same way or don't know the difference, you are gonna have a bad time.

https://developer.mozilla.org/en-US/docs/Web/API/Document_ob...

rudian
Please don't listen to this, it's misleading.

querySelector *does not* take 62ms to run. Both of them take 0.01ms at most, try it yourself. This is the sort of micro optimization you should not concern yourself with.

How often do you need to select unique elements by ID? Don't use IDs in the first place.

This is akin to using `i--` in loops to "speed up your code" — we're past that.

akersten
> ignores the first 5 results (to avoid caching effects).

This would be the correct approach if you're interested in the "sterile laboratory" performance of these APIs. But the average webpage is going to not be doing a bunch of throwaway work before it starts selecting elements.

I think it would actually be much more interesting to see the cold start results to see if they're comparable to each other. Hypothetically if e.g. GetElementById is only faster after the result has been cached by this simulation, then I think any conclusions about real world impact here could be misleading.

greggman3
I'm getting getElementById is 2x to 4x faster than querySelector depending on the browser

https://jsbenchit.org/?src=25e097f939f76b559b2515430fb5e459

I'm a little surprised. Sure i'd expected getElementById to be faster but honestly I'd have expected browser implementation of querySelector to do a relatively trivial up front check, is the selector a simple id, if so, call getElementById. I suppose that adds overheads to all queries, but that's true of many types of "best case" optimizations. (in the best case it's faster but adds some overhead for any non-best case)

Still, I don't care about this level of optimization. I'll just contuinue to use querySelector everywhere because it's more flexable. No code I've ever written looks up so many elements in a single interaction that this micro optimization would ever matter to me.

chrismorgan
I’m surprised by the apparent magnitude of the difference between Firefox and Chrome. On my laptop I’m getting results roughly twice as fast as reported in the article, but still fairly similar ratios all round:

Firefox 96 (Nightly): document.getElementById 2–4ms avg 3ms, document.querySelector 25–27ms avg 27ms.

Chromium 96 (stable): document.getElementById 11–37ms avg 19ms, document.querySelector 86–155ms avg 101ms.

I’m also a touch surprised by the difference between getElementById and querySelector, because I vaguely recall querySelector being optimised in browsers for the ID case some years back so that there was negligible difference.

(P.S. seeing Firefox’s version number continuing to creep up on Chromium’s, soon to overtake, I wish browsers would scrap their version numbering systems and switch to YYYY.MM instead, or even YYMM like Windows if they want just one number. Can’t even claim user-agent sniffing hazards any more since they’re slightly killing those off and reaching three digits is going to cause some trouble anyway.)

libria
In the worst case

> around 44ms and 206ms

so around 162ms difference per 100,000 elements. This doesn't concern most of us for anything less than 1,000 elements (1.62ms).

I use querySelector more often simply for aesthetics (consistent with other calls and qsAll).

esprehn
This is not measuring what the author thinks it's measuring in Chrome. The benchmark iterates through 100,000 sequential IDs, and does so 105 times.

For getElementById:

This is a map lookup every time.

For querySelector:

Chrome caches the parsed selector, but the benchmark doesn't use the same ID twice in any run, so the cache is not effective within a given run. Chrome also has a 256 query limit (per document) on the cache [1] which means that even though the benchmark runs 105 times, each time the browser is parsing 100,000 selectors since the cache would have the last 256 but it always starts at 0. querySelector does have a fast path [2] that calls getElementById which the benchmark hits, but the parsing cost is dominating.

So the benchmark is really measuring selector parsing vs a map lookup. Firefox might have a separate fast path for ID looking selectors that skips the real css parser. It might also have a larger cache.

Chrome's cache should probably be bigger than 256 for modern web apps , but even so that wouldn't help a benchmark that's parsing 100k selectors repeatedly since it doesn't make sense to have a cache that size just for micro benchmarks and real apps don't use 100k unique queries.

[1] https://source.chromium.org/chromium/chromium/src/+/main:thi...

[2] https://source.chromium.org/chromium/chromium/src/+/main:thi...

throwanem
It occurs to me that, during querySelector execution, components of a selector which match only by ID (or maybe by ID at all) could be maybe linearly or sublinearly resolved by calling the native-code implementation of getElementById. Per at least the MDN docs [1] [2], both return an Element, so nothing downstream will see any difference.

If the entire selector is a single ID matcher, execution time for querySelector probably would not be that much longer than for direct calls to getElementById; depending on implementation there might not even be any more stack frames. (Which would be a pain and might not matter, but there are a few ways you could do it if it did.)

In iOS 14.8 on this iPhone 12 mini with about half a battery, the getElementById test took 20ms, and the querySelector test 47. Of course I don't know what the implementation is actually doing, but those times seem awfully close together compared to those the author quotes.

[1] https://developer.mozilla.org/en-US/docs/Web/API/Document/ge...

[2] https://developer.mozilla.org/en-US/docs/Web/API/Document/qu...

tomxor
Interesting, but, if you are handling anywhere near 100,000 elements you should probably be maintaining references rather than querying the DOM each time.
Jamie9912
Why is Chrome so slow with this? Does anyone know
xg15
Was halfway expecting some counterintuitive result like that infamous "JSON.parse() is faster than an actual JSON literal" meme a while ago.

Somewhat relieving the results here follow the common-sense expectation. (I.e. getElementById is faster than querySelector)

spicybright
I love seeing little "science" experiments like these. Definitely interesting, ty article author.
pdenton
Interestingly, getElementById was 62% slower than querySelector on my computer with FF94. I reckon this is a moot issue as neither of these is likely to be a bottleneck for a web application.
JoeyBananas
You shouldn't be using either of these directly in 2021
jfrunyon
The histograms are essentially completely broken for me in Chrome on dark mode. Had to switch to light mode, refresh, and re-run.
emodendroket
I would expect the performance to be worse, given that it does something much more complex. But it’s still great to have it.