Document 对象:连接 BOM 与 DOM 的桥梁
Document 对象的特殊地位
在浏览器的对象体系中,document 对象扮演着一个独特的角色——它既是 BOM 的一部分,也是 DOM 的入口。如果把浏览器比作一座大厦,window 是整栋建筑的总控制室,那么 document 就是连接控制室和每个房间(网页内容)的中枢系统。
通过 window.document 访问,document 对象代表当前加载在浏览器窗口中的 HTML 文档。它提供了:
- 访问文档信息(标题、URL、域名、来源等)
- 文档状态管理(加载状态、就绪状态等)
- 文档元素集合(表单、图片、链接等)
- DOM 操作的入口(getElementById、querySelector 等)
文档信息属性
document 对象提供了一系列属性来获取当前文档的各种信息。
基本文档信息
javascript
// 文档标题
console.log(document.title); // 页面的 <title> 内容
// 可以修改
document.title = "新标题";
// 完整的 URL
console.log(document.URL);
// 例如: 'https://example.com/page.html?id=123#section'
// 域名
console.log(document.domain);
// 例如: 'example.com'
// 来源页面(referrer)
console.log(document.referrer);
// 用户从哪个页面跳转过来的 URL
// 最后修改时间
console.log(document.lastModified);
// 例如: '12/29/2025 08:57:20'
// 字符集
console.log(document.characterSet); // 通常是 'UTF-8'
// 文档兼容模式
console.log(document.compatMode);
// 'CSS1Compat' (标准模式) 或 'BackCompat' (怪异模式)实际应用:文档信息收集
javascript
class DocumentInfo {
static getAll() {
return {
title: document.title,
url: document.URL,
domain: document.domain,
referrer: document.referrer || "直接访问",
lastModified: document.lastModified,
charset: document.characterSet,
mode: document.compatMode,
readyState: document.readyState,
};
}
static display() {
const info = this.getAll();
console.log("=== 文档信息 ===");
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,
};
// 模拟发送到分析服务器
console.log("记录页面访问:", pageView);
// 实际应用中使用 fetch 发送数据
/*
fetch('/api/analytics/pageview', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(pageView)
});
*/
}
}
// 使用
DocumentInfo.display();
DocumentInfo.logPageView();文档状态属性
readyState - 文档加载状态
document.readyState 表示文档的加载状态:
javascript
console.log(document.readyState);
// 可能的值:
// 'loading' - 文档正在加载
// 'interactive' - 文档已解析,但子资源(图片、样式等)仍在加载
// 'complete' - 文档和所有子资源都已加载完成
// 监听状态变化
document.addEventListener("readystatechange", () => {
console.log("文档状态:", document.readyState);
if (document.readyState === "interactive") {
console.log("DOM 已就绪,可以操作 DOM 元素");
}
if (document.readyState === "complete") {
console.log("页面完全加载,包括所有图片和样式");
}
});实际应用:智能初始化管理器
javascript
class PageInitializer {
constructor() {
this.tasks = {
domReady: [], // DOM 就绪时执行
pageLoaded: [], // 页面完全加载后执行
};
this.init();
}
init() {
// 检查当前状态
if (document.readyState === "loading") {
// 如果还在加载,监听事件
document.addEventListener("DOMContentLoaded", () => {
this.runTasks("domReady");
});
window.addEventListener("load", () => {
this.runTasks("pageLoaded");
});
} else if (document.readyState === "interactive") {
// DOM 已就绪,但资源还在加载
this.runTasks("domReady");
window.addEventListener("load", () => {
this.runTasks("pageLoaded");
});
} else {
// 页面已完全加载
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(`执行 ${phase} 阶段任务`);
this.tasks[phase].forEach((callback) => {
try {
callback();
} catch (error) {
console.error(`${phase} 任务执行失败:`, error);
}
});
// 清空已执行的任务
this.tasks[phase] = [];
}
}
// 使用示例
const initializer = new PageInitializer();
initializer
.onDOMReady(() => {
console.log("DOM 就绪,初始化表单验证");
// 初始化不需要图片的功能
})
.onDOMReady(() => {
console.log("DOM 就绪,绑定事件监听器");
})
.onPageLoaded(() => {
console.log("页面完全加载,启动图片懒加载");
// 初始化需要图片的功能
})
.onPageLoaded(() => {
console.log("页面完全加载,启动性能监控");
});文档元素访问
document 对象提供了快捷方式来访问文档的关键元素。
文档根元素
javascript
// <html> 元素
console.log(document.documentElement);
// 返回: <html>...</html>
// 获取 HTML 元素的语言属性
console.log(document.documentElement.lang); // 例如: 'zh-CN'
// <head> 元素
console.log(document.head);
// 返回: <head>...</head>
// <body> 元素
console.log(document.body);
// 返回: <body>...</body>
// 实际应用:添加全局 class
function setTheme(theme) {
document.documentElement.setAttribute("data-theme", theme);
// 或者
document.documentElement.classList.remove("theme-light", "theme-dark");
document.documentElement.classList.add(`theme-${theme}`);
}
setTheme("dark"); // 应用暗色主题activeElement - 当前焦点元素
javascript
// 获取当前拥有焦点的元素
console.log(document.activeElement);
// 检查特定元素是否有焦点
function hasFocus(element) {
return document.activeElement === element;
}
// 实际应用:焦点管理
class FocusManager {
static trackFocus() {
document.addEventListener("focusin", (event) => {
console.log("焦点移到:", event.target);
console.log("当前焦点元素:", document.activeElement);
});
document.addEventListener("focusout", (event) => {
console.log("焦点离开:", 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();
}
}
});
}
}
// 使用:在模态框中限制焦点
FocusManager.trapFocus(".modal");文档元素集合
document 对象提供了几个预定义的集合,可以快速访问特定类型的元素。
forms - 表单集合
javascript
// 获取所有表单
console.log(document.forms); // HTMLCollection
// 通过索引访问
const firstForm = document.forms[0];
// 通过 name 或 id 访问
const loginForm = document.forms["loginForm"];
// 或
const loginForm2 = document.forms.loginForm;
// 遍历所有表单
Array.from(document.forms).forEach((form, index) => {
console.log(`表单 ${index}:`, form.name || form.id);
});
// 实际应用:表单验证管理器
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) {
// 简单的验证示例
const requiredFields = form.querySelectorAll("[required]");
for (const field of requiredFields) {
if (!field.value.trim()) {
console.log(`表单 ${form.name}: 字段 ${field.name} 为空`);
return false;
}
}
return true;
}
static setupAutoValidation() {
Array.from(document.forms).forEach((form) => {
form.addEventListener("submit", (event) => {
if (!this.validateForm(form)) {
event.preventDefault();
alert("请填写所有必填字段");
}
});
});
}
}
// 使用
FormValidator.setupAutoValidation();images - 图片集合
javascript
// 获取所有图片
console.log(document.images); // HTMLCollection
// 遍历所有图片
Array.from(document.images).forEach((img, index) => {
console.log(`图片 ${index}:`, img.src);
console.log(` alt:`, img.alt);
console.log(` 尺寸:`, img.width, "x", img.height);
});
// 实际应用:图片懒加载
class LazyImageLoader {
static init() {
const images = Array.from(document.images);
images.forEach((img) => {
// 如果图片有 data-src 属性,使用懒加载
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", // 提前 50px 开始加载
}
);
observer.observe(img);
}
static loadImage(img) {
const src = img.dataset.src;
if (!src) return;
console.log("加载图片:", src);
img.src = src;
img.removeAttribute("data-src");
img.addEventListener("load", () => {
img.classList.add("loaded");
});
img.addEventListener("error", () => {
console.error("图片加载失败:", 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;
});
}
}
// 使用
LazyImageLoader.init();links - 链接集合
javascript
// 获取所有带 href 属性的 <a> 和 <area> 元素
console.log(document.links);
// 遍历所有链接
Array.from(document.links).forEach((link, index) => {
console.log(`链接 ${index}:`, link.href);
console.log(` 文本:`, link.textContent);
});
// 实际应用:外部链接处理
class LinkManager {
static markExternalLinks() {
const currentDomain = window.location.hostname;
Array.from(document.links).forEach((link) => {
const linkDomain = new URL(link.href).hostname;
// 如果是外部链接
if (linkDomain && linkDomain !== currentDomain) {
// 添加外部链接图标
link.classList.add("external-link");
// 在新标签页打开
link.target = "_blank";
// 安全属性
link.rel = "noopener noreferrer";
// 添加提示
if (!link.title) {
link.title = "在新标签页打开(外部链接)";
}
}
});
}
static trackLinkClicks() {
Array.from(document.links).forEach((link) => {
link.addEventListener("click", (event) => {
console.log("用户点击链接:", {
href: link.href,
text: link.textContent.trim(),
timestamp: new Date().toISOString(),
});
// 发送到分析服务器
/*
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();
// 检查是否是文件下载链接
const downloadExtensions = [
".pdf",
".zip",
".doc",
".docx",
".xls",
".xlsx",
];
if (downloadExtensions.some((ext) => href.endsWith(ext))) {
link.download = ""; // 触发下载
link.classList.add("download-link");
}
});
}
}
// 使用
LinkManager.markExternalLinks();
LinkManager.trackLinkClicks();
LinkManager.addDownloadAttributes();文档写入方法
虽然不推荐在现代开发中使用,但了解这些方法仍然很重要。
document.write() 和 writeln()
javascript
// 向文档写入内容(会覆盖现有内容)
document.write("<h1>Hello World</h1>");
// writeln() 会在末尾添加换行符
document.writeln("<p>Line 1</p>");
document.writeln("<p>Line 2</p>");
// ⚠️ 注意:如果在文档加载完成后调用 write(),会覆盖整个文档
window.addEventListener("load", () => {
// 这会清空整个页面!
// document.write('<h1>页面被覆盖了</h1>');
});
// 更好的替代方案:使用 DOM 方法
function addContent(html) {
const container = document.getElementById("content");
container.innerHTML += html;
}
// 或者
function addElement(tag, content, parent = document.body) {
const element = document.createElement(tag);
element.textContent = content;
parent.appendChild(element);
}
addElement("h1", "Hello World");Cookie 访问
虽然 Cookie 管理有专门的文章,但 document.cookie 是访问 Cookie 的入口。
javascript
// 读取所有 Cookie
console.log(document.cookie);
// 例如: 'user=John; session=abc123; theme=dark'
// 设置 Cookie
document.cookie = "username=Sarah; path=/; max-age=3600";
// Cookie 管理工具类(简化版)
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 });
}
}
// 使用
CookieManager.set("user", "John", { maxAge: 3600, path: "/" });
console.log("用户名:", CookieManager.get("user"));实际应用综合示例
页面元数据管理器
javascript
class PageMetadata {
// 设置文档标题(支持模板)
static setTitle(title, append = false) {
if (append) {
document.title = `${title} | ${this.getSiteName()}`;
} else {
document.title = title;
}
}
static getSiteName() {
return 'TechCorp'; // 可以从配置读取
}
// 动态更新 meta 标签
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;
}
// 设置 Open Graph 标签
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;
}
// 添加 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;
}
// 动态加载样式表
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);
});
}
// 动态加载脚本
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);
});
}
// 设置网页描述
static setDescription(description) {
this.setMeta('description', description);
this.setOGMeta('og:description
', description);
}
// 设置网页关键词
static setKeywords(keywords) {
const keywordString = Array.isArray(keywords)
? keywords.join(', ')
: keywords;
this.setMeta('keywords', keywordString);
}
}
// 使用示例
PageMetadata.setTitle('产品详情', true);
// 文档标题变为: "产品详情 | TechCorp"
PageMetadata.setDescription('这是一款创新的产品,具有多项先进功能');
PageMetadata.setKeywords(['创新', '产品', '技术']);
PageMetadata.setCanonical('https://example.com/products/123');
// 动态加载资源
async function loadResources() {
try {
await PageMetadata.loadStylesheet('/css/product.css');
console.log('样式表加载成功');
await PageMetadata.loadScript('/js/product.js');
console.log('脚本加载成功');
} catch (error) {
console.error('资源加载失败:', error);
}
}Document 对象与 DOM 的关系
虽然 document 对象属于 BOM,但它也是 DOM 操作的起点:
javascript
// BOM 层面的 document 属性
console.log(document.URL); // BOM 功能
console.log(document.domain); // BOM 功能
console.log(document.referrer); // BOM 功能
// DOM 层面的 document 方法
const element = document.getElementById("myElement"); // DOM 操作
const elements = document.querySelectorAll(".class"); // DOM 操作
const newDiv = document.createElement("div"); // DOM 操作
// 两者的结合使用
function updatePageInfo() {
// 使用 BOM 功能获取信息
const title = document.title;
const url = document.URL;
// 使用 DOM 功能显示信息
const infoDiv = document.getElementById("page-info");
infoDiv.innerHTML = `
<h3>页面信息</h3>
<p>标题: ${title}</p>
<p>URL: ${url}</p>
`;
}最佳实践
1. 检查文档就绪状态
javascript
// 好的做法:检查文档状态再执行
function initApp() {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", runApp);
} else {
runApp();
}
}
function runApp() {
console.log("应用初始化");
// 应用逻辑...
}2. 谨慎使用 document.write()
javascript
// ❌ 避免这样做
document.write("<div>内容</div>");
// ✅ 使用 DOM 方法
const div = document.createElement("div");
div.textContent = "内容";
document.body.appendChild(div);
// ✅ 或使用 innerHTML(如果信任内容来源)
document.getElementById("container").innerHTML = "<div>内容</div>";3. 优化集合访问
javascript
// ❌ 每次都访问 DOM
for (let i = 0; i < document.images.length; i++) {
console.log(document.images[i].src);
}
// ✅ 缓存集合
const images = Array.from(document.images);
images.forEach((img) => {
console.log(img.src);
});小结
document 对象是连接 BOM 和 DOM 的重要桥梁。它提供了:
文档信息:
- 基本信息(
title,URL,domain,referrer) - 文档状态(
readyState,compatMode)
元素访问:
- 根元素(
documentElement,head,body) - 焦点元素(
activeElement) - 元素集合(
forms,images,links)
文档操作:
- 写入方法(
write(),writeln(),不推荐使用) - Cookie 访问(
document.cookie)
最佳实践:
- 检查文档就绪状态再执行代码
- 使用 DOM 方法替代
document.write() - 缓存集合引用以提高性能
- 合理利用文档信息进行页面分析