Skip to content

Document Object: The Bridge Connecting BOM and DOM

The Special Status of the Document Object

In the browser's object system, the document object plays a unique role—it's both part of BOM and the entry point to DOM. If we compare the browser to a building, window is the entire building's control center, then document is the central system connecting the control center to every room (web page content).

Accessed via window.document, the document object represents the HTML document currently loaded in the browser window. It provides:

  • Access to Document Information (title, URL, domain, referrer, etc.)
  • Document State Management (loading state, ready state, etc.)
  • Document Element Collections (forms, images, links, etc.)
  • Entry Point to DOM Operations (getElementById, querySelector, etc.)

Document Information Properties

The document object provides a series of properties to get various information about the current document.

Basic Document Information

javascript
// Document title
console.log(document.title); // Page's <title> content
// Can be modified
document.title = "New Title";

// Complete URL
console.log(document.URL);
// e.g.: 'https://example.com/page.html?id=123#section'

// Domain name
console.log(document.domain);
// e.g.: 'example.com'

// Referrer (source page)
console.log(document.referrer);
// URL of the page user came from

// Last modified time
console.log(document.lastModified);
// e.g.: '12/29/2025 08:57:20'

// Character set
console.log(document.characterSet); // Usually 'UTF-8'

// Document compatibility mode
console.log(document.compatMode);
// 'CSS1Compat' (standards mode) or 'BackCompat' (quirks mode)

Practical Application: Document Information Collection

javascript
class DocumentInfo {
  static getAll() {
    return {
      title: document.title,
      url: document.URL,
      domain: document.domain,
      referrer: document.referrer || "Direct access",
      lastModified: document.lastModified,
      charset: document.characterSet,
      mode: document.compatMode,
      readyState: document.readyState,
    };
  }

  static display() {
    const info = this.getAll();

    console.log("=== Document Information ===");
    Object.entries(info).forEach(([key, value]) => {
      console.log(`${key}: ${value}`);
    });
  }

  static logPageView() {
    const pageView = {
      title: document.title,
      url: document.URL,
      referrer: document.referrer,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
    };

    // Simulate sending to analytics server
    console.log("Record page view:", pageView);

    // In real application, use fetch to send data
    /*
    fetch('/api/analytics/pageview', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(pageView)
    });
    */
  }
}

// Usage
DocumentInfo.display();
DocumentInfo.logPageView();

Document State Properties

readyState - Document Loading State

document.readyState represents the loading state of the document:

javascript
console.log(document.readyState);
// Possible values:
// 'loading'    - Document is loading
// 'interactive' - Document has been parsed, but sub-resources (images, styles) are still loading
// 'complete'   - Document and all sub-resources have finished loading

// Listen for state changes
document.addEventListener("readystatechange", () => {
  console.log("Document state:", document.readyState);

  if (document.readyState === "interactive") {
    console.log("DOM is ready, can manipulate DOM elements");
  }

  if (document.readyState === "complete") {
    console.log("Page fully loaded, including all images and styles");
  }
});

Practical Application: Smart Initialization Manager

javascript
class PageInitializer {
  constructor() {
    this.tasks = {
      domReady: [], // Execute when DOM is ready
      pageLoaded: [], // Execute when page is fully loaded
    };

    this.init();
  }

  init() {
    // Check current state
    if (document.readyState === "loading") {
      // If still loading, listen for events
      document.addEventListener("DOMContentLoaded", () => {
        this.runTasks("domReady");
      });

      window.addEventListener("load", () => {
        this.runTasks("pageLoaded");
      });
    } else if (document.readyState === "interactive") {
      // DOM is ready but resources still loading
      this.runTasks("domReady");

      window.addEventListener("load", () => {
        this.runTasks("pageLoaded");
      });
    } else {
      // Page fully loaded
      this.runTasks("domReady");
      this.runTasks("pageLoaded");
    }
  }

  onDOMReady(callback) {
    this.tasks.domReady.push(callback);
    return this;
  }

  onPageLoaded(callback) {
    this.tasks.pageLoaded.push(callback);
    return this;
  }

  runTasks(phase) {
    console.log(`Executing ${phase} phase tasks`);

    this.tasks[phase].forEach((callback) => {
      try {
        callback();
      } catch (error) {
        console.error(`${phase} task execution failed:`, error);
      }
    });

    // Clear executed tasks
    this.tasks[phase] = [];
  }
}

// Usage example
const initializer = new PageInitializer();

initializer
  .onDOMReady(() => {
    console.log("DOM ready, initialize form validation");
    // Initialize features that don't need images
  })
  .onDOMReady(() => {
    console.log("DOM ready, bind event listeners");
  })
  .onPageLoaded(() => {
    console.log("Page fully loaded, start image lazy loading");
    // Initialize features that need images
  })
  .onPageLoaded(() => {
    console.log("Page fully loaded, start performance monitoring");
  });

Document Element Access

The document object provides shortcuts to access key elements of the document.

Document Root Elements

javascript
// <html> element
console.log(document.documentElement);
// Returns: <html>...</html>

// Get HTML element's language attribute
console.log(document.documentElement.lang); // e.g.: 'zh-CN'

// <head> element
console.log(document.head);
// Returns: <head>...</head>

// <body> element
console.log(document.body);
// Returns: <body>...</body>

// Practical application: Add global class
function setTheme(theme) {
  document.documentElement.setAttribute("data-theme", theme);

  // Or
  document.documentElement.classList.remove("theme-light", "theme-dark");
  document.documentElement.classList.add(`theme-${theme}`);
}

setTheme("dark"); // Apply dark theme

activeElement - Currently Focused Element

javascript
// Get element that currently has focus
console.log(document.activeElement);

// Check if specific element has focus
function hasFocus(element) {
  return document.activeElement === element;
}

// Practical application: Focus management
class FocusManager {
  static trackFocus() {
    document.addEventListener("focusin", (event) => {
      console.log("Focus moved to:", event.target);
      console.log("Current focused element:", document.activeElement);
    });

    document.addEventListener("focusout", (event) => {
      console.log("Focus left:", event.target);
    });
  }

  static trapFocus(containerSelector) {
    const container = document.querySelector(containerSelector);

    if (!container) return;

    const focusableElements = container.querySelectorAll(
      'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
    );

    const firstElement = focusableElements[0];
    const lastElement = focusableElements[focusableElements.length - 1];

    container.addEventListener("keydown", (event) => {
      if (event.key !== "Tab") return;

      if (event.shiftKey) {
        // Shift + Tab
        if (document.activeElement === firstElement) {
          event.preventDefault();
          lastElement.focus();
        }
      } else {
        // Tab
        if (document.activeElement === lastElement) {
          event.preventDefault();
          firstElement.focus();
        }
      }
    });
  }
}

// Usage: Trap focus in modal
FocusManager.trapFocus(".modal");

Document Element Collections

The document object provides several predefined collections for quick access to specific types of elements.

forms - Form Collection

javascript
// Get all forms
console.log(document.forms); // HTMLCollection

// Access by index
const firstForm = document.forms[0];

// Access by name or id
const loginForm = document.forms["loginForm"];
// Or
const loginForm2 = document.forms.loginForm;

// Iterate through all forms
Array.from(document.forms).forEach((form, index) => {
  console.log(`Form ${index}:`, form.name || form.id);
});

// Practical application: Form validation manager
class FormValidator {
  static validateAll() {
    const results = [];

    Array.from(document.forms).forEach((form) => {
      const isValid = this.validateForm(form);
      results.push({
        form: form.name || form.id,
        valid: isValid,
      });
    });

    return results;
  }

  static validateForm(form) {
    // Simple validation example
    const requiredFields = form.querySelectorAll("[required]");

    for (const field of requiredFields) {
      if (!field.value.trim()) {
        console.log(`Form ${form.name}: Field ${field.name} is empty`);
        return false;
      }
    }

    return true;
  }

  static setupAutoValidation() {
    Array.from(document.forms).forEach((form) => {
      form.addEventListener("submit", (event) => {
        if (!this.validateForm(form)) {
          event.preventDefault();
          alert("Please fill in all required fields");
        }
      });
    });
  }
}

// Usage
FormValidator.setupAutoValidation();

images - Image Collection

javascript
// Get all images
console.log(document.images); // HTMLCollection

// Iterate through all images
Array.from(document.images).forEach((img, index) => {
  console.log(`Image ${index}:`, img.src);
  console.log(`  alt:`, img.alt);
  console.log(`  size:`, img.width, "x", img.height);
});

// Practical application: Image lazy loading
class LazyImageLoader {
  static init() {
    const images = Array.from(document.images);

    images.forEach((img) => {
      // If image has data-src attribute, use lazy loading
      if (img.dataset.src) {
        this.observeImage(img);
      }
    });
  }

  static observeImage(img) {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            this.loadImage(entry.target);
            observer.unobserve(entry.target);
          }
        });
      },
      {
        rootMargin: "50px", // Start loading 50px early
      }
    );

    observer.observe(img);
  }

  static loadImage(img) {
    const src = img.dataset.src;

    if (!src) return;

    console.log("Loading image:", src);

    img.src = src;
    img.removeAttribute("data-src");

    img.addEventListener("load", () => {
      img.classList.add("loaded");
    });

    img.addEventListener("error", () => {
      console.error("Image loading failed:", src);
      img.src = "/images/placeholder.png";
    });
  }

  static preloadImages(selector) {
    const images = document.querySelectorAll(selector);

    images.forEach((img) => {
      const tempImg = new Image();
      tempImg.src = img.dataset.src || img.src;
    });
  }
}

// Usage
LazyImageLoader.init();
javascript
// Get all <a> and <area> elements with href attribute
console.log(document.links);

// Iterate through all links
Array.from(document.links).forEach((link, index) => {
  console.log(`Link ${index}:`, link.href);
  console.log(`  Text:`, link.textContent);
});

// Practical application: External link handling
class LinkManager {
  static markExternalLinks() {
    const currentDomain = window.location.hostname;

    Array.from(document.links).forEach((link) => {
      const linkDomain = new URL(link.href).hostname;

      // If external link
      if (linkDomain && linkDomain !== currentDomain) {
        // Add external link icon
        link.classList.add("external-link");

        // Open in new tab
        link.target = "_blank";

        // Security attributes
        link.rel = "noopener noreferrer";

        // Add tooltip
        if (!link.title) {
          link.title = "Opens in new tab (external link)";
        }
      }
    });
  }

  static trackLinkClicks() {
    Array.from(document.links).forEach((link) => {
      link.addEventListener("click", (event) => {
        console.log("User clicked link:", {
          href: link.href,
          text: link.textContent.trim(),
          timestamp: new Date().toISOString(),
        });

        // Send to analytics server
        /*
        fetch('/api/analytics/link-click', {
          method: 'POST',
          body: JSON.stringify({
            href: link.href,
            text: link.textContent.trim()
          })
        });
        */
      });
    });
  }

  static addDownloadAttributes() {
    Array.from(document.links).forEach((link) => {
      const href = link.href.toLowerCase();

      // Check if file download link
      const downloadExtensions = [
        ".pdf",
        ".zip",
        ".doc",
        ".docx",
        ".xls",
        ".xlsx",
      ];

      if (downloadExtensions.some((ext) => href.endsWith(ext))) {
        link.download = ""; // Trigger download
        link.classList.add("download-link");
      }
    });
  }
}

// Usage
LinkManager.markExternalLinks();
LinkManager.trackLinkClicks();
LinkManager.addDownloadAttributes();

Document Write Methods

Although not recommended in modern development, it's important to understand these methods.

document.write() and writeln()

javascript
// Write content to document (overwrites existing content)
document.write("<h1>Hello World</h1>");

// writeln() adds newline at end
document.writeln("<p>Line 1</p>");
document.writeln("<p>Line 2</p>");

// ⚠️ Note: Calling write() after document loading completes overwrites entire document
window.addEventListener("load", () => {
  // This will clear entire page!
  // document.write('<h1>Page overwritten</h1>');
});

// Better alternative: Use DOM methods
function addContent(html) {
  const container = document.getElementById("content");
  container.innerHTML += html;
}

// Or
function addElement(tag, content, parent = document.body) {
  const element = document.createElement(tag);
  element.textContent = content;
  parent.appendChild(element);
}

addElement("h1", "Hello World");

Although Cookie management has its own dedicated article, document.cookie is the entry point to access cookies.

javascript
// Read all cookies
console.log(document.cookie);
// e.g.: 'user=John; session=abc123; theme=dark'

// Set cookie
document.cookie = "username=Sarah; path=/; max-age=3600";

// Cookie management utility class (simplified)
class CookieManager {
  static get(name) {
    const cookies = document.cookie.split("; ");
    const cookie = cookies.find((c) => c.startsWith(name + "="));
    return cookie ? decodeURIComponent(cookie.split("=")[1]) : null;
  }

  static set(name, value, options = {}) {
    let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(
      value
    )}`;

    if (options.maxAge) {
      cookieString += `; max-age=${options.maxAge}`;
    }

    if (options.path) {
      cookieString += `; path=${options.path}`;
    }

    if (options.secure) {
      cookieString += "; secure";
    }

    if (options.sameSite) {
      cookieString += `; samesite=${options.sameSite}`;
    }

    document.cookie = cookieString;
  }

  static delete(name) {
    this.set(name, "", { maxAge: -1 });
  }
}

// Usage
CookieManager.set("user", "John", { maxAge: 3600, path: "/" });
console.log("Username:", CookieManager.get("user"));

Comprehensive Practical Application Example

Page Metadata Manager

javascript
class PageMetadata {
  // Set document title (with template support)
  static setTitle(title, append = false) {
    if (append) {
      document.title = `${title} | ${this.getSiteName()}`;
    } else {
      document.title = title;
    }
  }

  static getSiteName() {
    return 'TechCorp';  // Can read from config
  }

  // Dynamically update meta tags
  static setMeta(name, content) {
    let meta = document.querySelector(`meta[name="${name}"]`);

    if (!meta) {
      meta = document.createElement('meta');
      meta.name = name;
      document.head.appendChild(meta);
    }

    meta.content = content;
  }

  // Set Open Graph tags
  static setOGMeta(property, content) {
    let meta = document.querySelector(`meta[property="${property}"]`);

    if (!meta) {
      meta = document.createElement('meta');
      meta.setAttribute('property', property);
      document.head.appendChild(meta);
    }

    meta.content = content;
  }

  // Add canonical URL
  static setCanonical(url) {
    let link = document.querySelector('link[rel="canonical"]');

    if (!link) {
      link = document.createElement('link');
      link.rel = 'canonical';
      document.head.appendChild(link);
    }

    link.href = url;
  }

  // Dynamically load stylesheet
  static loadStylesheet(href) {
    return new Promise((resolve, reject) => {
      const link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = href;

      link.onload = () => resolve(link);
      link.onerror = () => reject(new Error(`Failed to load ${href}`));

      document.head.appendChild(link);
    });
  }

  // Dynamically load script
  static loadScript(src, async = true) {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = src;
      script.async = async;

      script.onload = () => resolve(script);
      script.onerror = () => reject(new Error(`Failed to load ${src}`));

      document.head.appendChild(script);
    });
  }

  // Set page description
  static setDescription(description) {
    this.setMeta('description', description);
    this.setOGMeta('og:description', description);
  }

  // Set page keywords
  static setKeywords(keywords) {
    const keywordString = Array.isArray(keywords)
      ? keywords.join(', ')
      : keywords;

    this.setMeta('keywords', keywordString);
  }
}

// Usage example
PageMetadata.setTitle('Product Details', true);
// Document title becomes: "Product Details | TechCorp"

PageMetadata.setDescription('This is an innovative product with multiple advanced features');
PageMetadata.setKeywords(['Innovation', 'Product', 'Technology']);
PageMetadata.setCanonical('https://example.com/products/123');

// Dynamically load resources
async function loadResources() {
  try {
    await PageMetadata.loadStylesheet('/css/product.css');
    console.log('Stylesheet loaded successfully');

    await PageMetadata.loadScript('/js/product.js');
    console.log('Script loaded successfully');
  } catch (error) {
    console.error('Resource loading failed:', error);
  }
}

Relationship Between Document Object and DOM

Although the document object belongs to BOM, it's also the starting point for DOM operations:

javascript
// BOM-level document properties
console.log(document.URL); // BOM functionality
console.log(document.domain); // BOM functionality
console.log(document.referrer); // BOM functionality

// DOM-level document methods
const element = document.getElementById("myElement"); // DOM operation
const elements = document.querySelectorAll(".class"); // DOM operation
const newDiv = document.createElement("div"); // DOM operation

// Combining both
function updatePageInfo() {
  // Use BOM functionality to get information
  const title = document.title;
  const url = document.URL;

  // Use DOM functionality to display information
  const infoDiv = document.getElementById("page-info");
  infoDiv.innerHTML = `
    <h3>Page Information</h3>
    <p>Title: ${title}</p>
    <p>URL: ${url}</p>
  `;
}

Best Practices

1. Check Document Ready State

javascript
// Good practice: Check document state before executing
function initApp() {
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", runApp);
  } else {
    runApp();
  }
}

function runApp() {
  console.log("Application initialized");
  // Application logic...
}

2. Use document.write() with Caution

javascript
// ❌ Avoid this
document.write("<div>Content</div>");

// ✅ Use DOM methods
const div = document.createElement("div");
div.textContent = "Content";
document.body.appendChild(div);

// ✅ Or use innerHTML (if content source is trusted)
document.getElementById("container").innerHTML = "<div>Content</div>";

3. Optimize Collection Access

javascript
// ❌ Access DOM every time
for (let i = 0; i < document.images.length; i++) {
  console.log(document.images[i].src);
}

// ✅ Cache collection
const images = Array.from(document.images);
images.forEach((img) => {
  console.log(img.src);
});

Summary

The document object is an important bridge connecting BOM and DOM. It provides:

Document Information:

  • Basic information (title, URL, domain, referrer)
  • Document state (readyState, compatMode)

Element Access:

  • Root elements (documentElement, head, body)
  • Focused element (activeElement)
  • Element collections (forms, images, links)

Document Operations:

  • Write methods (write(), writeln(), not recommended)
  • Cookie access (document.cookie)

Best Practices:

  1. Check document ready state before executing code
  2. Use DOM methods instead of document.write()
  3. Cache collection references to improve performance
  4. Make reasonable use of document information for page analytics