In JavaScript, the difference between querySelector and querySelectorAll and getElementsByClassName and getElementById
P粉442576165
2023-08-21 21:45:39
<p>I want to know what is the difference between <code>querySelector</code> and <code>querySelectorAll</code> and <code>getElementsByClassName</code> and <code>getElementById</code> What's the difference? </p>
<p>From this link, I learned that using <code>querySelector</code>, I can write <code>document.querySelector(".myclass")</code> to get the file with class name< ;code>myclass</code>, and <code>document.querySelector("#myid")</code> to get the element with ID <code>myid</code>. But I can already achieve this functionality using <code>getElementsByClassName</code> and <code>getElementById</code>. Which one should be preferred? </p>
<p>Also, I'm working in XPages, and the IDs are dynamically generated, contain colons, and look like this <code>view:_id1:inputText1</code>. So when I write <code>document.querySelector("#view:_id1:inputText1")</code>, it doesn't work. But when I write <code>document.getElementById("view:_id1:inputText1")</code>, it works. Any ideas why this is happening? </p>
For this answer, I will refer to
querySelector
andquerySelectorAll
as querySelector* andgetElementById
,getElementsByClassName
,getElementsByTagName
andgetElementsByName
are called getElement*.Much of this information can be verified in the specification, and much of it was derived from various benchmarks I ran while writing. Specification: https://dom.spec.whatwg.org/
Main difference
querySelector
andgetElementById
both return a single element.querySelectorAll
andgetElementsByName
both return NodeList.getElementsByClassName
andgetElementsByTagName
both return HTMLCollection. Both NodeList and HTMLCollection are called collections of elements.These concepts are summarized in the table below.
Details, Tips and Examples
HTMLCollection is not as array-like as NodeList and does not support .forEach(). I found the spread operator useful to get around this problem:
[...document.getElementsByClassName("someClass")].forEach()
All these functions are accessible per element and globally
document
, exceptgetElementById
andgetElementsByName
, which are only available indocument
is implemented on.Chaining getElement* calls instead of querySelector* will improve performance, especially on very large DOMs. Usually faster even on small DOMs and/or very long chains. However, readability of querySelector* should be preferred over readability unless you know you need performance.
querySelectorAll
is usually harder to override because you have to select elements from a NodeList or HTMLCollection at each step. For example, the following code does not work :document.getElementsByClassName("someClass").getElementsByTagName("div")
Because you can only use getElements* on a single element, not a collection, but if you only want one element then:
document.querySelector("#someId .someClass div")
can be written as:
document.getElementById("someId").getElementsByClassName("someClass")[0].getElementsByTagName("div")[0]
Note that
[0]
is used in each step of returning the collection to get the first element of the collection. The final result is only one element, just like usingquerySelector
.Since all elements can be called using querySelector* and getElement*, it is possible to chain operations using both calls, which is useful when you want some performance improvements but can't avoid using getElement* calls that cannot be written in Very useful when querySelector.
Although it is usually easy to tell whether a selector can be written using only getElement* calls, there is one situation where it may not be obvious:
document.querySelectorAll(".class1.class2")
can be rewritten as
document.getElementsByClassName("class1 class2")
Using getElement* on a static element obtained with querySelector* will cause the element to be dynamic relative to the static DOM subset copied by the querySelector, but static relative to the full document DOM... That's simple This is where the dynamic/static element interpretation starts to fall apart. You should try to avoid situations where you need to worry about this, but if it does exist, remember that querySelector* calls copy the elements they find before returning the reference, while getElement* calls get the reference directly without copying.
querySelector* and
getElementById
traverse elements in a preorder, depth-first manner, called "tree order" in the specification. For the other getElement* calls, I can't tell from the spec if they are in the same tree order, butgetElementsByClassName(".someClass")[0]
may not result reliably in every browser.getElementById("#someId")
should be reliable even if you have multiple copies of the same id on your page.I had to look into this issue when I was working on infinite scroll pages and I thought this might be a common situation where performance becomes an issue. We have onScroll event in our code which contains querySelectorAll call. Even if the calls are rate limited, the page will crash if you scroll far enough, at which point there will be too many calls iterating over too many elements for the browser to keep up. The size of the DOM is relevant in this use case, so in code running on an infinite scroll page, getElement* calls are preferred.
Syntax and browser support.
querySelector
is more useful when you want to use more complex selectors.For example, a list of all elements belonging to class foo:
.foo li
: The
characters have special meaning in selectors. You need to escape it. (Selector escape characters also have special meaning in JS strings, so you need to escape for it as well).