JS getElementsByClassName vs querySelector Guide
Compare getElementsByClassName and querySelector to understand live vs static collections, performance, and when each DOM selection method is the right choice.
Both getElementsByClassName and querySelector can find elements by their CSS class. The difference is not in what they find, it is in what they return and how that return value behaves over time.
One gives you a live, self-updating collection. The other gives you a static snapshot.
Here is the core distinction in one comparison:
| getElementsByClassName | querySelector / querySelectorAll | |
|---|---|---|
| Returns | Live HTMLCollection | Static element or NodeList |
| Updates with DOM? | Yes, automatically | No, snapshot at call time |
| Selector type | Class name only | Any CSS selector |
| Iteration | Convert to array first | forEach built in (NodeList) |
| Speed | Faster for class lookup | Parses CSS selector |
A live collection stays connected to the page. Add a new element with the matching class and it appears in the collection without re-querying.
A static result is a one-time snapshot. Add a new element later and your existing variable will not include it.
Live Collection: getElementsByClassName
The defining feature of getElementsByClassName is that it returns a live HTMLCollection. Every time the DOM changes, the collection updates to match the current state of the page.
const cards = document.getElementsByClassName("card");
console.log(cards.length); // Suppose it is 3
const newCard = document.createElement("div");
newCard.className = "card";
document.body.appendChild(newCard);
console.log(cards.length); // Now 4 -- automatically updatedNo second call to getElementsByClassName is needed. The cards variable watched the DOM change and updated itself. This is useful when you are adding and removing elements in the same script and want a single reference that always reflects the current state.
HTMLCollections have one limitation: they do not support forEach directly. Convert to an array before iterating:
const items = document.getElementsByClassName("item");
Array.from(items).forEach((item) => {
item.style.border = "1px solid blue";
});Array.from or spread syntax both create a real array from the HTMLCollection, giving you access to all array methods.
Static Snapshot: querySelector and querySelectorAll
The querySelector family returns static results. The element or NodeList you get back is a snapshot of what existed at the moment you made the call.
const cards = document.querySelectorAll(".card");
console.log(cards.length); // Suppose it is 3
const newCard = document.createElement("div");
newCard.className = "card";
document.body.appendChild(newCard);
console.log(cards.length); // Still 3 -- the snapshot did not updateThe new card element is on the page, visible to the user, but the cards NodeList does not know about it. If you need the updated list, you must call querySelectorAll again.
This static behavior is usually what you want. It means your loop will not suddenly grow while you are iterating, and the results are predictable no matter what other code does to the page afterward.
The Flexibility Advantage
querySelector accepts any CSS selector, not just a class name. This makes it far more flexible when you need more than a simple class match:
// Only active cards
document.querySelectorAll(".card.active");
// Cards inside a specific section
document.querySelectorAll("#featured .card");
// Cards with a data attribute
document.querySelectorAll('.card[data-type="premium"]');getElementsByClassName can only match by class name. For anything more specific, you must use querySelector or querySelectorAll.
When to Use Each
Use getElementsByClassName when you specifically need a live collection that stays in sync with the DOM. The most common case is when you are adding or removing elements dynamically and want a single reference that always reflects the current count.
Use querySelector when you need a clean, predictable snapshot. This covers most situations: reading elements once, applying styles, attaching event listeners, and filtering by anything beyond a simple class match.
For an overview of all five DOM selection methods and how they compare, see the complete guide to selecting DOM elements. If you are new to DOM selection entirely, start with what the DOM is.
Rune AI
Key Insights
- getElementsByClassName returns a live HTMLCollection that updates automatically.
- querySelector and querySelectorAll return static results that do not change.
- getElementsByClassName is slightly faster for class-only lookups.
- querySelector is more flexible, accepting any CSS selector.
- Default to querySelector unless you specifically need a live collection.
Frequently Asked Questions
Which is faster, getElementsByClassName or querySelector?
Can querySelector replace getElementsByClassName entirely?
Why does getElementsByClassName return an HTMLCollection and not a NodeList?
Conclusion
getElementsByClassName and querySelector both select elements by class, but they differ in one fundamental way: live versus static. getElementsByClassName returns a live HTMLCollection that updates with the DOM. querySelector and querySelectorAll return static snapshots. Choose the one that matches your specific need, and default to querySelector when you do not need live updates.
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.