DOM Nodes and Traversal

Part 2 of Introduction to the DOM series

Continuing our series Introducing the DOM and how we can use it to build web sites, today we will talk about DOM Nodes and how we can move around or navigate the DOM.

Traversing the DOM

The DOM also gives us the ability to traverse or move along its tree-like structure using properties that it gives us. HTML elements are nested within each other forming the overall structure of our page. There may be only a few or several levels of nesting elements within elements and all of that is represented in the DOM tree of element nodes. All items in the DOM are defined as nodes. There are three main types of nodes that we will focus on here: Element Nodes, Text Nodes and Comment Nodes.

What are these Nodes?

  • An HTML element in the DOM, is an element node
  • The text content of an element is a text node
  • An HTML comment is a comment node

Pretty straight forward, right? It's nice when that happens for us. An odd thing to note that you may come across, is that the empty whitespace between our HTML elements will often be read by the browser as a new line tag ( "\n" ). Let's picture a family tree showing parents, children, and siblings, the nodes in the DOM also have a relationship to each other as parents, children, and siblings. Now with this little bit of background we can start playing with moving around our DOM.

Parent Nodes

The parent of any node is the node that is one level above it. There are two properties to get the parent node - element.parentNode and element.parentElement. Let's look at some code shall we. If this is all new to you, I encourage you to follow along and copy this code into your own IDE of choice and experiment yourself.

<!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>
    <link rel="stylesheet" href="./parent.css">
</head>
<body>
    <div id="title">
        <h2>Parent / Child DOM Elements</h2>
    </div>
    <div class="parent">
        <div class="child">Box 1</div>
        <div class="child">Box 2</div>
    </div>
    <script src="./parent.js"></script>
</body>
</html>
body {
    background: lightslategray;
}

.parent {
    border: 5px solid red;
}

.child {
    height: 300px;
    width: 300px;
    border: 5px solid green;
    margin: 20px 20px;
    padding: 10px 10px;
    text-align: center;
}
let parent = document.querySelector(".parent");
let child = document.querySelector(".child");

// Both of these properties return the PARENT of the respective element
console.log(child.parentNode);
console.log(child.parentElement);

// What if we look at the PARENT of our 'class="parent"' element (?)
console.log(parent.parentNode);
console.log(parent.parentElement);

Screen Shot 2022-09-15 at 6.18.40 PM.png

In the code above we select reference to two of the elements on our HTML page. We can see that using either property we get back the <div class="parent"> tag as the parent element. What if we did the same thing on the parent element? We get the <body> tag element from our HTML page.

Now what about the children?

Child Nodes

<!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>
    <link rel="stylesheet" href="./styles.css">
</head>
<body>
    <div class="list-container">
        <p>Child - List Demo</p>
        <ul id="list">
            <li data-value="1">One</li>
            <li data-value="2">Two</li>
            <li data-value="3">Three</li>
            <li data-value="4">Four</li>
            <li data-value="5">Five</li>
        </ul>
    </div>

    <script src="./list.js"></script>
</body>
</html>
// Grab our reference element
let listItems = document.getElementById("list");
console.log(listItems);
// open up the console in your browser and take a look at these outputs yourself!!
console.log(listItems.children);
console.log(listItems.childNodes);
console.log(listItems.childElementCount);

Screen Shot 2022-08-18 at 9.39.26 AM.png

We can see that using the element.children property we get back a list of the child element nodes, in fact we get back an HTMLCollection containing all of the child nodes within the selected element. When we print out element.childNodes we see several more items in the returned NodeList than we originally anticipate. The reason for this is because the childNodes property is including not only the Element Nodes, but also any Text or Comment Nodes that are in the DOM. Both of the returned objects, HTMLCollection and NodeList, are Arrays. If we wanted to select a specific list item, then we have to select its index position within the array type returned. For example element.children[0] would return the first list element <li data-value="1">One</li>

When you are first learning there seems like there is an infinite amount of things you need to remember and understand before you can start building something with code. Thats very common, you're not alone in this, try not to stress about knowing everything or even knowing enough. Just start coding and piece things together as you need to understand a topic more in depth.

Sibling Nodes

Siblings, just like in real life some of us have a sibling or two, maybe several, maybe none. Even in JavaScript we can see that some siblings just aren't on the same level. I know, bad programming joke :-p

Just as we have to keep in mind parent and child relationships having different Node or Element properties, siblings also have similar properties we can use to navigate along DOM object elements. We still need to be mindful of what we are trying to target, are we trying to navigate to the next Node in the DOM or do we want the next Element? Let's modify our practice html a bit and explore siblings.

<p>DOM Sibling Demo</p>
<div class="list-container">
      <ul id="list">
          <li data-value="1">One</li>
          <li data-value="2">Two</li>
          <li class="listItem" data-value="3">Three</li>
          <li data-value="4">Four</li>
          <li data-value="5">Five</li>
      </ul>
</div>
let listItems = document.getElementById("list");
console.log(listItems.children);
console.log(listItems.childNodes);
console.log("// ----------------------------- //");
// Let's select the list item with the class="listItem" attribute that we added to the HTML file
let item3 = document.querySelector(".listItem");
console.log(item3)
// Log the NEXT sibling (if there is one)
console.log(item3.nextSibling);
console.log(item3.nextElementSibling);
// Log the PREVIOUS sibling (if one exists)
console.log(item3.previousSibling);
console.log(item3.previousElementSibling);

Screen Shot 2022-08-18 at 10.43.17 AM.png

We can see above that by using the properties nextSibling and previousSibling (at least in our case) are returning Text Nodes. Again, in our case, it is just a reference to a new line character, \n, that doesn't visually appear on the DOM, but we have the ability to navigate to if we so choose. If we take a look at the properties nextElementSibling and previousElementSibling they return to us a reference to the elements <li data-value="2">Two</li> and <li data-value="4">Four</li> respectively. Now that we have hold of these references we can modify or manipulate the elements in various ways. We might want to grab hold of the text content and highlight it, or capture the data-value attribute value to use later in our code. Maybe we just want to change the color of the background, whatever we want to modify we first need to get the reference to the element or elements. This is what we are going to explore in the next article when we continue our exploration of the DOM and how we can use it.

We are going to end things there for the moment, and in the next article we will talk about how to make modifications to DOM element attributes.

I hope this article on Traversing the DOM with JavaScript was useful to you. Thanks for reading, until next time!