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
// 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
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:
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
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
// <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 themeactiveElement - Currently Focused Element
// 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
// 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
// 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();links - Link Collection
// 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()
// 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");Cookie Access
Although Cookie management has its own dedicated article, document.cookie is the entry point to access cookies.
// 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
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:
// 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
// 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
// ❌ 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
// ❌ 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:
- Check document ready state before executing code
- Use DOM methods instead of
document.write() - Cache collection references to improve performance
- Make reasonable use of document information for page analytics