Learning Objectives
- Recognize legacy named access (
document.formsand friends) in older code - Select by id with
getElementById, the long-standing workhorse - Distinguish a live
HTMLCollectionfrom a staticNodeList - Use any CSS selector via
querySelector/querySelectorAll, scoped to any element
The method ladder
Legacy named access
The oldest layer: the document exposes collections like forms and images, indexable by name. It still works, and you should recognize it in code you inherit — but do not build on it. Names collide, and the collections cover only a few element types.
getElementById
The workhorse since DOM Level 1, and still the fastest, most direct call when you have named a thing with an id. One element or null — no collection to unwrap.
getElementsBy* — and the live-collection trap
These return an HTMLCollection, and it is live: it is a view of the tree, not a copy. Add a matching element and the collection you are holding grows; remove elements while looping over the collection and items shift under your index. That behavior has produced decades of off-by-one loop bugs.
querySelector / querySelectorAll
The modern pair takes any CSS selector — the same language you already use in stylesheets. Learn selectors once, use them everywhere: that is the payoff of the whole select-and-effect design. querySelectorAll returns a static NodeList, a snapshot that holds still no matter what happens to the tree afterward. And both methods run on any element, not just document, so you can scope a query to the subtree you care about instead of searching the whole page.
getElementById is still fine — it is direct and fast — but the query pair covers every case with one selector language, and the static list it returns behaves the way you expect a list to behave.Live demo
One shared sample DOM — three forms, three paragraphs, some nested <strong> elements — and every rung of the ladder run against it. Whatever a button selects flashes highlighted for two seconds, so you can see the set each method returns, not just its length. Finish in section 5, the selector playground: type any CSS selector, press Enter, and see what matches — including what happens when the selector is invalid (the call throws, and the demo catches it).
Next Steps
Querying is not the only way to reach a node. In Step 3: Walking the tree, we move by relationship instead of by selector:
- Walk with
children,parentNode, and the sibling properties - See why every relationship comes in a node flavor and an element flavor
- Walk upward with
closest() - Traverse an entire subtree depth-first