Skip to content

DOM Navigation

The HTML Document Object Model (DOM) represents an HTML document as a tree-like structure of nodes. JavaScript allows you to navigate this tree, accessing and manipulating its elements, attributes, and content.

According to the W3C HTML DOM standard, everything in an HTML document is a node:

  • The entire document is a document node (document).
  • Every HTML element (e.g., <h1>, <p>, <div>) is an element node.
  • The actual text content inside HTML elements are text nodes.
  • Every HTML attribute (e.g., href in <a>, src in <img>) is an attribute node (though typically accessed via element properties).
  • Comments (<!-- ... -->) are comment nodes.

Imagine an HTML document as a family tree. The <html> element is the root. Elements like <head> and <body> are its children. Elements nested further down are descendants. This tree structure allows JavaScript to traverse and interact with any part of the document.

With the HTML DOM, JavaScript can:

  • Find, get, change, add, or delete HTML elements.
  • Change HTML attributes.
  • Change CSS styles.
  • React to HTML events.

Nodes in the DOM tree have hierarchical relationships:

  • Root Node: The topmost node in the tree (usually the <html> element in an HTML document, or the document node itself).
  • Parent Node: Every node (except the root) has exactly one parent node.
  • Child Nodes: A node can have zero, one, or many child nodes.
  • Siblings: Nodes that share the same parent node are siblings.
  • Descendants: A node’s children, its children’s children, and so on.
  • Ancestors: A node’s parent, its parent’s parent, and so on, up to the root.

Consider this simple HTML structure:

<html>
<head>
<title>DOM Tutorial</title>
</head>
<body>
<h1>DOM Lesson One</h1>
<p>Hello world!</p>
</body>
</html>

From this structure:

  • <html> is the root element node.
  • <html> is the parent of <head> and <body>.
  • <head> and <body> are siblings and children of <html>.
  • <title> is a child of <head>.
  • The text “DOM Tutorial” is a text node and a child of <title>.
  • <h1> and <p> are children of <body> and are siblings.

JavaScript provides properties to navigate the DOM tree based on these relationships:

Properties including whitespace/text/comment nodes:

Section titled “Properties including whitespace/text/comment nodes:”
  • parentNode: Accesses the parent node of the current node.
  • childNodes: Returns a NodeList (a collection) of all child nodes, including text nodes (whitespace) and comment nodes.
  • firstChild: Returns the first child node (can be text, comment, or element).
  • lastChild: Returns the last child node (can be text, comment, or element).
  • nextSibling: Returns the next node at the same tree level (can be text, comment, or element).
  • previousSibling: Returns the previous node at the same tree level (can be text, comment, or element).

Properties focusing on element nodes (often more convenient):

Section titled “Properties focusing on element nodes (often more convenient):”
  • parentElement: Similar to parentNode, but returns null if the parent is not an element node.
  • children: Returns an HTMLCollection of child element nodes only (ignores text and comment nodes).
  • firstElementChild: Returns the first child element node.
  • lastElementChild: Returns the last child element node.
  • nextElementSibling: Returns the next element sibling.
  • previousElementSibling: Returns the previous element sibling.
Common Pitfall: childNodes, firstChild, lastChild, nextSibling, and previousSibling can return text nodes (often just whitespace between elements in your HTML source) or comment nodes. If you specifically need element nodes, use their Element counterparts like children, firstElementChild, etc. This can save you from unexpected errors or behavior.

Accessing Node Content: textContent and innerHTML

Section titled “Accessing Node Content: textContent and innerHTML”

A common mistake is expecting an element node (e.g., <p>My Text</p>) to directly contain its text. The element node <p> actually contains a text node whose value is “My Text”.

To get or set the textual content of an element and its descendants:

  • textContent: Gets or sets the text content of a node and its descendants. It’s generally safer and often faster for manipulating text as it doesn’t parse HTML.
  • innerHTML: Gets or sets the HTML markup contained within an element. Use with caution when setting content from untrusted sources due to potential XSS security risks. It re-parses the content, which can be slower.

The nodeValue property can be used to get or set the value of certain node types, primarily text nodes and comment nodes. For element nodes, nodeValue is null.

Example using firstElementChild and textContent:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DOM Navigation Example</title>
</head>
<body>
<div id="container">
<h1>My First Page</h1>
<p id="intro">This is the introduction.</p>
</div>
<p id="demo">Result will appear here.</p>
<script>
const containerDiv = document.getElementById("container");
const firstHeading = containerDiv.firstElementChild; // Gets the <h1>
const demoP = document.getElementById("demo");
if (firstHeading && demoP) {
// Get text from h1 using textContent
demoP.textContent = "The first element's text: " + firstHeading.textContent;
}
// To get text from the p#intro using childNodes and nodeValue (more verbose)
// const introP = document.getElementById("intro");
// if (introP && introP.firstChild && introP.firstChild.nodeType === Node.TEXT_NODE) {
// console.log("Intro text node value:", introP.firstChild.nodeValue);
// }
</script>
</body>
</html>

In the example, document.getElementById() is a method to find an element. firstElementChild and textContent are properties.

JavaScript provides direct access to key parts of the document:

  • document.documentElement: Represents the root element of the document (usually the <html> tag).
  • document.body: Represents the <body> element of the document.
  • document.head: Represents the <head> element of the document.

Example: Alerting the HTML content of the body.

/*
// This would display the entire HTML content of the body in an alert box.
// Be cautious with alerting large amounts of HTML as it can be unreadable.
if (document.body) {
alert(document.body.innerHTML);
} else {
// This might happen if script runs before body is parsed and defer/DOMContentLoaded isn't used
console.error("document.body is not available yet.");
}
*/

The read-only nodeName property specifies the name of a node:

  • For element nodes, it’s the uppercase tag name (e.g., “DIV”, “P”, “H1”).
  • For attribute nodes, it’s the attribute name.
  • For text nodes, it’s always “#text”.
  • For comment nodes, it’s always “#comment”.
  • For the document node, it’s always “#document”.
const myDiv = document.createElement('div');
console.log(myDiv.nodeName); // Output: "DIV"

The read-only nodeType property returns a number representing the type of the node. This is useful for checking what kind of node you are dealing with.

Key nodeType values:

Node Type DescriptionNodeType ValueConstant (e.g., Node.ELEMENT_NODE)
Element1Node.ELEMENT_NODE
Attribute2Node.ATTRIBUTE_NODE
Text3Node.TEXT_NODE
Comment8Node.COMMENT_NODE
Document9Node.DOCUMENT_NODE
Document Fragment11Node.DOCUMENT_FRAGMENT_NODE

Example:

const pElement = document.querySelector('p');
if (pElement && pElement.firstChild && pElement.firstChild.nodeType === Node.TEXT_NODE) {
console.log("The first child is a text node.");
} else if (pElement && pElement.firstChild && pElement.firstChild.nodeType === Node.ELEMENT_NODE) {
console.log("The first child is an element node.");
}