9 ms·
GetElementById vs. QuerySelector
- simlevesque 5y agoEdit: 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_object_model/Locating_DOM_elements_using_selectors https://developer.mozilla.org/en-US/docs/Web/API/Document_ob...
- sodapopcan 5y agoWhoa! As someone who likes to think they know a thing or two about vanilla JS, I did not know this. This is pretty dang important.
- sillysaurusx 5y agoWhat an awful design. Why would it differ like that? No doubt some legacy baggage that was later turned into a justification.
- Ozzie_osman 5y agoI'd actually expect the opposite just from the names. I'd expect a selector to select a "live" node, and getElement to get a static one.
- jayflux 5y agoThese APIs didn’t all come out the same time, so they weren’t designed to differ. Instead they decided against doing live node lists in future but couldn’t change how the older methods work as that would break websites. In the world of DOM/JS you can’t really make breaking changes. https://humanwhocodes.com/blog/2010/09/28/why-is-getelementsbytagname-faster-that-queryselectorall/ https://humanwhocodes.com/blog/2010/09/28/why-is-getelements... Has more context
- minitech 5y agoThat’s incorrect. Maybe you’re thinking of querySelectorAll, which returns a static list, compared to getElementsByTagName and getElementsByClassName, which return live ones?
- tentacleuno 5y agoI wonder why we need all these different collections. Makes the DOM feel hacked together by lots of totally different people not communicating (?) There's probably a reason, though.
- joshspankit 5y agoFor reference: https://xkcd.com/927/ https://xkcd.com/927/
- jfrunyon 5y agoThat's because it was hacked together by lots of totally different people not communicating. Over a couple of decades, no less.
- rndgermandude 5y agoExactly. Once upon a time the powers that were figured a live node list would be cool, so they did that. Many moons later, the new powers that were decided that while a live node list looks cool, it contains too much magic pixie dust which may cause allergies in a lot of people (symptoms include frantically screaming at your screen about why this stuff doesn't work like you expect it to work). But they couldn't just change existing stuff or risk breaking the web. But they could avoid it for new stuff, and so they did that.
- LeonB 5y agoThe responses to this are a fantastic example of Cunningham’s Law at work. Thank you!
- rudasn 5y agoYeah, I surprised to even see those two terms on an article on HN (who uses getElementById or querySelectors nowadays right?) but I was even more surprised by the number of comments it gathered (which are of course unrelated to the original article)
- chrismorgan 5y agoThis is nonsense for querySelector and getElementById. They both query the current state of the DOM and return an Element or null. Variables can’t get deleted under you in JavaScript, so if you have a reference to a node that you remove from the DOM, it’s still the same node until you release all references and it gets garbage collected, or you put it back in the DOM. What you’re talking about is only applicable or relevant for the methods that return collections. querySelectorAll returns a static NodeList, getElementsByName/getElementsByTagName/getElementsByClassName return live NodeLists or HTMLCollections.
- anderskaseorg 5y agoYou are misreading the documentation. There's no such distinction as live node vs. static node in that sense. There's only a distinction between a live node list and a static node list. This is a difference between getElementsByClassName("foo") and querySelectorAll(".foo"), but not between getElementById("foo") and querySelector("#foo"). The difference is whether membership changes in the collection are reflected immediately. Changes to the nodes themselves are reflected as usual either way, and node references do not spookily invalidate or repoint themselves.
- snovv_crash 5y agoGlad to hear that Javascript is such a simple language and only experts should use things like C++.
- samastur 5y agoThis is an API issue (DOM), not a language one.
- da_chicken 5y agoDeflecting to semantics or categorization isn't a defense.
- christophilus 5y agoSimilarly, C is crappy because operating system APIs are crappy.
- samastur 5y agoMy point it that it is a defined behaviour of the DOM API which is generally implemented in C++ and exposed to Javascript environment. It behave exactly the same way if you use bindings for any other language. It's not an ECMAScript defined feature. You can also implement exactly this behaviour in any language so how is this then a Javascript language issue?
- 5y ago
- mysecretaccount 5y ago> 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. I'm mostly sure that this is not true: https://imgur.com/a/XaS3b0W https://imgur.com/a/XaS3b0W
- throwanem 5y agoIt makes sense that that would be the case. You still have a reference in scope to the element, so the GC leaves it alone. I could see the described behavior as a bug in maybe an older engine, but so far as I can recall I've never run into it in practice.
- chrismorgan 5y agoI’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.)
- ehsankia 5y agoAs mentioned elsewhere, this is probably not testing what it thinks it is. The benchmark is doing a bunch of weird things, using timers that are not very precise, throwing away the first results, using template literals, etc. The results should be taken with a grain of salt.
- throwanem 5y agoIt 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/getElementById https://developer.mozilla.org/en-US/docs/Web/API/Document/ge... [2] https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector https://developer.mozilla.org/en-US/docs/Web/API/Document/qu...
- libria 5y agoIn 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).
- lifthrasiir 5y agoI would prefer getElementById regardless of its performance because you may need escaping for CSS selectors, for example `getElementById('comment:1234')` vs. `querySelector('#comment\\:1234')`.
- onion2k 5y agoYou could avoid that quite easily by using IDs that don't need escaping though. That said, the fact escaping is necessary could point to part of the reason why querySelector is slower. There's obviously some additional parsing necessary just to work out what the developer is requesting. If you don't need to spend that CPU time then it's certainly better not to.
- qw 5y agoThis is my subjective opinion, but I think getElementById makes the code easier to understand when you scan the code. Even if the query selector is simple, it still requires you to read the query to understand it's just a simple lookup by id
- akersten 5y ago> 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.
- chrismorgan 5y agoUnless you are accessing elements by ID ridiculously often, the time taken will be utterly unnoticeable rounding error (far below one millisecond). But if the difference is enough to skew the benchmarks, then it makes perfect sense to remove them. (In practice, on removing the skipping, I see no significant evidence that it actually makes a difference.)
- Jamie9912 5y agoWhy is Chrome so slow with this? Does anyone know
- Kavelach 5y agoThere is a lot of cruft in Chrome's engine that accumulated over the years. Meanwhile, Firefox had a lot of the engine logic rewritten from scratch, using Rust.
- teaearlgraycold 5y agoGoogle's greed /s
- agys 5y agoIt’s Firefox that has become fast…! This is totally unscientific, but I like to write experimental apps that manipulate the DOM heavily and Firefox is very often the fastest between Chrome and Safari in repainting the DOM (this wasn’t the case ~3 years ago).
- esprehn 5y agoYes, I explained this above: https://news.ycombinator.com/item?id=29350612 https://news.ycombinator.com/item?id=29350612 This is less of an issue of Chrome being slow and more about measuring different things across the two browsers because of how the micro benchmark is structured.
- rudian 5y agoPlease 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.
- lhorie 5y agoOP is explaining their methodology poorly. The 62ms number is the time it takes to run the call 100,000 times, in a loop, with a string interpolation. And measurement is done by taking the delta of two performance.now() calls, which are known to be precise to only about 1ms for spectre mitigation[0]. FWIW, JS old timers have known querySelector is slower than getElementById since querySelector became a thing. [0] https://developer.mozilla.org/en-US/docs/Web/API/Performance/now https://developer.mozilla.org/en-US/docs/Web/API/Performance...
- rudian 5y agoI know, that's why it's misleading and it should not be considered. A quick reader will think that "using getElementById will save me 32ms", but it'd be off by several orders of magnitude.
- lhorie 5y agoThat sounds like a pit of success, though. The gain just isn't that significant. IMHO, misleading would be if querySelector was faster under normal circumstances but shown to be slower through bad benchmarking
- leeoniya 5y agoyep. and please dont query the dom 100k times, or in a loop :)
- afiori 5y agoalso with querySelector you can use queries like > ids.map(id=> `#test${id}`).join(" , ") or > `[id^="test"]` to get all elements that have an id that starts with test
- deleted 5y ago[deleted]
- deleted 5y ago[deleted]
- emodendroket 5y agoI would expect the performance to be worse, given that it does something much more complex. But it’s still great to have it.
- greggman3 5y agoI'm getting getElementById is 2x to 4x faster than querySelector depending on the browser https://jsbenchit.org/?src=25e097f939f76b559b2515430fb5e459 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.
- bobince 5y agoFlexible is a double-edged sword. `querySelector` is bringing in the added complexity of selector syntax, which is more stuff to think about that isn't relevant to what you're trying to do. For example now you have to worry about whether there are any characters in the ID that need escaping in a selector (eg `.`), something that may not be easy to verify when formatting an ID out of variables. So I'd suggest preferring getElementById for its directness, rather than for micro-optimisation reasons. (In principle the same should be true for getElementsByClassName, but the live NodeLists returned by that method are a trap for the unwary, so neither option is ideal.)
- esprehn 5y agoBrowsers do exactly the optimization you're describing: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/selector_query.cc;l=382;drc=fde2db9853cab5681586ea71d877f4cefd5eee13 https://source.chromium.org/chromium/chromium/src/+/main:thi... Both getElementById and querySelector are quite fast, down to the level of measuring individual branches in a micro benchmark. querySelector does have to do a bit more work to lookup the cached query and a handful of extra branches over getElementById, it's not scanning the document for an ID query unless you're in quirks mode though.
- endless1234 5y ago
- xg15 5y agoWas 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)
- tomxor 5y agoInteresting, but, if you are handling anywhere near 100,000 elements you should probably be maintaining references rather than querying the DOM each time.
- jfrunyon 5y agoThe histograms are essentially completely broken for me in Chrome on dark mode. Had to switch to light mode, refresh, and re-run.
- deleted 5y ago[deleted]
- pdenton 5y agoInterestingly, 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.
- esprehn 5y agoThis 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:third_party/blink/renderer/core/css/selector_query.cc;l=487;drc=66f8666257dd5d2687c37c155f40cc72e0176270 https://source.chromium.org/chromium/chromium/src/+/main:thi... [2] https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/css/selector_query.cc;l=382;drc=fde2db9853cab5681586ea71d877f4cefd5eee13 https://source.chromium.org/chromium/chromium/src/+/main:thi...
- JoeyBananas 5y agoYou shouldn't be using either of these directly in 2021
- spicybright 5y agoI love seeing little "science" experiments like these. Definitely interesting, ty article author.
- chrismorgan 5y agoRelated: one of my favourite code golfing tricks is named access on the Window object <https://html.spec.whatwg.org/multipage/window-object.html#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.
- ducharmdev 5y agoWoah, no way. Although I could see this being abused, it's amazing that this even works.
- flomo 5y agoThis was the standard way of DOM touching in the early days, e.g. FormName.FieldName.value = "foo";
- esnard 5y agoIt has been abused to allow remote code execution in LastPass, a password manager. https://bugs.chromium.org/p/project-zero/issues/detail?id=1225 https://bugs.chromium.org/p/project-zero/issues/detail?id=12...
- JimDabell 5y agoYou could also crash Internet Explorer 6 simply by including an element with id="tags" on the page. When the user chose to print the page out, the browser would try to access window.tags, find the element instead of what it was expecting to find, and give up.
- vgallur 5y ago