Before you can manipulate DOM elements, you need to find them first! JavaScript provides several methods for selecting elements from the DOM, each suited for different scenarios. Modern methods like querySelector() accept CSS selectors, while older methods like getElementById() provide optimized access by ID. Understanding when to use each method is key to efficient DOM manipulation. Let's master element selection!
Why Element Selection Matters
You can't change what you can't find. Element selection is the first step in any DOM manipulation task.
<!DOCTYPE html>
<html>
<body>
<h1 id="title">Welcome</h1>
<p class="text">Paragraph 1</p>
<p class="text">Paragraph 2</p>
<button id="btn">Click Me</button>
<script>
// Step 1: SELECT the element
let title = document.getElementById('title');
let button = document.getElementById('btn');
// Step 2: DO something with it
button.addEventListener('click', function() {
title.textContent = 'Hello World!';
title.style.color = 'blue';
});
</script>
</body>
</html>Selection → Manipulation → Result
- Select: Find the element(s) you want to work with
- Manipulate: Change content, style, or attributes
- Result: User sees the changes on the page
getElementById() - Select by ID
The most common and efficient way to select a single element. IDs must be unique on a page.
Basic Usage
<!DOCTYPE html>
<html>
<body>
<h1 id="title">My Title</h1>
<div id="container">
<p id="description">Some text here</p>
</div>
<script>
// Select by ID
let title = document.getElementById('title');
let container = document.getElementById('container');
let description = document.getElementById('description');
console.log(title); // <h1 id="title">My Title</h1>
console.log(container); // <div id="container">...</div>
console.log(description); // <p id="description">Some text here</p>
// Note: No # symbol needed (just the ID name)
// ✓ Correct: getElementById('title')
// ✗ Wrong: getElementById('#title')
</script>
</body>
</html>Handling Non-Existent Elements
// Element exists
let title = document.getElementById('title');
console.log(title); // <h1> element
// Element doesn't exist
let missing = document.getElementById('doesNotExist');
console.log(missing); // null
// Always check before using!
let element = document.getElementById('myElement');
if (element) {
element.textContent = 'Found!';
console.log('Element exists');
} else {
console.log('Element not found');
}
// Or use optional chaining (modern)
let text = document.getElementById('myElement')?.textContent;
console.log(text); // Value if exists, undefined if notWhen to use getElementById():
- When you know the exact ID
- When selecting a single, unique element
- When performance is important (fastest method)
querySelector() - Select by CSS Selector
The most flexible modern method. Accepts any CSS selector and returns the first matching element.
Basic Usage
<!DOCTYPE html>
<html>
<body>
<h1 id="title">Title</h1>
<p class="text">Paragraph 1</p>
<p class="text highlight">Paragraph 2</p>
<div data-role="admin">Admin Panel</div>
<script>
// Select by ID (use # for ID)
let title = document.querySelector('#title');
console.log(title); // <h1 id="title">Title</h1>
// Select by class (use . for class)
let text = document.querySelector('.text');
console.log(text); // First <p> with class "text"
// Select by tag
let paragraph = document.querySelector('p');
console.log(paragraph); // First <p> element
// Select by attribute
let admin = document.querySelector('[data-role="admin"]');
console.log(admin); // <div data-role="admin">
// Multiple classes
let highlighted = document.querySelector('.text.highlight');
console.log(highlighted); // <p> with both classes
</script>
</body>
</html>Advanced CSS Selectors
// Descendant selector (space)
let innerPara = document.querySelector('div p');
// Selects <p> inside <div>
// Direct child selector (>)
let directChild = document.querySelector('div > p');
// Selects <p> that is direct child of <div>
// Sibling selector (+)
let nextSibling = document.querySelector('h1 + p');
// Selects <p> immediately after <h1>
// Attribute selectors
let input = document.querySelector('input[type="text"]');
let link = document.querySelector('a[href^="https"]');
let image = document.querySelector('img[alt*="logo"]');
// Pseudo-classes
let firstChild = document.querySelector('li:first-child');
let lastChild = document.querySelector('li:last-child');
let nthChild = document.querySelector('li:nth-child(3)');
// Combination
let specific = document.querySelector('div.container > p.text:first-child');
// Very specific selection!querySelector() is more versatile
getElementById()
// Using getElementById
let element = document.getElementById('title');
// Limited: only works with IDs
// Can't use CSS selectors
// Fast but inflexiblequerySelector()
// Using querySelector
let element = document.querySelector('#title');
// Flexible: any CSS selector
let byClass = document.querySelector('.text');
let byTag = document.querySelector('p');
let complex = document.querySelector('div > p.text');
// More powerful!querySelectorAll() - Select Multiple Elements
Returns all elements matching a CSS selector as a NodeList (array-like object).
Basic Usage
<!DOCTYPE html>
<html>
<body>
<p class="text">Paragraph 1</p>
<p class="text">Paragraph 2</p>
<p class="text">Paragraph 3</p>
<div class="text">Div text</div>
<script>
// Select all elements with class "text"
let allText = document.querySelectorAll('.text');
console.log(allText); // NodeList(4) [p.text, p.text, p.text, div.text]
console.log(allText.length); // 4
console.log(allText[0]); // First element
// Check if it's an array
console.log(Array.isArray(allText)); // false (it's a NodeList)
// But you can loop through it
allText.forEach(element => {
console.log(element.textContent);
});
// Or use for...of
for (let element of allText) {
console.log(element);
}
// Select all paragraphs
let allParagraphs = document.querySelectorAll('p');
console.log(allParagraphs.length); // 3
</script>
</body>
</html>Working with NodeLists
let items = document.querySelectorAll('.item');
// Get count
console.log('Found', items.length, 'items');
// Access by index
let first = items[0];
let last = items[items.length - 1];
// Loop with forEach (works on NodeList)
items.forEach((item, index) => {
console.log(`Item ${index}:`, item.textContent);
});
// Loop with for...of
for (let item of items) {
console.log(item);
}
// Traditional for loop
for (let i = 0; i < items.length; i++) {
console.log(items[i]);
}
// Convert to real array (if needed)
let itemsArray = Array.from(items);
// or
let itemsArray2 = [...items];
// Now you can use array methods
let texts = itemsArray.map(item => item.textContent);
console.log(texts);
// Empty NodeList (no matches)
let nothing = document.querySelectorAll('.doesNotExist');
console.log(nothing.length); // 0 (empty, not null)Practical Example
<!DOCTYPE html>
<html>
<body>
<button class="btn">Button 1</button>
<button class="btn">Button 2</button>
<button class="btn">Button 3</button>
<script>
// Select all buttons
let buttons = document.querySelectorAll('.btn');
// Add click listener to each
buttons.forEach((button, index) => {
button.addEventListener('click', function() {
console.log(`Button ${index + 1} clicked!`);
this.style.backgroundColor = 'blue';
this.style.color = 'white';
});
});
// Change all buttons at once
function highlightAll() {
buttons.forEach(button => {
button.classList.add('highlight');
});
}
</script>
</body>
</html>Older Selection Methods
These methods predate querySelector but are still widely used and supported.
getElementsByClassName() - Select by Class
<!DOCTYPE html>
<html>
<body>
<p class="text">Paragraph 1</p>
<p class="text">Paragraph 2</p>
<div class="text">Div text</div>
<script>
// Returns HTMLCollection (array-like)
let textElements = document.getElementsByClassName('text');
console.log(textElements); // HTMLCollection(3)
console.log(textElements.length); // 3
// Access by index
console.log(textElements[0]);
// Loop through (no forEach on HTMLCollection!)
for (let i = 0; i < textElements.length; i++) {
console.log(textElements[i].textContent);
}
// Or convert to array first
let array = Array.from(textElements);
array.forEach(el => {
console.log(el);
});
// Multiple classes (space-separated)
let multi = document.getElementsByClassName('text highlight');
// Finds elements with BOTH classes
</script>
</body>
</html>getElementsByTagName() - Select by Tag
// Select all paragraphs
let paragraphs = document.getElementsByTagName('p');
console.log(paragraphs.length);
// Select all divs
let divs = document.getElementsByTagName('div');
// Select all links
let links = document.getElementsByTagName('a');
// Get all elements
let allElements = document.getElementsByTagName('*');
console.log('Total elements:', allElements.length);
// Returns HTMLCollection
for (let i = 0; i < paragraphs.length; i++) {
console.log(paragraphs[i].textContent);
}getElementsByName() - Select by Name Attribute
<!DOCTYPE html>
<html>
<body>
<input type="radio" name="gender" value="male"> Male
<input type="radio" name="gender" value="female"> Female
<input type="text" name="username">
<script>
// Select by name attribute
let genderInputs = document.getElementsByName('gender');
console.log(genderInputs.length); // 2
// Useful for form inputs
let username = document.getElementsByName('username')[0];
console.log(username);
// Check which radio is selected
genderInputs.forEach(input => {
if (input.checked) {
console.log('Selected:', input.value);
}
});
</script>
</body>
</html>Method Comparison
| Method | Returns | Speed | Use Case |
|---|---|---|---|
getElementById() | Element or null | ⚡⚡⚡ Fastest | Single element by ID |
querySelector() | Element or null | ⚡⚡ Fast | First match, any selector |
querySelectorAll() | NodeList | ⚡⚡ Fast | All matches, any selector |
getElementsByClassName() | HTMLCollection (live) | ⚡⚡ Fast | By class name |
getElementsByTagName() | HTMLCollection (live) | ⚡⚡ Fast | By tag name |
Live vs Static Collections
<!DOCTYPE html>
<html>
<body>
<p class="text">Paragraph 1</p>
<p class="text">Paragraph 2</p>
<script>
// HTMLCollection (LIVE - updates automatically)
let liveCollection = document.getElementsByClassName('text');
console.log('Live count:', liveCollection.length); // 2
// NodeList (STATIC - doesn't update)
let staticList = document.querySelectorAll('.text');
console.log('Static count:', staticList.length); // 2
// Add new element
let newP = document.createElement('p');
newP.className = 'text';
newP.textContent = 'Paragraph 3';
document.body.appendChild(newP);
// Check counts again
console.log('Live count:', liveCollection.length); // 3 (updated!)
console.log('Static count:', staticList.length); // 2 (not updated)
// Static list needs to be re-queried
staticList = document.querySelectorAll('.text');
console.log('Static count:', staticList.length); // 3 (now updated)
</script>
</body>
</html>Best Practices and Recommendations
Modern Recommendation:
- Use
querySelector()for single elements - Use
querySelectorAll()for multiple elements - Use
getElementById()when performance is critical - querySelector methods are more consistent and flexible
// ✓ Good: Modern approach
let title = document.querySelector('#title');
let buttons = document.querySelectorAll('.btn');
// ✓ Also good: When you need speed
let mainContent = document.getElementById('main-content');
// ⚠️ Works but less flexible
let items = document.getElementsByClassName('item');
// ✗ Avoid: mixing styles unnecessarily
let title2 = document.getElementById('title');
let subtitle = document.querySelector('#subtitle');
// Pick one style and be consistent!Error Prevention
// Always check if element exists
let element = document.querySelector('.my-element');
// Method 1: if statement
if (element) {
element.textContent = 'Found!';
} else {
console.log('Element not found');
}
// Method 2: Optional chaining (modern)
element?.classList.add('active');
// Method 3: Short-circuit evaluation
element && element.classList.add('active');
// Store common selections
let header = document.querySelector('header');
let nav = document.querySelector('nav');
let main = document.querySelector('main');
// Use them multiple times
if (header) {
header.style.backgroundColor = 'blue';
header.classList.add('sticky');
}Practical Examples
Example 1: Interactive Menu
<!DOCTYPE html>
<html>
<head>
<style>
.menu-item { padding: 10px; cursor: pointer; }
.menu-item:hover { background: #f0f0f0; }
.active { background: blue; color: white; }
</style>
</head>
<body>
<nav>
<div class="menu-item" data-page="home">Home</div>
<div class="menu-item" data-page="about">About</div>
<div class="menu-item" data-page="contact">Contact</div>
</nav>
<div id="content"></div>
<script>
// Select all menu items
let menuItems = document.querySelectorAll('.menu-item');
let content = document.querySelector('#content');
// Add click handler to each
menuItems.forEach(item => {
item.addEventListener('click', function() {
// Remove active from all
menuItems.forEach(mi => mi.classList.remove('active'));
// Add active to clicked item
this.classList.add('active');
// Update content
let page = this.getAttribute('data-page');
content.textContent = `Showing ${page} page`;
});
});
</script>
</body>
</html>Example 2: Form Validation
<!DOCTYPE html>
<html>
<body>
<form id="myForm">
<input type="text" id="username" placeholder="Username">
<input type="email" id="email" placeholder="Email">
<input type="password" id="password" placeholder="Password">
<button type="submit">Submit</button>
</form>
<div id="errors"></div>
<script>
let form = document.querySelector('#myForm');
let username = document.querySelector('#username');
let email = document.querySelector('#email');
let password = document.querySelector('#password');
let errorsDiv = document.querySelector('#errors');
form.addEventListener('submit', function(e) {
e.preventDefault();
let errors = [];
// Validate username
if (username.value.trim() === '') {
errors.push('Username is required');
}
// Validate email
if (!email.value.includes('@')) {
errors.push('Valid email is required');
}
// Validate password
if (password.value.length < 6) {
errors.push('Password must be at least 6 characters');
}
// Display errors
if (errors.length > 0) {
errorsDiv.innerHTML = errors.map(err => `<p style="color: red;">${err}</p>`).join('');
} else {
errorsDiv.innerHTML = '<p style="color: green;">Form is valid!</p>';
}
});
</script>
</body>
</html>Element Selection Practice
Master DOM element selection methods
console.log() to see your output in the console above.Key Takeaways
getElementById()is fastest for single elements by IDquerySelector()accepts any CSS selector, returns first matchquerySelectorAll()returns all matches as NodeList- Always check if element exists before manipulating (returns null if not found)
- querySelector methods are most flexible and modern
- HTMLCollections are "live" and update automatically
- NodeLists from querySelectorAll are static
- Use
forEach()orfor...ofto loop through selections - Store selections in variables to avoid repeated queries
- No # symbol needed for getElementById(), but needed for querySelector('#id')
What's Next?
You now know how to find and select DOM elements using all the major selection methods! This is the foundation for all DOM manipulation—you can't change what you can't find.
In the next lesson, we'll explore Modifying Elements—learning how to change element content with textContent and innerHTML, modify styles with the style property, and work with classes using classList. This is where your pages become truly dynamic!
💪 Practice Challenge:
Before moving on, try creating:
- A page that selects an element by ID and logs it to console
- Use querySelector() to select elements by class, tag, and attribute
- Select all buttons on a page and log how many there are
- Create a function that safely selects an element (checks if it exists)
- Build a simple navigation system that selects and highlights menu items