Frontend Master

JavaScript Curriculum

Traversing the DOM
+50 XP

Traversing the DOM

easy
~25 min·50 XP

A customer clicks the 'Remove' button inside a menu card. The button doesn't know which card it's in. Traversal lets it walk up the tree to find its parent .card and remove the whole thing.

Traversing the DOM

Selecting elements with querySelector every time is slow and fragile. Once you have one element, you can navigate the entire tree from it — up, down, and sideways.

DOM Traversal Navigator

Click any node in the tree, then pick a traversal property to see where it lands:

traversal.js
Click to select a node
Traversal property
Result<div.cards>

Direct parent element

const el = document
  .querySelector('div')
el.parentElement
// → <div.cards>
💡Element vs Node properties
Always use the Element versions of traversal properties — parentElement, children, firstElementChild, nextElementSibling. The Node versions (parentNode, childNodes, firstChild, nextSibling) include text nodes and comments, which makes them harder to work with in practice.

The closest() Method

closest() is one of the most useful traversal tools — it walks UP the tree and finds the nearest ancestor matching a CSS selector:

closest-demo.js

A delete button is deeply nested. Clicking it needs to find its parent .card to remove the whole thing.

HTML structure
<div class="cards">
  <div class="card">
    <h3>Espresso</h3>
    <div class="actions">
      <button class="delete-btn">×</button>
    </div>
  </div>
</div>
JavaScript
deleteBtn.addEventListener('click', (e) => {
  // Without closest — fragile:
  // e.target.parentElement.parentElement.parentElement

  // With closest — robust:
  const card = e.target.closest('.card')
  card.remove()
})
closest() walks UP the DOM tree (towards the root) and returns the first ancestor matching the selector. Returns null if nothing matches. Much better than chaining .parentElement.parentElement.
ℹ️Why closest() beats parentElement chains
`el.parentElement.parentElement.parentElement` breaks the moment you change your HTML structure. `el.closest('.card')` still works regardless of how many wrapper divs you add between the button and the card. Always prefer closest() when the depth might change.

Step-by-Step Tree Walk

Follow the traversal path from a single starting element:

tree-walker.js1/6
DOM Tree
<main>
<h1> "Our Menu"
<div.cards>
<div.card>← here
<h3> "Espresso"
<p> "£2.50"
<div.card>
<h3> "Cappuccino"
<p> "£3.50"

Select your starting element with querySelector.

const card = document.querySelector('.card')
// → <div class="card">
⚠️Check for null
Every traversal property can return null. nextElementSibling on the last child returns null. closest() returns null if no ancestor matches. Always guard: `const parent = el.parentElement; if (!parent) return;`

Your Challenge

In the console, select the first .card. Then: log card.parentElement.className, log card.firstElementChild.textContent, log card.nextElementSibling?.tagName, and use card.closest('main') to find the main element.

Challenge

Select a .card element. From it, navigate to its parent using parentElement, its first child using firstElementChild, and its next sibling using nextElementSibling. Then use closest() to find the nearest <main> ancestor.

parentElementchildrenfirstElementChildlastElementChildnextElementSiblingpreviousElementSiblingclosestDOM-traversal
Traversing the DOM | Nexus Learn