The Document Object Model (DOM)- A Complete Guide
Tags: javascript, html
Published: 2024-05-20 17:42:51

The Document Object Model (DOM) is fundamental to modern web development. It represents the structure of an HTML document as a hierarchical tree. Each element, attribute, and text node is a part of this tree. JavaScript allows us to interact with, and manipulate this tree dynamically. Understanding how to manipulate the DOM with JavaScript is important for creating dynamic and interactive web applications. This guide will, in the end, cover everything you need to know about DOM manipulation, from selecting elements to handling events and creating dynamic content.

HTML elements are the building blocks of web pages. Each HTML element represents a specific part of the page's content or structure, such as headings, paragraphs, images, links, etc. HTML elements are defined by tags, attributes, and content. For example:

```html
<! DOCTYPE html>
<html>
<head>
  <title>Document</title>
</head>
<body>
  <h1>Document Object Model</h1>
<img src="image.jpg" alt="image1">
  <p>This is a paragraph </p>
  <a href="https://link.com">Click for more details</a>
</body>
</html>
```

In the above Html code `<h1>`, `<img>`, `<p>`, and `<a>`, are HTML elements.

**What is a DOM Node?**

In the Document Object Model (DOM), every part of an HTML document is represented as a node. These nodes form a tree-like structure, where each node represents an object in the document. Each HTML element, attribute, and text node in the document corresponds to a node in the DOM tree. There are several types of DOM node, examples are:

1. **Element Nodes**: These represent HTML elements and form the structure of the document. For example, `<div>`, `<p>`, `<h1>`, etc.

2. **Text Nodes**: These represent the textual content within an element. For example, the text "This is a paragraph" in `<p> This is a paragraph </p>`.

3. **Attribute Nodes**: These represent attributes of an element. For example, the _src_ and _alt_ attribute of an `<img>` element.

4. **Comment Nodes**: These represent comments in the HTML code. For example, _<!-- This is a comment -->._


**Relationship Between HTML Elements and DOM Nodes**

HTML (Hypertext Markup Language) elements and DOM (Document Object Model) nodes are closely related and linked together in a way that makes it difficult to mention one, without mentioning the other in web development. Understanding their relationship is necessary for manipulating and interacting with web pages using JavaScript. The code snippet below summarizes what a node is:

```html
<!DOCTYPE html>
<html>
<head>
    <title>DOM Node Example</title>
</head>
<body>
      <h1>This is heading</h1>
  <p>This is a paragraph.</p>
  <img src="image1.jpg" alt="An image">
  <a href="https://link.com">Click on my link</a>
    <!-- This is a comment -->
</body>
</html>

```

The DOM tree of the above Html code looks like this:

Document (Node)
└── DocumentType: <!DOCTYPE html> (Node)
└── Element: `<html>` (Node)
    ├── Element: `<head>` (Node)
    │   └── Element: `<title>` (Node)
    │       └── Text: "DOM Node Example" (Node)
    └── Element: `<body>` (Node)
        ├── Element: `<h1>` (Node)
        │   └── Text: "This is heading" (Node)
        ├── Element: `<p>` (Node)
        │   └── Text: "This is a paragraph." (Node)
        ├── Element: `<img src="image1.jpg" alt="An image">` (Node)
        ├── Element: `<a href="https://link.com">` (Node)
        │   └── Text: "Click on my link" (Node)
        └── Comment: " This is a comment " (Node)


In this tree, each HTML element is represented as an element node. The text content within each element is represented as text node. Attributes of elements are represented as properties of their respective nodes.

**Accessing DOM Elements**

When working with JavaScript and the DOM, one main interest is to interact with the elements of the webpage. This involves accessing these elements, to modify, update, or manipulate them, as the case may be. There are several methods to achieve this, and each has its own strengths and use cases.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Accessing DOM Elements</title>
    
</head>
<body>
    
    <h1>Accessing Elements in the DOM</h1>

    <h2>getElementById method</h2>
  <div id="demo">Get this element using it's ID</div>

  <h2>getElementsByClassName method(.demo)</h2>
  <div class="demo">Access me by class (1)</div>
  <div class="demo">Access me by class (2)</div>

  <h2>getElementsByTagName method</h2>
  <span>This is a span tag</span> <br>
  <span>This is a span tag</span> <br>
  <!-- <span>This is a span tag</span> <br> -->

  <h2>Query Selector</h2>
  <div id="demo-query">Access me by query</div>

  <h2>Query Selector All</h2>
  <div class="demo-query-all">Access me by query all (1)</div>
  <div class="demo-query-all">Access me by query all (2)</div>
</body>
<script src="main.js"></script>

</html>

```

Below shows how the above code snippet looks like when inspected on a browser. _This will be used henceforth here, to illustrate all methods of accessing Dom Elements_:

![Dom manipulation1 - Inspect Elements1](https://i.imgur.com/gZ9raBr.png)

**Accessing Elements by ID**: An element's ID can be targeted or grabbed with the _getElementById()_ method of the document object. This is done by writing _document.getElementById()_, and then passing in the unique ID as a parameter, in-between two quotation marks inside the bracket.

![Dom manipulation2 - Inspect console1](https://i.imgur.com/gyAxzAF.png)

**Accessing Elements by ClassName**:Unlike in the above, here we can use the same class multiple times. This means, an _ID_ can only be for one element node, while _Class_ can be for more than one. Accessing elements by _ClassName_ can be done by using the _getElementsByClassName()_ method on the document object. That is, as, document. _getElementsByClassName()_, and then passing in the class name of the elements as a parameter, in-between two quotation marks inside the bracket. Thus:

![Dom manipulation3 - Inspect console2](https://i.imgur.com/jK6rJaH.png)

The above is unlike in _getElementById()_ method of accessing element, where at the end only the node of the targeted element was outputted. Here in _getElementsByClassName()_ method, we get an array-like (but not an array) output on the console, showing that there are two nodes having that class name. This is an _HTMLCollection_, not an array.

To see these element’s contents, and because there are other div elements in the Html code, store _getElementsByClassName(‘demo’)_, in a variable, say classDiv. Then access each same way an index array is accessed.

![Dom manipulation4 - Inspect console3](https://i.imgur.com/CSY4IZ0.png)

**Accessing Elements by TagName**: This is kind of similar to _getElementsByClassName()_ method, in that an _HTMLCollection_ is derived when we grab them. _document.getElementsByTagName()_ method is used to grab a collection of elements that have the same tag. For example, all `<h2>` tags on the webpage can be grabbed using this method.

![Dom manipulation5 - Inspect console4](https://i.imgur.com/7jJGneb.png)

Again, as in _getElementsByClassName()_ method, to access each content in this _HTMCollection_, showing there are five(5) h2 nodes in the Dom tree of the code above, it is done same way an index array is accessed

![Dom manipulation6 - Inspect console5](https://i.imgur.com/eph9cTU.png)

**The querySelector()**: The querySelector method is one of the most powerful and versatile method for selecting elements in the Document Object Model (DOM). It allows to select elements using CSS selectors, providing a flexible and concise way to target specific elements in a document. Its syntax is straightforward:
```Javascript
const element = document.querySelector(selector);
```
Where _selector_ is a CSS selector string that specifies the elements to be selected, and element is the first element that matches the specified selector, or null if no matches are found.

![Dom manipulation7 - Inspect console6](https://i.imgur.com/grmBtYy.png)

![Dom manipulation8 - Inspect console7](https://i.imgur.com/V1a9cso.png)

While the outputs of each of the above queried Selectors are okay, we notice that in the case of selecting elements by their _class_ logs out only one, instead of the two _divs_ with the class name _demo_.  The method, _querySelector_ will always only output one of more than one content found in the selector used. That is, when the likes of tagname, classname, and other selectors that houses more than one node, or text node, are queried, only the first node found is displayed. For this reason, instead of the _querySelector_, _querySelectorAll_ is preferred.

**querySelectorAll()**: 

![Dom manipulation9 - Inspect console8](https://i.imgur.com/KZRm8wn.png)

Different from the case of _getElementsByClassName()_ and _getElementsByTagName()_ methods, where an **HTMLCollection** is outputted on the console when a group of element or content is targeted, with _querySelectorAll()_ method, we get a **NodeList**, instead.

Now, with the above nodelist, we could select a particular div among the other div found within the selector used. Like others(targeting more than one node at the same time), each node is accessed same way an index array is accessed.

![Dom manipulation10 - Inspect console9](https://i.imgur.com/Dw3RYwz.png)

**Traversing the DOM**

To traverse is simply, to climb, descend, or visit all part of something. Traversing the DOM is a method for navigating the DOM tree. We could traverse from parent to child, vise-versa. The aim here is how to navigate from one node to another.

**What is parentNode?**: In the DOM, every element, attribute, and text node has a parent node, except for the root node (typically <html>). The _parentNode_ property is a key feature that allows to move up the DOM hierarchy from a selected element to its parent node. It provides a reference to the parent node of a selected element. The example below explains this:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
    <body>
        <div id="parent">
            <p id="child">This is a child with ID, 'child', inside it's parent, having ID, 'parent'</p>
        </div>

</body>
</html>

```

![Dom manipulation11 - Inspect Elements2](https://i.imgur.com/5LNcyoK.png)


![Dom manipulation12 - Inspect Elements3](https://i.imgur.com/04YEZTC.png)

The paragraph element (The child node) can be selected, and the _parentNode_ property can be used to access its parent. First step here is to select the paragraph element with the ID "child" using _document.getElementById("child")_, or any other method to access it. Then, its parent node can be accessed using the _parentNode_ property. For this example, the result is stored in the variable - parentNode.

![Dom manipulation13 - Inspect console10](https://i.imgur.com/pyUCKP9.png)

![Dom manipulation14 - Inspect console11](https://i.imgur.com/6MDvsRM.png)

Parent nodes are often useful when removing, styling, or modifying elements. For example, the paragraph (child) inside the div (parent) can be removed, using **removeChild()** method, and passing in as parameter, the variable where the particular child to be removed is stored. Thus:

![Dom manipulation15 - Inspect console12](https://i.imgur.com/vtUkV8G.png)

Notice now, that the parent node now is empty, with no child. The paragraph (child) can be added back to its parent node (div), using the **appendChild()**:

![Dom manipulation16 - Inspect console13](https://i.imgur.com/KTbZUkL.png)

There is another method that act same way as parent node, and returns exactly the same value. It is called, **_parentElement()_**. For example:

![Dom manipulation17 - Inspect Elements4](https://i.imgur.com/9rMfiPI.png)

Now the div above, with ID parent acts as the parent, with three children, each with ID, child-1, child-2, and child-3, respectively. Just like _parentNode()_ and _childNode()_ above, both the parent and each child can be accessed.

![Dom manipulation18 - Inspect console14](https://i.imgur.com/9DyT9zz.png)

The above shows how each child can be accessed in the Dom tree. To access their parent, use any of the child. E.g. secondChildNode:

![Dom manipulation19 - Inspect console15](https://i.imgur.com/s1WPiHv.png)

The parent before the above parent can be seen, by:

![Dom manipulation20 - Inspect console16](https://i.imgur.com/yfeIURo.png)

All of the above shows and explains how to traverse upward, especially from each child to their parent. We can as well go downward, from parent to children. The _firstChild_ and _lastChild_ property references the first and last child nodes of a parent.
To get the entire children (a collection of child nodes) of a parent, the _childNode_ property is used. For example, the entire children of the parent node above can be gotten thus:

![Dom manipulation21 - Inspect console17](https://i.imgur.com/DbvrwNM.png)

Because there is more than one child of this parent, it outputs a _NodeList_. Each child can be accessed same way elements in an index array are accessed, by using their respective index. Opening the _NodeList_ reveals that the indexes to get each child seen before, are 1,3, and 5, and this displayed thus: 

![Dom manipulation22 - Inspect console18](https://i.imgur.com/69ao19v.png)

Another property that works to traverse downward from the parent node to give the children is, the _children_ property.

![Dom manipulation23 - Inspect console19](https://i.imgur.com/UELbuep.png)

Aside traversing downward, from parent to children, or upward from children to parent, the **_nextElementSibling_** and **_previousElementSibling_** properties makes it possible to traverse or move from sibling to sibling.

![Dom manipulation24 - Inspect console20](https://i.imgur.com/3ocUqfD.png)

The firstSibling can be accessed again using the secondSibling. This can be achieved by using the _previousElementSibling_ property, thus:

![Dom manipulation25 - Inspect console21](https://i.imgur.com/GE4OYCo.png)

 **Manipulating DOM Content**

_Modifying Text Content_

One important feature of Document Object Model (DOM) is its ability to modify text and Html contents. Two important properties help to achieve these:

-	The textcontent
-	The innerHtml property

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

       <div id="parent">

        <p id="child">This is the paragraph</p>

       </div>

</body>

</html>

```

With the textContent we can output, change, or modify the text of the child node, having an ID ‘child’, in the above code. To do this, first grab and store the node in a variable. This is summarized in the example below:

![Dom manipulation26 - Inspect console22](https://i.imgur.com/Sa7RNKo.png)

While the _textContent_ handles adding new text, removing existing ones, and overall modifying the text on the Html document, the _innerHtml_ helps to update the HTML. For example:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    
    <p>This is a paragraph</p>
      
</body>

</html>

```
The above shows an Html code with a paragraph as its content in its body, which is displayed on the browser. Amongst others, with the _innerText_, it is possible change the already existing element and text node to something entirely different, directly with javascript.

![Dom manipulation27 - Inspect console23](https://i.imgur.com/KbbALbh.png)

**Creating New Elements**

The _createElement()_ method, also part of the Document interface, allows to create new HTML elements dynamically using JavaScript. It takes the tag name of the element you want to create as a string argument.

```javascript
const newElement = document.createElement('h2'); // Creates an <h2> element
newElement.textContent = "This is a dynamically created heading";

const parentElement = document.getElementById('parent');
parentElement.appendChild(newElement); // Append the new heading to the parent

```

This code will create an `<h2>` element with the text "This is a dynamically created heading" and add it as the last child of the element with the ID "parent" in your HTML document.

**Events and Event Handling**

Events are the signals that make web pages dynamic and engaging. These are actions or occurrences triggered by the user interacting with the page (clicking a button, submitting a form), or by the browser itself (loading a page, resizing the window). JavaScript allows us listen for these events and execute specific code in response. This mechanism, known as event handling, is important for creating interactive web experiences.

_What is an Event Listener?_

An event listener is a function that waits for a specific event to occur on a particular DOM element. When that event happens, the event listener function is executed, running the code that has been defined within it. For example, when you run the code below, and click the button, the _clickButton_ function will be triggered, displaying the alert message " You just clicked this button ". This demonstrates how an event listener allows code to respond to user interaction with a button.

```html
<!DOCTYPE html>
<html>
<body>

<button id="Button">Click here to continue!</button>

<script>
// Select the button element using its ID
const button = document.getElementById("Button");

// Define the function to be executed on click
function clickButton() {
  alert("You just clicked this button");
}

// Add the event listener to the button
button.addEventListener("click", clickButton);
</script>

</body>
</html>

```
_Directly Modify the Style Property of Any Element_

You can directly access the style property of a DOM element like any other property (e.g., element.style). This property acts like a CSS style declaration object. It allows to set individual style properties like color, _backgroundColor_, _fontSize_, etc., by assigning values directly (e.g., _element.style.color = 'red'_). For example, we can change the color of an `<h2>` element when a button is clicked:

```Javascript
// Accessing a paragraph element
Const pageHeading = document.getElementById('heading');

// Accessing a button element
const pageButton = document.getElementById('Button');

// Adding a click event listener to the button
pageButton.addEventListener('click', function() {
    // Change the color style of  pageHeading
    pageHeading.style.color = 'green';
});

```
The above code targets two elements from the HTML document: A paragraph element with the ID "heading" is stored in the pageHeading variable, and a button element with the ID "Button" is stored in the pageButton variable. It attaches a click event listener to the button element (pageButton). This means whenever the button is clicked, the code within the listener function will be executed. When the button is clicked, the code inside the listener function changes the color style of the paragraph element (pageHeading) to green. This is achieved by accessing the style property of the paragraph and setting the color property to "green".

**Conclusion**

The Document Object Model (DOM) serves as the bridge between web page's structure and its interactivity. It makes it possible to understand how HTML elements are translated into DOM nodes, making JavaScript’s ability to change the webpage live. This article has provided various ways of navigating and changing the DOM. It, in the end assists to understand how to select specific parts using selectors, traverse the DOM tree and change text value, attributes and even deleting them.