Selecting DOM Elements in JavaScript: Full Guide
Learn every way to select DOM elements with JavaScript: getElementById, querySelector, querySelectorAll, and live collections. See which method fits each situation.
To do anything with the DOM, you first need to find the right element. JavaScript gives you five methods to select DOM elements, each designed for a different situation. The right choice depends on whether you are targeting one element or many, whether you know the element's ID, and whether you need a snapshot or a collection that updates itself automatically.
Here is a quick comparison of all five methods:
| Method | Returns | Live? | Select by |
|---|---|---|---|
| getElementById | Single element or null | N/A | ID |
| querySelector | First match or null | No | Any CSS selector |
| querySelectorAll | Static NodeList | No | Any CSS selector |
| getElementsByClassName | Live HTMLCollection | Yes | Class name |
| getElementsByTagName | Live HTMLCollection | Yes | Tag name |
The table gives you the facts at a glance, but choosing the right method also depends on your specific task. The decision flow below covers the most common scenarios:
The flow shows that when you know the ID, getElementById is the answer. When you need the first match for any CSS selector, use querySelector.
When you need every match and a static list is fine, querySelectorAll is the right choice. Only when you specifically need a live collection that stays in sync with the page should you reach for getElementsByClassName or getElementsByTagName.
getElementById: The Fastest Single-Element Lookup
When an element has a unique id attribute, getElementById is the fastest and most direct way to select it. The browser maintains an internal lookup table of all IDs, so this method finds the element almost instantly.
<button id="submit-btn">Submit</button>This button has a unique ID that makes it easy to target. The getElementById method uses the browser's internal ID lookup to find it instantly:
const button = document.getElementById("submit-btn");
console.log(button.textContent);The output is the string "Submit". The method returns the single element with that ID, or null if no match exists.
Since IDs must be unique on a page, you will always get at most one element back. Always check for null before using the result, especially in shared code where you cannot guarantee the element exists.
querySelector: One Element, Any CSS Selector
The querySelector method is the most flexible of the five. It accepts any valid CSS selector and returns the first element that matches. You can select by tag name, class, ID, attribute, pseudo-class, or any combination you would write in a stylesheet.
Here are examples covering the most common patterns:
const heading = document.querySelector("h1");
const intro = document.querySelector(".intro");
const button = document.querySelector("#submit-btn");
const emailInput = document.querySelector('input[type="email"]');
const firstItem = document.querySelector("ul.menu > li:first-child");Each line finds one element using a different kind of CSS selector. If no element matches, querySelector returns null, not an error. This means you should always guard the result before using it.
const modal = document.querySelector("#modal");
if (modal) {
modal.classList.add("visible");
}This guard pattern is essential. Without it, calling a method or accessing a property on null will crash your script with a TypeError. For most everyday DOM work, querySelector handles the vast majority of selection needs on its own.
querySelectorAll: Multiple Elements, Static List
When you need every element that matches a selector, querySelectorAll is the answer. It returns a static NodeList containing all matching elements in document order:
const allParagraphs = document.querySelectorAll("p");
console.log(allParagraphs.length);
const activeItems = document.querySelectorAll(".item.active");
const formFields = document.querySelectorAll("input, select, textarea");A NodeList is array-like: it has a length property, supports index access with square brackets, and has a built-in forEach method for iteration.
It does not have map, filter, or reduce, but you can easily convert it to a real array with Array.from or spread syntax when you need those methods.
const links = document.querySelectorAll("a");
links.forEach((link) => {
console.log(link.href);
});The most important detail about querySelectorAll is that the returned NodeList is static. It is a snapshot taken at the exact moment you called the method.
If you later add a new paragraph or link to the page, the existing NodeList from an earlier querySelectorAll call will not include it. This is usually what you want because it avoids unexpected changes while you are iterating.
getElementsByClassName and getElementsByTagName: Live Collections
These two methods are older but have one unique feature: they return live HTMLCollections that automatically update when the DOM changes.
const cards = document.getElementsByClassName("card");
const listItems = document.getElementsByTagName("li");The live behavior is the key difference from querySelectorAll. Instead of freezing a snapshot, the collection stays connected to the live page and reflects every insertion or removal immediately:
const items = document.getElementsByClassName("item");
console.log(items.length); // Suppose it is 3
const newDiv = document.createElement("div");
newDiv.className = "item";
document.body.appendChild(newDiv);
console.log(items.length); // Now 4After the new element is appended, the collection size updates automatically. This live behavior is the defining feature of these two methods.
One limitation to remember: HTMLCollections do not have forEach. Always convert to an array before iterating:
const cards = document.getElementsByClassName("card");
Array.from(cards).forEach((card) => {
card.style.border = "1px solid blue";
});Live collections are useful when you are adding and removing elements in the same script and want your reference to stay current. For most other cases, querySelectorAll with its static NodeList is simpler and less surprising.
Calling Selectors on Elements, Not Just the Document
Every selection method works on individual elements, not just the document object. This limits the search to that element's children, which is both cleaner and often faster:
<section id="blog">
<article class="post">First</article>
<article class="post">Second</article>
</section>
<article class="post">Third (outside)</article>The third article sits outside the blog section, which means scoped selection should ignore it. Call querySelectorAll on the blog section instead of the document to prove this:
const blogSection = document.querySelector("#blog");
const blogPosts = blogSection.querySelectorAll(".post");
console.log(blogPosts.length);The output is 2. The third article with class "post" sits outside the blog section, so the scoped querySelectorAll does not find it. Scoping like this is especially useful in component-based code and when working with sections of a page that have repeating class names.
Common Mistakes
The most frequent mistake with querySelector and querySelectorAll is forgetting the CSS prefix for classes and IDs. These methods use CSS selector syntax, so a class needs a dot and an ID needs a hash:
// Wrong: looks for a <submit-btn> HTML tag, which likely does not exist
document.querySelector("submit-btn"); // null
// Correct
document.querySelector("#submit-btn");A second common mistake is calling array methods like forEach directly on a live HTMLCollection instead of converting it first. And a third is forgetting to check for null after querySelector before accessing properties. If the element might not be on the page, the guard pattern shown earlier is not optional, it is required.
Where to Go Next
Now that you know all five selection methods, learn the two most important ones in depth: how to use getElementById for ID-based lookups and how to use querySelector and querySelectorAll for CSS-based selection. Once you can select elements, learn how to change text content on the elements you find.
Rune AI
Key Insights
- Use getElementById when you have a unique ID on the element.
- Use querySelector for flexible one-element lookups with CSS selectors.
- Use querySelectorAll when you need every matching element.
- getElementsByClassName and getElementsByTagName return live collections.
- All selectors work on any element, not just the document.
Frequently Asked Questions
Which DOM selector is the fastest?
Does querySelectorAll return a live collection?
Can I use querySelector on an element instead of document?
Conclusion
Selecting DOM elements is the first step in every interactive JavaScript feature. getElementById is the simplest and fastest for unique elements. querySelector is the most flexible and is the go-to for most situations. Knowing all five methods means you can pick the right tool for each task.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.