It is the oldest way of getting hold of an element, and it has outlived everything else. One identifier, one call, one element.
Its longevity comes from its simplicity: no selector to parse, an internal index kept by the browser, and an intent readable at a glance.
Definition
getElementById takes the value of an id attribute and hands back the element carrying it, or null when no element does. The name goes in on its own, without the hash used by CSS selectors.
const button = document.getElementById("confirm");
const missed = document.getElementById("#confirm");
console.log(button.textContent);
console.log(missed); // null: the hash has no business hereThe method only exists on document. An element does not own it, which makes sense: an identifier is meant to be unique across the whole page, so there is no reason to restrict the search to one branch.
One identifier, one element
HTML requires that the same id appear only once per document. When the rule is broken, nothing reports the mistake: the method hands back the first element met in document order, and the following ones become unreachable.
- A class repeats freely, that is its job. An identifier names a single copy.
- Case matters:
cartandCartare two distinct identifiers. - Spaces are forbidden inside an
idvalue, unlike inside aclassattribute.
The global variable trap
Browsers expose every element carrying an identifier as a property of window. The following code therefore works, even though no variable was ever declared.
// <div id="cart"></div>
console.log(cart); // the element, with no search at all
const safeCart = document.getElementById("cart");That convenience is a trap. A let declaration carrying the same name, an identifier renamed in the HTML, and the code breaks with no explanation. Always go through an explicit search stored in a const.
Frequently asked questions
How does it differ from querySelector("#cart")?
The result is identical as long as the identifier is unique. querySelector() has to parse a selector before searching, whereas getElementById queries an index directly. The speed gap is negligible on a single call, and the choice mostly comes down to keeping one file consistent.
What about an identifier with an unusual character?
That is exactly where this method takes the lead again. An identifier starting with a digit or containing a dot breaks a CSS selector, which then demands escaping through CSS.escape. getElementById compares strings and never worries about it.
Why does it hand back null when the element is visible?
Almost always because the script runs before the browser has parsed that part of the HTML. Adding defer to the script tag settles the most frequent case. After that, check the exact spelling of the identifier, casing included.