
The querySelectorAll method is a fundamental tool when you need to grab multiple elements from the DOM that match a certain CSS selector. Unlike getElementById or getElementsByClassName, it’s more flexible because it accepts any valid CSS selector, so that you can pinpoint exactly what you want.
Its return value isn’t a live collection but a static NodeList. This distinction matters: if the DOM changes after you call querySelectorAll, the NodeList won’t automatically update. You have to call it again to get the latest set of elements.
Here’s a basic example:
const buttons = document.querySelectorAll('.btn-primary');
buttons.forEach(button => {
console.log(button.textContent);
});
Notice the use of forEach directly on the NodeList. This wasn’t always possible in older browsers, but it’s now standard and convenient. You can also convert the NodeList to an array if you want to use array methods like map or filter.
For instance:
const buttons = Array.from(document.querySelectorAll('.btn-primary'));
const buttonTexts = buttons.map(btn => btn.textContent);
console.log(buttonTexts);
One subtlety is that querySelectorAll respects the scope of the element you call it on. If you call it on document, it searches the entire DOM. But if you call it on a specific element, it only looks inside that subtree. That’s useful for modular code and avoiding unnecessary searches.
For example:
const container = document.querySelector('#container');
const items = container.querySelectorAll('.item');
Here, the search is confined to elements inside #container. This can save time and prevent accidental matches outside the scope you care about.
Keep in mind that selectors can be combined and complex. You can chain classes, IDs, pseudo-classes, attribute selectors, and more. This expressiveness lets you get very specific about which elements you select.
For example:
const activeItems = document.querySelectorAll('ul#list > li.item.active[data-state="enabled"]');
In this selector, you’re getting list items with the classes item and active that are direct children of a ul with ID list, and that also have a specific data attribute. This kind of precision is what makes querySelectorAll so powerful.
However, be cautious with overly complex selectors inside loops or performance-critical code. Browsers optimize these queries, but complexity still has a cost. Sometimes it’s more efficient to filter elements after a simpler query.
Also, remember that querySelectorAll can return an empty NodeList if no elements match, so always check before proceeding if your code depends on those elements existing.
You can even use pseudo-classes to target elements by their state or position, like :nth-child or :not. This makes it easy to do things like select every other element or exclude certain matches directly in the selector.
For example:
const oddRows = document.querySelectorAll('table tr:nth-child(odd)');
const nonDisabledButtons = document.querySelectorAll('button:not(:disabled)');
This ability to mix and match selectors means you can often avoid manual filtering and keep your DOM queries concise and expressive. But when you do use filters, combining them with Array.from lets you write clear, functional-style code.
What’s less obvious is that while querySelectorAll is powerful, it’s not always the fastest method for selecting elements by simple criteria like class or tag name. If you only need elements by class, sometimes getElementsByClassName can be slightly quicker because it returns a live HTMLCollection optimized for that purpose.
Still, the convenience and flexibility of querySelectorAll usually outweigh those micro-optimizations. It’s the go-to when you want readable, maintainable code that can express complex queries without jumping through hoops.
One last thing to note: because querySelectorAll uses CSS selectors, you can dynamically build selectors as strings in your code. This lets you create adaptive queries based on runtime data:
function getItemsByType(type) {
return document.querySelectorAll(.item.type-${type});
}
const fruits = getItemsByType('fruit');
This pattern is common in components or libraries that need to handle variants or states without hardcoding every possible selector.
Understanding these nuances helps you wield querySelectorAll not just as a blunt tool, but as a precise instrument for DOM manipulation, making your code both powerful and elegant.
Now loading...
practical patterns for selecting multiple elements efficiently
When selecting multiple elements, think grouping selectors to reduce the number of queries. Instead of querying separately for each class or tag, combine them into a single selector string. This reduces the overhead of multiple DOM traversals.
const inputsAndButtons = document.querySelectorAll('input[type="text"], button.primary, select');
inputsAndButtons.forEach(el => {
// handle each form control uniformly
el.classList.add('highlight');
});
This pattern is especially useful in forms or UI components where you want to apply the same logic or styling to a variety of elements without repeatedly querying.
Another practical pattern is caching your NodeList or converted array if you plan to reuse the same set of elements multiple times. This avoids redundant DOM queries and keeps your code efficient.
const menuItems = document.querySelectorAll('.menu-item');
// perform some initial setup
menuItems.forEach(item => item.setAttribute('tabindex', '0'));
// later in the code, reuse the same list without querying again
menuItems.forEach(item => {
item.addEventListener('click', () => {
console.log('Clicked:', item.textContent);
});
});
Be mindful when you store references to DOM elements that might be removed or replaced later. Since querySelectorAll returns a static NodeList, your cached references may become stale if the DOM changes drastically.
Using attribute selectors is a powerful way to target elements with specific data or properties without extra classes. This is common in component-driven architectures where data attributes signify state or role.
const toggles = document.querySelectorAll('[data-toggle="collapse"]');
toggles.forEach(toggle => {
toggle.addEventListener('click', () => {
const targetId = toggle.getAttribute('data-target');
const target = document.querySelector(targetId);
target.classList.toggle('collapsed');
});
});
In this example, the selector is simple but expressive, targeting all elements marked to trigger collapsible behavior. The code remains decoupled from specific classes or IDs, improving maintainability.
For scenarios where you want to select elements that do not have a certain class or attribute, use the :not() pseudo-class. This reduces the need for filtering in JavaScript and keeps selections declarative.
const enabledOptions = document.querySelectorAll('option:not([disabled])');
enabledOptions.forEach(opt => {
console.log('Enabled option:', opt.value);
});
When working with large documents, ponder limiting the scope of your queries by selecting a container element first, then querying within it. This confines the search space and can improve performance.
const sidebar = document.querySelector('#sidebar');
const links = sidebar.querySelectorAll('a.nav-link');
links.forEach(link => {
link.addEventListener('mouseover', () => link.classList.add('hover'));
});
If you need to perform complex filtering based on properties that CSS selectors cannot express, fetch a broader set of elements and then use JavaScript array methods to narrow down your targets.
const allItems = Array.from(document.querySelectorAll('.item'));
const visibleItems = allItems.filter(item => {
return item.offsetParent !== null; // visible in the layout
});
console.log('Visible items:', visibleItems.length);
This approach balances the power of CSS selectors with the flexibility of programmatic filtering.
Finally, when working inside frameworks or libraries that reuse DOM nodes, be cautious about when and how often you call querySelectorAll. Excessive querying inside loops or event handlers can degrade performance. Cache results where possible and invalidate caches only when the DOM changes.
Source: https://www.jsfaq.com/how-to-select-multiple-elements-using-queryselectorall-in-javascript/
