The Document Object Model

The Document Object Model (DOM) is a programming interface that represents HTML documents as a tree structure of objects that JavaScript can access and manipulate. It serves as the bridge between web pages and programming languages, transforming static HTML into dynamic, interactive applications.

What the DOM Provides:

  • Structured Representation: HTML elements become JavaScript objects

  • API for Manipulation: Methods to find, create, modify, and delete elements

  • Event System: Mechanisms to respond to user interactions

  • Live Connection: Changes to the DOM immediately reflect in the browser

  • Platform Independence: Standard interface across all browsers

The browser DOM APIs

The browser provides several related APIs for working with web pages:

  • DOM Core: Finding, accessing, and modifying elements and their content

  • DOM Events: Handling user interactions (clicks, keyboard input, form submissions)

  • DOM Style: Modifying CSS properties and classes dynamically

  • DOM Traversal: Navigating relationships between elements (parents, children, siblings)

  • DOM HTML: Specific methods and properties for HTML elements

  • DOM Manipulation: Creating, moving, and removing elements from the page

In essence: The HTML DOM is a standard for how to get, change, add, or delete HTML elements using JavaScript.

DOM Methods and Properties

DOM methods are actions you can perform on HTML elements (functions you can call). DOM properties are values associated with HTML elements that you can read or modify (like object properties).

html

Methods are called with parentheses and often accept arguments: getElementById("demo"). Properties are accessed without parentheses like regular object properties: element.innerHTML. Methods typically perform actions or return values, while properties store the current state of elements. Understanding this distinction helps you read documentation and write cleaner code.

Finding HTML elements

Before you can manipulate elements, you need to select them from the DOM. JavaScript provides multiple methods for finding elements, each suited to different scenarios.

Selecting by ID

Selecting by tag name

Selecting by Class name

Comparison of Selection Methods

Manipulating element content

Once you've selected elements, you can read and modify their content using various properties.

innerHTML vs textContent vs innerText

Modifying attributes

Attributes can be manipulated using getAttribute(), setAttribute(), removeAttribute(), and hasAttribute() methods. For standard HTML attributes, you can also use direct property access (element.src, element.href). Data attributes (data-*) are perfect for storing custom information and can be accessed via the dataset property, which automatically converts data-user-id to dataset.userId using camelCase. Direct property access is generally faster and more convenient for standard attributes.

Working with Classes

The classList API is the modern, safe way to manipulate CSS classes. It prevents common pitfalls like duplicate classes or spacing issues. The toggle() method is particularly useful for showing/hiding elements or changing states—it returns true if the class was added and false if removed. Never manipulate className directly with string concatenation; use classList methods instead for reliability and readability.

Modifying CSS styles

JavaScript can modify element styles through the style property or by changing classes. Each approach has specific use cases.

Inline styles with style property

The style property only accesses inline styles set via JavaScript or the HTML style attribute. It won't return styles from CSS files or <style> tags. Use window.getComputedStyle(element) to read the final computed styles from all sources.

Best Practice: Prefer adding/removing classes over setting inline styles when possible. Inline styles have high specificity and are harder to override, making CSS maintenance difficult. Use inline styles only for dynamic values that can't be predefined in CSS (like animation positions or calculated dimensions).

Creating and removing elements

The DOM isn't static—you can dynamically create new elements and remove existing ones to build interactive interfaces.

Creating new elements

Creating elements with createElement() and building them programmatically is safer than using innerHTML with string concatenation, especially with user input. When you set innerHTML, all existing content is destroyed and recreated, removing event listeners. Use insertAdjacentHTML() when you need to insert HTML strings—it's faster and doesn't destroy existing content. For complex structures, consider using template literals or document fragments for better performance.

Inserting elements at specific positions

insertAdjacentElement() and its siblings provide precise control over where new elements appear: beforebegin inserts before the element as a sibling, afterbegin inserts as the first child, beforeend inserts as the last child, and afterend inserts after the element as a sibling. These methods are more flexible than appendChild() which always adds at the end. Use insertAdjacentText() to safely insert user-provided text that should not be interpreted as HTML.

Removing elements

The modern remove() method is the simplest way to remove elements—just call it on the element you want to remove. The older removeChild() method requires a parent reference and is more verbose. When removing all children, using innerHTML = "" is fastest but doesn't properly clean up event listeners, potentially causing memory leaks. Using a loop with remove() or removeChild() is safer for elements with attached event listeners.

Cloning elements

cloneNode(true) creates a deep copy of an element including all descendants, while cloneNode(false) only copies the element itself. Clones are independent copies—changes to the clone don't affect the original. Event listeners are NOT copied, so you must reattach them. The template pattern is perfect for creating multiple similar elements: keep a hidden template in your HTML, clone it when needed, customize the clone, and add it to the page.

Handling events

Events are actions or occurrences that happen in the browser—clicks, key presses, form submissions, page loads, and more. JavaScript can listen for these events and respond accordingly.

Common event types

{% hint style="success" %} Understanding event types helps you respond to the right user actions. Mouse events handle pointer interactions, keyboard events capture typing, form events monitor user input, and window events track page state. DOMContentLoaded fires when the HTML is parsed and DOM is ready, while load waits for all resources (images, stylesheets). Use input for real-time validation and change for actions that should occur after the user finishes editing. {% endhint %}

Adding event listeners

The Event Object

The event object contains valuable information about what happened. event.target is the element that triggered the event (the clicked button), while event.currentTarget is the element with the listener attached (useful in event delegation). Use preventDefault() to stop default behaviors like form submissions or link navigation. Use stopPropagation() to prevent events from bubbling up to parent elements. Keyboard events provide key (character value) and code (physical key), plus modifier key booleans for building keyboard shortcuts.

DOM Traversal

Navigating the DOM tree to find related elements is called DOM traversal.

Parent, Child, and Sibling relationships

{% hint style="warning" %} childNodes and children are different: childNodes includes all nodes (text, comments, elements) while children only includes elements. Similarly, firstChild might be a text node, while firstElementChild is always an element. Text nodes are created by whitespace in HTML, which can cause unexpected behavior. For most purposes, use the element-specific properties (children, firstElementChild, etc.) to avoid dealing with text nodes. {% endhint %}

Querying within elements

All query methods (querySelector, querySelectorAll, getElementsBy*) can be called on any element, not just document. This scopes the search to descendants of that element only. The closest() method searches up the tree for the nearest ancestor matching a selector, which is perfect for finding container elements. These scoped searches are more efficient than searching the entire document and make code more maintainable by clearly expressing intent.

Best practices

1. Cache DOM References

2. Minimize reflows and repaints

3. Use document fragments for multiple elements

4. Remove Event Listeners when not needed

5. Prefer semantic HTML and CSS over JavaScript

{% hint style="success" %} Efficient DOM manipulation requires understanding browser rendering: reading layout properties like offsetHeight triggers reflows (expensive), and multiple DOM changes cause multiple repaints. Cache element references to avoid repeated queries. Batch DOM changes together, or use Document Fragments to build complex structures off-DOM then add them in one operation. Clean up event listeners to prevent memory leaks, especially with dynamically created elements. Let CSS handle styling and animations—JavaScript should manage state (add/remove classes), not visual details. {% endhint %}

Modern DOM APIs

IntersectionObserver

MutationObserver

ResizeObserver

Last updated

Was this helpful?