querySelectorAll & forEach0%

querySelectorAll & forEach

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

querySelectorAll & forEach in Modern JavaScript

Selecting and manipulating collections of DOM nodes is a cornerstone of client-side web development. While single-element selectors like getElementById() or querySelector() return either an Element or null, document.querySelectorAll() matches all DOM elements satisfying a standard CSS selector and returns a static NodeList.


1. querySelector vs. querySelectorAll

Understanding the return types and behavioral differences is vital:

FeaturequerySelector()querySelectorAll()getElementsByClassName()
Return TypeSingle Element or nullStatic NodeListLive HTMLCollection
Selector SyntaxAny CSS selectorAny CSS selectorClass name string only
Iteration SupportN/A (Single node)Native .forEach()Requires conversion to Array
DOM Mutation EffectNo effect on variableSnapshot (Static)Live updates with DOM changes
JavaScript
// Selecting single element
const submitBtn = document.querySelector('#btn-submit');
 
// Selecting all matching elements (Static NodeList)
const featureCards = document.querySelectorAll('.card.feature');
console.log(`Found ${featureCards.length} feature cards.`);

2. Iterating with NodeList.prototype.forEach

Modern browsers support .forEach() directly on NodeList instances, enabling clean functional iteration without manual for loops.

JavaScript
const navLinks = document.querySelectorAll('nav a.nav-item');
 
navLinks.forEach((link, index) => {
// Check if link is active
if (link.getAttribute('href') === window.location.pathname) {
link.classList.add('active');
}
// Attach telemetry or custom interaction
link.addEventListener('click', (event) => {
console.log(`User clicked nav item #${index + 1}: ${link.textContent}`);
});
});

3. NodeList vs. HTMLCollection vs. Array

A NodeList is not an Array. While modern NodeList has .forEach(), it lacks array transformation methods like .map(), .filter(), .reduce(), or .some().

Converting NodeList to an Array

To leverage array transformations, convert the NodeList using the spread operator or Array.from():

JavaScript
const priceBadges = document.querySelectorAll('.badge-price');
 
// Option A: Spread operator (Recommended)
const priceElements = [...priceBadges];
 
// Option B: Array.from() with mapping callback
const prices = Array.from(priceBadges, (el) => parseFloat(el.textContent.replace('$', '')));
 
// Now you can filter and reduce!
const affordablePrices = prices.filter(price => price < 50.00);
const totalPrice = prices.reduce((sum, price) => sum + price, 0);
 
console.log('Prices under $50:', affordablePrices);
console.log('Total Cart Value:', totalPrice.toFixed(2));
Output
DOM Tree Selector:
document.querySelectorAll('.item')
│
▼
Static NodeList
┌──────┬──────┬──────┐
│ el 0 │ el 1 │ el 2 │
└──────┴──────┴──────┘
│
[...spread operator]
│
▼
Standard Array
(.map, .filter, .reduce available)

4. Static vs. Live Collections

querySelectorAll() returns a static snapshot. Changes to the DOM after the query will not be reflected in the existing NodeList:

JavaScript
const container = document.querySelector('#container');
const staticList = document.querySelectorAll('.item'); // e.g., 3 items
const liveList = document.getElementsByClassName('item'); // 3 items
 
// Add a new element dynamically
const newItem = document.createElement('div');
newItem.className = 'item';
container.appendChild(newItem);
 
console.log(staticList.length); // Still 3! (Static snapshot)
console.log(liveList.length); // 4! (Live collection updated automatically)
Pro Tip
Static collections are generally faster and safer because iterating over a live collection while mutating the DOM can cause infinite loops or skipped indices.

5. Practical Example: Tab Controller

JavaScript
function initTabs() {
const tabButtons = document.querySelectorAll('[data-tab-target]');
const tabPanes = document.querySelectorAll('.tab-pane');
 
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const targetSelector = button.getAttribute('data-tab-target');
const targetPane = document.querySelector(targetSelector);
 
// Deactivate all buttons & panes
tabButtons.forEach(btn => btn.classList.remove('active'));
tabPanes.forEach(pane => pane.classList.remove('show'));
 
// Activate current
button.classList.add('active');
if (targetPane) {
targetPane.classList.add('show');
}
});
});
}

Interactive Quiz

Practice Quiz

Question 1
What does document.querySelectorAll() return when matching multiple elements?
Question 2
How can you convert a NodeList into a true JavaScript Array to use array methods like .filter()?
Question 3
How does a static NodeList behave when a new matching element is appended to the DOM after querying?
Question 4
Which method is natively available on modern NodeList instances without converting to an Array?
Question 5
What is returned by document.querySelectorAll('.non-existent-class') if no elements match?
Next Lesson

Traversing DOM Nodes

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext