Chapter 5: The Document Object Model (DOM)

The Document Object Model (DOM) is a programming interface for web documents. It represents the page so that programs can change the document structure, style, and content. The DOM represents the document as nodes and objects, allowing JavaScript to interact with the page.

What you'll learn:
  • Global DOM objects (window, document, location, navigator, screen, history)
  • Unobtrusive JavaScript and best practices
  • Modifying element content, styles, and classes
  • Traversing and manipulating the DOM tree
  • Using browser DevTools for debugging

5.1 Global DOM Objects

When JavaScript runs in a browser, it has access to several global objects that represent different aspects of the browser environment. These objects provide the foundation for all DOM manipulation and browser interaction.

The Six Global DOM Objects

Every browser provides six core global objects that JavaScript can access:

Object Description Common Uses
window The browser window itself; the global scope Timers, alerts, window dimensions
document The HTML document loaded in the window Selecting/modifying elements
location The current URL and navigation Redirects, URL parameters
navigator Information about the browser Browser detection, geolocation
screen Information about the user's screen Screen dimensions, color depth
history The browser's session history Back/forward navigation

The window Object

The window object is the top-level object in the browser. All global variables and functions become properties of window.

// Window properties
console.log(window.innerWidth);  // Browser viewport width
console.log(window.innerHeight); // Browser viewport height
console.log(window.outerWidth);  // Full window width
console.log(window.outerHeight); // Full window height

// Window methods
window.alert("Hello!");   // Show alert dialog
window.confirm("Sure?");  // Show confirm dialog (returns boolean)
window.prompt("Name?");   // Show input dialog (returns string)

// Timers
window.setTimeout(() => console.log("Delayed!"), 1000);
window.setInterval(() => console.log("Repeating!"), 2000);

The document Object

The document object represents the entire HTML page. It's your primary interface for selecting and manipulating DOM elements.

// Document properties
console.log(document.title);       // Page title
console.log(document.URL);         // Current URL
console.log(document.referrer);    // Previous page URL
console.log(document.cookie);      // Cookies (string)

// Selecting elements
document.getElementById("myId");          // By ID
document.querySelector(".myClass");       // CSS selector (first match)
document.querySelectorAll("p");           // All matches

// Document structure
console.log(document.documentElement);    // <html> element
console.log(document.head);               // <head> element
console.log(document.body);               // <body> element

The location Object

The location object provides information about the current URL and methods to navigate to other pages.

// Location properties (for URL: https://example.com:8080/path/page.html?id=123#section)
console.log(location.href);      // Full URL
console.log(location.protocol);  // "https:"
console.log(location.host);      // "example.com:8080"
console.log(location.hostname);  // "example.com"
console.log(location.port);      // "8080"
console.log(location.pathname);  // "/path/page.html"
console.log(location.search);    // "?id=123"
console.log(location.hash);      // "#section"

// Navigation methods
location.assign("https://google.com");  // Navigate (adds to history)
location.replace("https://google.com"); // Navigate (no history entry)
location.reload();                      // Refresh the page

The navigator, screen, and history Objects

// Navigator - browser information
console.log(navigator.userAgent);   // Browser identifier string
console.log(navigator.language);    // Browser language (e.g., "en-US")
console.log(navigator.onLine);      // true if connected to internet
console.log(navigator.cookieEnabled); // true if cookies enabled

// Screen - display information
console.log(screen.width);       // Screen width in pixels
console.log(screen.height);      // Screen height in pixels
console.log(screen.availWidth);  // Available width (minus taskbar)
console.log(screen.colorDepth);  // Color depth (bits)

// History - session navigation
history.back();      // Go back one page
history.forward();   // Go forward one page
history.go(-2);      // Go back 2 pages
console.log(history.length); // Number of entries in history

5.1.2 Unobtrusive JavaScript

Unobtrusive JavaScript is a best practice that keeps JavaScript code separate from HTML, making code more maintainable and accessible. Instead of inline event handlers, we attach events from external JavaScript files.

Obtrusive JavaScript (Avoid This!)
<button onclick="alert('Clicked!')">Click Me</button>
<a href="#" onclick="doSomething(); return false;">Link</a>
<input onchange="validateField(this)">

Problems: Mixes behavior with structure, hard to maintain, not accessible.

Unobtrusive JavaScript (Best Practice)
<button id="myButton">Click Me</button>
<a href="/actual-page" id="myLink">Link</a>
<input id="myInput">
document.getElementById("myButton").onclick = function() {
  alert("Clicked!");
};

document.getElementById("myLink").onclick = function(event) {
  event.preventDefault();
  doSomething();
};

The window.onload Event Handler

When JavaScript runs, the DOM might not be fully loaded yet. The window.onload event fires when the entire page (including images, stylesheets) has loaded.

// Wait for page to fully load before running code
window.onload = function() {
  // DOM is now fully loaded - safe to manipulate elements
  const heading = document.getElementById("main-heading");
  heading.textContent = "Page Loaded!";
};

// Modern alternative: DOMContentLoaded (faster, doesn't wait for images)
document.addEventListener("DOMContentLoaded", function() {
  console.log("DOM ready!");
});

Anonymous Functions

An anonymous function is a function without a name. They're commonly used for event handlers and callbacks.

// Named function
function greet() {
  console.log("Hello!");
}

// Anonymous function (assigned to variable)
const greet = function() {
  console.log("Hello!");
};

// Anonymous function (as event handler)
button.onclick = function() {
  console.log("Clicked!");
};

// Arrow function (ES6 shorthand)
button.onclick = () => console.log("Clicked!");

// With parameters
button.onclick = (event) => {
  console.log(event.target);
};

The this Keyword in DOM Events

In event handlers, this refers to the DOM element that triggered the event. This is incredibly useful for reusable event handlers.

// 'this' refers to the clicked button
const buttons = document.querySelectorAll(".btn");

buttons.forEach(function(button) {
  button.onclick = function() {
    console.log(this.textContent); // Text of THIS button
    this.style.backgroundColor = "green";
  };
});

// ⚠️ Warning: Arrow functions don't bind 'this'!
button.onclick = () => {
  console.log(this); // 'this' is NOT the button (inherits from scope)
};

// Solution: Use event.target with arrow functions
button.onclick = (event) => {
  console.log(event.target); // The clicked element
};

5.2 DOM Element Objects

Once you've selected a DOM element, you can read and modify its properties. This section covers the most common ways to manipulate element content, styles, and attributes.

Modifying Element Text

There are three main properties for working with element content:

Property Description When to Use
innerHTML Gets/sets HTML content (parses tags) When you need to insert HTML elements
textContent Gets/sets text only (ignores tags) When inserting plain text (safer!)
value Gets/sets value of form inputs For <input>, <textarea>, <select>
const div = document.getElementById("myDiv");
const input = document.getElementById("myInput");

// innerHTML - parses HTML tags
div.innerHTML = "<strong>Bold text</strong>"; // Renders as bold
console.log(div.innerHTML); // "<strong>Bold text</strong>"

// textContent - treats everything as text (safer)
div.textContent = "<strong>Bold text</strong>"; // Shows literal tags
console.log(div.textContent); // "<strong>Bold text</strong>"

// value - for form elements
console.log(input.value);    // Current input value
input.value = "New text";    // Set new value
input.value = "";            // Clear the input
Security Warning: innerHTML and XSS

Never use innerHTML with user-provided data! It can lead to Cross-Site Scripting (XSS) attacks where malicious code is injected into your page.

// ❌ DANGEROUS - user could inject scripts
div.innerHTML = userInput;

// ✅ SAFE - textContent escapes HTML
div.textContent = userInput;

Adjusting Styles with the DOM

Every element has a style property that allows you to directly modify CSS properties. Note that CSS properties with hyphens become camelCase in JavaScript.

const element = document.getElementById("myElement");

// Setting individual styles
element.style.color = "blue";
element.style.backgroundColor = "yellow";  // Note: camelCase!
element.style.fontSize = "20px";
element.style.border = "2px solid red";
element.style.marginTop = "10px";

// Reading styles (only works for inline styles)
console.log(element.style.color); // "blue"

// To read computed styles (including CSS file styles)
const computed = window.getComputedStyle(element);
console.log(computed.fontSize); // "20px"

CSS Property Name Conversion

CSS Property JavaScript Property
background-color backgroundColor
font-size fontSize
margin-top marginTop
border-radius borderRadius
z-index zIndex

Unobtrusive Styling with className and classList

Instead of setting styles directly, the unobtrusive approach is to toggle CSS classes. This keeps your styles in CSS files and your JavaScript clean.

const element = document.getElementById("myElement");

// className - get/set the entire class attribute
console.log(element.className); // "card primary"
element.className = "card active"; // Replaces ALL classes

// classList - modern API for managing classes (preferred!)
element.classList.add("active");      // Add a class
element.classList.remove("hidden");   // Remove a class
element.classList.toggle("expanded"); // Add if missing, remove if present
element.classList.contains("active"); // Returns true/false
element.classList.replace("old", "new"); // Replace one class with another

// Add multiple classes
element.classList.add("class1", "class2", "class3");
Best Practice: Use classList for State Changes

Define your visual states in CSS, then toggle classes in JavaScript:

.button { background: blue; }
.button.loading { background: gray; cursor: wait; }
.button.success { background: green; }
button.classList.add("loading");
// After async operation...
button.classList.remove("loading");
button.classList.add("success");

5.3 The DOM Tree

The DOM represents an HTML document as a tree structure where each element, attribute, and piece of text is a node. Understanding this tree structure is essential for navigating and manipulating the DOM.

Types of DOM Nodes

There are three main types of nodes you'll work with:

Node Type nodeType Value Description Example
Element Node 1 HTML tags <div>, <p>, <span>
Text Node 3 Text content inside elements "Hello World"
Attribute Node 2 Attributes on elements id="main", class="card"
<div id="container">
  <p class="intro">Hello <strong>World</strong></p>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
  </ul>
</div>
const div = document.getElementById("container");

console.log(div.nodeType);     // 1 (Element)
console.log(div.nodeName);     // "DIV"
console.log(div.nodeValue);    // null (elements have no nodeValue)

const textNode = div.firstChild;
console.log(textNode.nodeType); // 3 (Text - includes whitespace!)

Traversing the DOM Tree

You can navigate between nodes using these properties:

Property Returns Includes Text Nodes?
parentNode Parent node N/A
childNodes All child nodes (NodeList) ✅ Yes
children Child elements only ❌ No
firstChild First child node ✅ Yes
firstElementChild First child element ❌ No
lastChild Last child node ✅ Yes
lastElementChild Last child element ❌ No
nextSibling Next sibling node ✅ Yes
nextElementSibling Next sibling element ❌ No
previousSibling Previous sibling node ✅ Yes
previousElementSibling Previous sibling element ❌ No
Watch Out for Text Nodes!

Whitespace between tags creates text nodes! Use the Element variants (like firstElementChild) to skip text nodes.

const ul = document.querySelector("ul");

// Parent navigation
console.log(ul.parentNode);       // <div id="container">

// Child navigation (prefer Element variants)
console.log(ul.children);         // HTMLCollection [li, li]
console.log(ul.firstElementChild); // First <li>
console.log(ul.lastElementChild);  // Last <li>

// Sibling navigation
const firstLi = ul.firstElementChild;
console.log(firstLi.nextElementSibling); // Second <li>

Selecting Groups of Elements

Modern JavaScript provides powerful methods to select elements:

// By ID (returns single element or null)
const header = document.getElementById("header");

// By tag name (returns live HTMLCollection)
const paragraphs = document.getElementsByTagName("p");

// By class name (returns live HTMLCollection)
const cards = document.getElementsByClassName("card");

// By name attribute (returns live NodeList)
const radios = document.getElementsByName("color");

// CSS selectors (RECOMMENDED - most flexible)
const firstCard = document.querySelector(".card");         // First match
const allCards = document.querySelectorAll(".card");       // All matches
const nested = document.querySelector("#main .card.active"); // Complex selector

Creating and Modifying Nodes

You can dynamically create, insert, and remove DOM elements:

// Create a new element
const newDiv = document.createElement("div");
newDiv.id = "myNewDiv";
newDiv.className = "card";
newDiv.textContent = "I'm a new div!";

// Create a text node (rarely needed - textContent is easier)
const textNode = document.createTextNode("Hello!");

// Set attributes
newDiv.setAttribute("data-id", "123");
newDiv.setAttribute("aria-label", "Card");
console.log(newDiv.getAttribute("data-id")); // "123"
const container = document.getElementById("container");
const newItem = document.createElement("li");
newItem.textContent = "New Item";

// appendChild - adds to the end
container.appendChild(newItem);

// insertBefore - insert before a reference node
const referenceNode = container.firstElementChild;
container.insertBefore(newItem, referenceNode);

// Modern methods (easier syntax)
container.prepend(newItem);              // Add to beginning
container.append(newItem);               // Add to end
referenceNode.before(newItem);           // Insert before
referenceNode.after(newItem);            // Insert after

// Insert HTML string
container.insertAdjacentHTML("beforeend", "<p>New paragraph</p>");
const container = document.getElementById("container");
const oldChild = container.firstElementChild;

// removeChild - remove a child element (returns removed node)
const removed = container.removeChild(oldChild);

// Modern: remove() - element removes itself
oldChild.remove();

// replaceChild - replace one child with another
const newChild = document.createElement("p");
newChild.textContent = "Replacement!";
container.replaceChild(newChild, oldChild);

// Modern: replaceWith()
oldChild.replaceWith(newChild);

5.3.2 Browser DevTools for DOM Debugging

Modern browsers have powerful built-in developer tools (DevTools) that replaced tools like Firebug. Here's how to use them for DOM debugging:

Opening DevTools

  • Chrome/Edge: F12 or Ctrl+Shift+I (Cmd+Opt+I on Mac)
  • Firefox: F12 or Ctrl+Shift+I
  • Right-click: "Inspect" on any element

Key DevTools Panels

Elements Panel
  • View and edit the DOM tree in real-time
  • Inspect and modify CSS styles
  • See computed styles (final applied values)
  • Right-click elements to copy, delete, or edit
Console Panel
  • Run JavaScript commands directly
  • $0 - Reference to currently selected element
  • $("selector") - Shorthand for querySelector
  • $$("selector") - Shorthand for querySelectorAll

Debugging with Breakpoints

In the Sources panel, you can set breakpoints to pause code execution:

function updateElement() {
  const el = document.getElementById("myElement");
  debugger; // Code pauses here when DevTools is open
  el.textContent = "Updated!";
}

Useful Console Commands

// Inspect selected element
console.dir($0);           // Show all properties

// Find elements
$$("button")               // All buttons on page

// Monitor events
monitorEvents($0, "click"); // Log all click events on element

// Copy to clipboard
copy($0.outerHTML);        // Copy element's HTML

Quick Quizzes

Test your knowledge! Click on an answer to check if you're correct.

Quiz 1: Which global object provides information about the current URL?

A navigator
B location
C history

Quiz 2: What is the safest way to insert user-provided text into a DOM element?

A element.innerHTML = userInput
B element.outerHTML = userInput
C element.textContent = userInput

Quiz 3: What does classList.toggle("active") do?

A Adds "active" if missing, removes it if present
B Always adds the "active" class
C Checks if the "active" class exists

Quiz 4: Why should you use firstElementChild instead of firstChild?

A firstElementChild is faster
B firstChild might return a whitespace text node instead of an element
C firstChild is deprecated

Hands-On Exercises

Practice makes perfect! Write code in the textarea and click "Run Code" to execute it and see the results in the preview area.
EXERCISE

DOM Manipulation

Practice creating and styling elements dynamically. Modify the code to create a styled card with:

  • A title and description
  • Padding and background color
  • Rounded corners and a border
Your preview will appear here...
EXERCISE

DOM Traversal

Practice navigating the DOM tree. Write code that:

  • Creates a list with multiple items
  • Traverses the list using firstElementChild and nextElementSibling
  • Styles each item during traversal
Your preview will appear here...