If you're a web developer, chances are you've at least heard of the DOM - Document Object Model. It might, however, be nothing more than an abstract concept at the back of your mind, yearning for clarification. That is what this article is hoping to achieve. So, on to the burning question: what exactly is the Document Object Model?
Well, at the highest level and as the name implies it's a way of representing a document (usually HTML or XML) as a model consisting of objects (also called nodes). The nodes are organized in a hierarchical tree structure, with the document itself at the root, followed by the <html>
element which is in turn followed by top-level elements such as <head>
and <body>
.
But, elements can also have inner text and attributes like src
and href
. These are also represented as nodes belonging to their respective parent elements.
The 5 types of nodes
Document: the root of the DOM tree that represents the web page as a whole. Corresponds to the
document
object in JavaScript.Element: represents the HTML elements such as
<div>
,<h1>
and<p>
that make up the building blocks of the page. Corresponds to their respective element names in JavaScript but are often manipulated using theirid
orclass
attributes.Text: represents text inside elements, such as
<p>Hello</p>
, where "Hello" is a text node belonging to the<p>
element node. Text nodes are always leaf nodes, meaning they have no child nodes / attributes of their own. Can be accessed usingelement.innerText
in JavaScript.Attribute: represents attributes of elements (like aforementioned
class
andid
). Are usually accessed as properties of elements (element.href
for example) rather than as direct nodes.Comment: represents HTML comments (
<!-- like so -->
) that are not visible on the page but still part of the DOM tree. There is rarely a need to access these through JavaScript.
The purpose of the DOM
So, with what the DOM is and the different types of nodes out of the way, let's move on to the topic of what the DOM actually is used for. Its main function is to act as a sort of bridge or interface between the page content (HTML, CSS) and programming languages (most commonly JavaScript), allowing the programming language to manipulate the nodes in the DOM.
These changes are then reflected in the HTML when the page is re-rendered by the browser. This allows for content to be changed dynamically, such as adding items to a list or clicking a button to change the background color of an element.
Without this mechanism in place all content would have to be loaded statically from the server, with all "dynamic" updates being communicated through form submissions or similar, only then for everything to be fully re-rendered by the server.
This is pretty much how the web worked in its infancy, with the DOM since then having become one of the most major foundations of the modern web.
Real-world examples of manipulating the DOM
The main way developers interact with the DOM is with a programming language, usually JavaScript. This is done using the document object, which represents the whole web page in JavaScript (the document object is itself part of the window object which represents the browser's window). The document object allows us to access JavaScript's DOM methods for adding, removing and modifying existing nodes.
Modifying an element
To modify an element, we first have to find it, something which is most easily done using document.getElementById(id)
, a method that finds a single element using its id attribute. We can also find collections of elements using document.getElementsByClassName("<class>")
or document.getElementsByTagName("<tag>")
.
These methods return HTMLCollection objects, which are array-like lists of elements that will have to be looped through in some way in order to be modified. Another perhaps more straightforward approach is using the newer querySelector method, which returns the first element that matches a specific query (CSS selector):
const linkElement = document.querySelector("#linkElement");
This makes it possible to use the same method regardless if we're matching by id, tag name or class name. As mentioned this method only returns the first match, while a related method, document.querySelectorAll(".class")
, returns all matches.
Regardless of method used, once we have an element identified, we are now able to change its inner content, attributes and style properties. Here are some examples of modifying an already existing element in the DOM tree.
const linkElement = document.querySelector(#linkElement);
linkElement.innerText = "Now my text is updated.";
linkElement.innerHTML = "<p>Now my innerHTML consists of a paragraph element with text.</p>";
linkElement.href = "Now I'm pointing at a new destination!";
linkElement.style.backgroundColor = "red"; // Now my background color is red
Creating an element
To create a new element we use the following method: document.createElement("element")
. This method takes as an argument the type of element to create, such as h1, p, or div. However, simply creating the element is not enough, it's also necessary to define its location in the DOM tree.
Before this step, the element still exists in memory and can be manipulated like other elements, but it's not part of the DOM. To fix this and make it visible to the user, we have to append it to an already existing element. For example, if we have an unordered list, <ui>
, we can create list items, <li>
programmatically and append them to the <ul>
:
// Select the unordered list element
const ul = document.querySelector(\#list);
// Create a list item element
const li1 = document.createElement("li");
// Set the innerText of the list item element
li1.innerText = "I'm a list item";
// Append the list item element to the unordered list
ul.appendChild(li1);
If it's more of a stand-alone element, however, the created element can simply be appended to the <body>
element. It's also possible to append "invisible" elements like <script>
to the <head>
or <body>
elements, but it's not recommended to append directly to the <html>
element, since this is supposed to only have <head>
and <body>
elements as its children.
Removing an element
Just like when modifying an element, removing an element also requires first retrieving it in the DOM. This is done in the same way, either using document.getElementById
or by using querySelector
. Once the element has been found, running element.remove
takes care of the rest.
To remove multiple elements at once, such as those sharing the same class, the elements have to be retrieved as a HTMLCollection or NodeList and looped through, running the remove function on each item:
const items = document.querySelectorAll(".remove-me");
items.forEach(item => item.remove());
It's also possible to set the innerHTML attribute of an element to an empty string, but this will remove everything inside the element:
const container = document.getElementById("demo");
container.innerHTML = ""; // clears all child elements
In a similar fashion, replaceChildren()
can also be used to clear the contents of an element (by simply replacing its children with nothing):
const container = document.getElementById("demo");
container.replaceChildren(); // removes all children
How to skip the heavy lifting
In addition to the methods of interacting with the DOM using vanilla JavaScript presented in this section, there are also many other libraries and frameworks that have more convenient, built-in ways of accomplishing the same things.
One example is jQuery, which was created to simplify DOM manipulation across browsers:
// Select elements and change text
$(".item").text("Updated text");
// Add a new element
$("#list").append("<li>New item</li>");
// Hide elements with animation
$(".alert").fadeOut();
Another is React, which does not directly manipulate the DOM but instead uses a virtual DOM in order to only update the real DOM where new changes have been detected.
function App() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Here, the p element is the only component that is re-rendered when the count state changes (when the button is clicked).
Conclusion
To wrap things up, we've learned about the tree structure of the DOM, the 5 different types of nodes, why the DOM exists in the first place, how to modify, create and remove nodes in the DOM and finally how frameworks and libraries can simplify interacting with the DOM, making our developer lives a little easier. Happy coding and try to avoid DOMscrolling!
Top comments (0)