DOM Traversal
Objectives
By the end of this chapter, you should be able to:
- Move from a child element to its parent with
.parentElement - Access an element’s children with
.children - Move sideways between elements with
.nextElementSiblingand.previousElementSibling - Find the nearest matching ancestor with
.closest()
💡 Why this matters: You won’t always have a direct selector for the exact element you need. Traversal lets you start from an element you can select and move to a nearby one you can’t.
Every example in this lesson uses this same HTML:
<div class="card">
<h2>Title</h2>
<p>First</p>
<p>Second</p>
<button class="delete">Delete</button>
</div>
Moving Up: .parentElement
Every element has a .parentElement, the element directly containing it.
const button = document.querySelector(".delete");
console.log(button.parentElement.className);
// card
This is common when handling a click on a small element (like a delete button) and needing to act on the larger element it belongs to (like the whole card).
Moving Down: .children
.children gives you an element’s direct child elements (not grandchildren, and not text nodes, only actual elements).
const card = document.querySelector(".card");
console.log(card.children.length);
// 4
for (const child of card.children) {
console.log(child.tagName);
}
// H2
// P
// P
// BUTTON
.tagName is always uppercase, regardless of how you wrote the tag in your HTML.
Moving Sideways: Siblings
.nextElementSibling and .previousElementSibling move between elements that share the same parent.
const firstP = document.querySelector("p");
console.log(firstP.nextElementSibling.textContent);
// Second
console.log(firstP.previousElementSibling.tagName);
// H2
Starting from the first <p>, its next sibling is the second <p>, and its previous sibling is the <h2> before it. If there’s no matching sibling, either property is null, so check before using the result.
Finding the Nearest Ancestor: .closest()
.closest(selector) starts at an element and walks upward through its ancestors, returning the first one that matches the selector, or null if none do.
const closestCard = button.closest(".card");
console.log(closestCard === card);
// true
This is the more reliable version of .parentElement when you’re not sure exactly how many levels up the element you want is, or when the structure might change. Instead of chaining .parentElement.parentElement, .closest() searches until it finds a match.
Try It
- Given a
<div class="card">containing an<h3>and a<button>, select the button and use.parentElementto log the card’s class name. - Given a
<ul>with three<li>children, select the<ul>and use.childrento log how many there are and each one’s.tagName. - Select the first
<li>in that list and use.nextElementSiblingto log the second one’s text. - Nest a
<button>two levels inside a<div class="panel">. Select the button and use.closest(".panel")to reach the outer div directly.
Recap
.parentElementmoves up one level..childrengives you an element’s direct child elements..nextElementSiblingand.previousElementSiblingmove between elements sharing a parent, and arenullwhen there’s nothing there..closest(selector)walks upward until it finds a matching ancestor, useful when you don’t know exactly how many levels away it is.
Next lesson: reading values out of forms.