Traversing the DOM

Last updated: October 18, 2021.

In a previous article, we covered how to select DOM elements, i.e. select elements by ID or class using the getElementById, getElementsByClassName, getElementsByTagName, querySelector and querySelectorAll methods.

In this article, we cover a related topic: how to reach nearby nodes and elements of an already selected element.

Why traverse the DOM?

In some situations, it is better to traverse than to repeatedly select elements.

For example, when programming a blog, a new post could be programmed to appear as the ‘first child’ of a container div. Or, when programming a ‘clear cart’ button for a webshop, it is easier to delete all the ‘children’ (items) from the cart (its ‘parent’ element) than to program the selection of each item individually.

Learning how to traverse the DOM efficiently will make it much easier for you to manipulate it.

HTML template for this tutorial

To follow along with this tutorial, save this code in a new HTML document and create a new script.js file in the same directory.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
      div {
        margin-left: 5vw;
      }
    </style>
</head>
<body>
    
<div id="grandparent">
  Grandparent
    <div id = "parent1">
    Parent 1
      <div id = "p1child1">Child 1</div>
      <div id = "p1child2">Child 2</div>
      <div id = "p1child3">Child 3</div>
    </div>
    <div id = "parent2">
    Parent 2
      <div id = "p2child1">Child 1</div>
      <div id = "p2child2">Child 2</div>
      <div id = "p2child3">Child 3</div>
    </div>
</div>

<script src="script.js"></script>
</body>
</html>

This should render something like the following to your screen:

Rendered HTML markup screenshot

How to traverse the DOM

We can actually traverse the DOM without selecting any elements at all first.

We can do this by calling the document object and applying to it either the head or body method:

console.log(document.head) // Returns the head section of the DOM
console.log(document.body) // Returns the body section of the DOM

This is useful in some contexts. But often we want to select nearby nodes or elements of a select element.

Traversing down

As a starting point, let’s select the grandparent div:

const grandparent = document.getElementById('grandparent');

From here, let’s try to select the ‘first child’ of this element by using the firstChild method:

console.log(grandparent.firstChild) // Result: “ Grandparent “

You may have expected this to return the parent1 div, but instead it returns the text “ Grandparent “. This is because this text is in fact the first child of grandparent. To select the first element, we must specify this explicitly using the firstElementChild method:

console.log(grandparent.firstElementChild) // Selects parent1 div

Similarly, we select the ‘last child’:

console.log(grandparent.lastChild) // Returns text “      “
console.log(grandparent.lastElementChild) // Returns parent2 div

You might be wondering why grandparent.lastChild returns an empty text object. This is because the ‘last child’ of the grandparent div is the empty character space between the closing div tag of parent2 and grandparent. Again, we need to use lastElementChild if we want to select the last element.

If we want to select all the children of the grandparent element, we can apply the children method. And we can add the index of a child inside square brackets to select a specific child from the children of grandparent.

console.log(grandparent.children) // Returns parent1 and parent2 div
console.log(grandparent.children[0]) // Returns parent1 div
console.log(grandparent.children[1]) // Returns parent2 div

Unlike firstChild and lastChild, the children method selects elements only.

To go down multiple levels, the downwards traversal methods can be combined. For example:

console.log(grandparent.firstElementChild.lastElementChild)
// Returns the p1child3 div

Traversing up

For traversing up, let’s start by selecting one of the lowest level elements:

const p1child2 = document.getElementById('p1child2')

To traverse upwards from this starting point, the parentNode or parentElement method can be used:

console.log(p1child2.parentNode) // Returns parent1 div
console.log(p1child2.parentElement) // Returns parent1 div

Both parentNode and parentElement return the same result because an element is a type of node. In practice, the parent of an element or node is almost always an element. So both usually return the same result.

To traverse up, the methods can be combined:

console.log(p1child2.parentElement.parentElement) // Returns grandparent div

Traversing horizontally

Continuing from the selected p1child2 element, we can travsere sideways by using the nextSibling and previousSibling methods:

console.log(p1child2.nextSibling) // Returns empty text object
console.log(p1child2.previousSibling) // Returns empty text object

You may have expected this to select the p1child3 and then the p1child1 div. But because elements have not been selected explicitly, the empty text space between p1child2 div and the other divs is selected.

To select the sibling elements, we must specify this using nextElementSibling and previousElementSibling:

console.log(p1child2.nextElementSibling) // Returns p1child3 div
console.log(p1child2.previousElementSibling) // Returns p1child1 div

Summary

Traversal directionMethodSelects
Traversing down.firstChildFirst child
.firstElementChildFirst child element
.lastChildLast child
.lastElementChildLast child element
.childrenAll child elements
.children[0]Child element index 0
Traversing up.parentNodeParent node
.parentElementParent element
Traversing horizontally.nextSiblingNext node or element
.previousSiblingPrevious node or element
.nextElementSiblingNext element
.previousElementSiblingPrevious element