DOM Manipulation
Selecting Elements
Master querying elements in the DOM. Compare getElementById, querySelector, and querySelectorAll in terms of performance and return values.
1. Introduction
To interact with HTML elements using JavaScript, you must first select them from the DOM tree. The browser provides several APIs for querying nodes, ranging from legacy ID selectors to modern CSS selector matching engines.
2. Why It Matters
Choosing the correct selection method affects both code readability and performance. Knowing the differences between static and live collections prevents bugs where elements appear to be missing or updated unexpectedly.
3. Real-World Analogy
Think of finding Folders in a Filing Cabinet:
- getElementById: Searching for a folder using a unique barcode ID. You retrieve the folder instantly without needing to scan the rest of the cabinet (highest performance lookup).
- querySelector: Asking for the "first red folder with an invoice label" (CSS selector). The clerk scans folders until they locate the first match, and stops.
- querySelectorAll: Asking for "all folders containing unpaid invoices." The clerk scans the entire cabinet, pulls out all matching folders, and returns a static stack (NodeList).
4. Querying APIs
Let's explore the common element selection methods:
1. getElementById:
Queries a single element using its unique id attribute. It is the fastest selection method because the browser keeps a hash index of element IDs.
2. querySelector:
Queries the first element that matches a CSS selector. Returns null if no match is found.
3. querySelectorAll:
Queries all elements matching a CSS selector, returning them in a static NodeList.
5. NodeList vs HTMLCollection (Static vs Live)
| Collection Type | Returned By | Updates dynamically when DOM changes? | Supports Array Methods? |
|---|---|---|---|
| HTMLCollection (Live) | getElementsByClassName, getElementsByTagName |
Yes (reflects DOM updates automatically) | No (must convert using Array.from) |
| NodeList (Static) | querySelectorAll |
No (represents a snapshot in time) | Partial (supports only forEach) |
6. Practical Example
This script illustrates the difference between live and static collections when elements are dynamically added:
7. Common Mistakes
- Trying to call map or filter on a NodeList directly: NodeList only implements
forEach. Attempting to call other array methods throws a TypeError. Convert the NodeList to a true array usingArray.fromor the spread operator first. - Forgetting CSS selector syntax in querySelector: Forgetting the period prefix (
.) for class lookups or the hash prefix (#) for ID lookups.
8. Quick Quiz
Q1: Which method returns a live HTMLCollection that updates automatically when the DOM changes?
A) querySelectorAll
B) getElementsByClassName
Answer: B — getElementsByClassName returns a live HTMLCollection, whereas querySelectorAll returns a static NodeList.
9. Scenario-Based Challenge
The Multi-List Filter Utility:
You have a list of cards: querySelectorAll('.card'). You need to filter this list to find only cards containing the class active. Write a clean utility that converts the NodeList to a true array and filters it.
10. Debugging Exercise
Explain why this filter throws a TypeError, and how to fix it:
const cards = document.querySelectorAll('.card');
// Objective: find cards containing the class 'highlight' const highlighted = cards.filter(card => card.classList.contains('highlight')); // crashes!
View Solution
Diagnosis: The querySelectorAll method returns a NodeList, which does not support the filter method on its prototype, throwing a TypeError: cards.filter is not a function.
Fix: Convert the NodeList into a true array before calling filter:
const highlighted = Array.from(cards).filter(card => card.classList.contains('highlight'));
11. Interview Questions
🟢 Q1: Compare getElementById and querySelector in terms of performance and usage.
Answer:
• Performance: getElementById is faster because browsers keep a hash index of element IDs, enabling O(1) constant time lookups. querySelector must parse CSS selectors and traverse the DOM tree, resulting in slower lookups.
• Usage: getElementById only supports selecting elements by their ID. querySelector supports any valid CSS selector, making it more flexible.
12. Production Considerations
- • Cache Selections: DOM queries are relatively slow. Avoid querying elements repeatedly inside high-frequency event handlers (like scroll or resize listeners); query the elements once and store their references in variables instead.