学习 JavaScript 的 Web 开发,DOM 和 BOM 是连接“静态代码”与“动态交互”的桥梁。简单来说,一个是操作网页内容的工具,一个是操作浏览器窗口的工具。

一,BOM (Browser Object Model) —— 浏览器对象模型

BOM 是你的“外壳”。 它提供了独立于内容而与浏览器窗口进行交互的对象。它的核心对象是 window,它代表整个浏览器窗口,是 JS 中的顶级对象。可以用来弹窗、获取屏幕分辨率、控制浏览器前进后退、获取当前网址等。

而且由于没有标准组织的统一规范,不用浏览器的表现可能略有差异。

补充说明:BOM本身不是一个具体的对象,而是一个由浏览器提供的,用于和浏览器窗口及浏览器功能进行交互的对象集合,可以理解为一套API规范。

下面是常见的 BOM 对象及其作用的表格:

对象名称 作用描述
window BOM 的顶层对象,代表浏览器窗口。它也是全局作用域,所有 BOM/DOM 对象都作为其属性存在(如 window.document)。
navigator 提供浏览器自身的信息,如用户代理(navigator.userAgent)、是否在线(navigator.onLine)、语言、插件等。
location 操作当前页面的 URL 信息,可读取或修改(如跳转、刷新、获取参数)。
history 控制浏览器的历史记录,支持前进(history.forward())、后退(history.back())、无刷新跳转(history.pushState())等。
screen 获取用户屏幕的尺寸、色彩深度等物理信息(如 screen.widthscreen.height)。
document 虽然主要是 DOM 的根对象,但通常也视为 BOM 的一部分(因为可通过 window.document 访问),代表加载的网页文档。
console 提供浏览器控制台调试功能,如 console.log()console.error() 等。
frames 一个类数组对象,表示当前窗口中所有 <iframe> 子窗口的集合(也可通过 window.frames 访问)。
localStorage 提供持久化的本地存储(无过期时间),数据保存在浏览器中(同源策略)。
sessionStorage 提供会话级别的存储,标签页关闭后数据清除。
performance 提供性能相关的信息,如页面加载时间、资源获取耗时等(通过 window.performance 访问)。
locationbarmenubarpersonalbar 一些用于表示浏览器界面栏的可选对象(仅在某些旧浏览器或权限下可访问,现代浏览器通常不可修改)。

1.Window 对象

window是BOM的顶层核心对象,大部分BOM的属性和方法都挂在window上,而且window本身就代表浏览器窗口。

完全可以用这玩意儿来点出BOM的所有属性和方法使用,只是有时候可以省略罢了。

既然 window 是全局对象,所有全局变量和函数都会自动成为其属性。

// 使用 let 或 const 声明变量,避免意外创建全局属性
const name = "Gemini";
console.log(window.name); // 注意:这里输出的是 window.name,而不是上面声明的 name
// 因为 var 声明的全局变量会自动成为 window 的属性,
// 但 let/const 声明的全局变量不会添加到 window 上。
// 所以这段代码如果期望输出 "Gemini",需要用 var 或者手动给 window.name 赋值。
// 为了保持逻辑正确,我们可以明确给 window.name 赋值:
window.name = "Gemini";
console.log(window.name); // 输出: Gemini

// 常用弹窗方法(生产环境建议少用,体验不佳)
window.alert("这是一个警告框");          // 简单提示,无返回值

const confirmDelete = window.confirm("你确定要删除吗?"); // 返回 boolean
console.log(confirmDelete); // 用户点“确定”为 true,点“取消”为 false

const nickname = window.prompt("请输入你的昵称", "默认值"); // 返回 string 或 null
console.log(nickname); // 用户输入后返回输入字符串,点“取消”返回 null
  1. window.name

    • window 对象的一个属性,用于设置或获取当前窗口的名称。
    • 通过 var 声明的全局变量会自动成为 window 对象的属性(例如 var a = 1 等价于 window.a = 1)。但 let/const 声明的全局变量不会添加到 window 上。
  2. window.alert(message)

    • 弹出一个模态对话框,显示一条消息和一个“确定”按钮。无返回值
    • 代码执行会暂停,直到用户点击“确定”。
    • 阻塞主线程,影响用户体验;移动端样式简陋。可以自定义轻提示(toast)或非模态通知(如 Element UI 的 Message 组件)。
  3. window.confirm(message)

    • 弹出一个包含消息、“确定”和“取消”按钮的模态对话框。
    • 用户点击“确定” → true,用户点击“取消”(或按 ESC) → false
    • 同样阻塞后续 JavaScript 执行,直到用户做出选择。
  4. window.prompt(message, defaultValue)

    • 弹出一个包含消息、文本输入框、“确定”和“取消”按钮的模态对话框。
    • message:提示文本,defaultValue(可选):输入框中的预填值。
    • 用户点击“确定” → 输入框中的字符串(若未输入则为空字符串 “”),用户点击“取消” → null

2.定时器 (Timers)

这是 BOM 中最常用的功能之一,用于处理异步执行。
setTimeout: 延迟一段时间执行一次。setInterval: 每隔一段时间重复执行。

// 3秒后执行
const timerId = setTimeout(() => {
    console.log("三秒到了!");
}, 3000);

// 取消定时器
clearTimeout(timerId);

// 每隔1秒显示当前时间
const clockId = setInterval(() => {
    console.log(new Date().toLocaleTimeString());
}, 1000);

// 停止时钟
// clearInterval(clockId);

可以看到,setTimeout以及setInterval是全局函数,参数都为(要延迟执行的函数,延迟的毫秒数,可选的给延迟执行的函数的参数),返回一个正整数(定时器 ID),用于后续取消(clearTimeout)。

setTimeout 不是 JavaScript 语言本身的一部分(ECMAScript 规范中没有),而是由宿主环境(浏览器或 Node.js)提供的 API。

受事件循环、其他任务执行时间等因素影响,实际延时可能略大于设定值。

3.Location 对象:地址栏管理

location 对象用于获取或设置当前页面的 URL 路径,常用于页面跳转。

例如:

// 获取 URL 信息
console.log(location.href);     // 当前完整 URL
console.log(location.hostname); // 域名 (如: www.google.com)
console.log(location.pathname); // 路径 (如: /search)
console.log(location.search);   // URL 参数 (如: ?id=123)

// 操作跳转
function goToBaidu() {
    location.assign("https://www.baidu.com"); // 正常跳转,可后退
}

function replaceWithBaidu() {
    location.replace("https://www.baidu.com"); // 替换当前记录,不可后退
}

function reloadPage() {
    location.reload(); // 刷新当前页面
}

4.Navigator 对象:浏览器信息

主要用于判断用户使用的设备、浏览器类型或是否在线。

console.log(navigator.userAgent); // 返回浏览器详细信息字符串
console.log(navigator.language);  // 返回浏览器的偏好语言(如 "zh-CN")
console.log(navigator.onLine);    // 返回布尔值,判断是否联网

// 现代 API:剪贴板操作
navigator.clipboard.writeText("复制的内容").then(() => {
    console.log("已复制到剪贴板");
});

注意:
网络状态变化时,onLine 的值会更新,但不要只依赖它做关键判断;可配合 online / offline 事件监听(window.addEventListener(‘online’, …))。

5.History 对象:浏览历史

出于隐私安全,你不能获取用户具体的访问记录 URL,但可以控制页面的前进和后退。

history.back();    // 后退一步,等同于点击浏览器后退按钮
history.forward(); // 前进一步
history.go(-2);    // 后退两步

6. 窗口大小与滚动事件

BOM 还负责处理浏览器窗口本身的物理属性。

// 获取窗口宽高(不含工具栏/滚动条)
const width = window.innerWidth;
const height = window.innerHeight;

// 监听窗口大小变化
window.onresize = function() {
    console.log(`当前尺寸:${window.innerWidth} x ${window.innerHeight}`);
};

// 监听滚动条
window.onscroll = function() {
    console.log("用户正在滚动页面,当前距离顶部:" + window.scrollY + "px");
};

二,DOM (Document Object Model)

DOM(Document Object Model,文档对象模型) 是一个独立于语言的 API,用于表示和操作 HTML/XML 文档的结构。它将文档解析成一个树形结构(节点树),每个元素、属性、文本等都是一个节点,JavaScript 可以通过 DOM 提供的接口来增删改查页面内容。

简单来说,DOM是让js能够操作页面内容的标准。

1.DOM 树结构:万物皆节点

当浏览器加载 HTML 时,它会创建一个对象模型。

  • Document:树的根部,代表整个网页。
  • Element (元素):HTML 标签(如 <div>, <a>)。
  • Text (文本):标签包裹的文字。
  • Attribute (属性):标签的属性(如 href, src)。

2.核心操作:增、删、改、查

学习 DOM,本质上就是学习如何用 JavaScript 操控这棵“树”。

1.查找元素(查)

这是最基础的一步。你想改谁,得先把它“抓”出来。

以下是 DOM 查找元素的主要方法及其特性的表格:

方法 / 属性 返回类型 是否实时(Live) 描述
getElementById(id) 单个元素(Elementnull 不适用(返回单个) 通过元素的 id 属性获取,ID 应在文档中唯一。
getElementsByClassName(className) HTMLCollection ✅ 是 获取所有具有指定类名的元素,返回实时集合。
getElementsByTagName(tagName) HTMLCollection ✅ 是 获取所有指定标签名的元素(如 "div"),返回实时集合。
getElementsByName(name) NodeList ✅ 是(注1) 获取所有 name 属性为指定值的元素,通常用于表单控件。
querySelector(selector) 单个元素(Elementnull 不适用 返回匹配指定 CSS 选择器的第一个元素。
querySelectorAll(selector) NodeList(静态) ❌ 否 返回匹配指定 CSS 选择器的所有元素,返回静态快照。
parentNode / parentElement 单个元素或 null 不适用 获取当前元素的父节点(parentElement 只返回元素节点)。
children HTMLCollection ✅ 是 获取当前元素的所有元素子节点(不包括文本、注释)。
childNodes NodeList(通常实时,取决于实现) ⚠️ 部分浏览器实时 获取当前元素的所有子节点(包括文本、注释等)。
firstElementChild / lastElementChild 单个元素或 null 不适用 获取第一个/最后一个元素子节点
previousElementSibling / nextElementSibling 单个元素或 null 不适用 获取前一个/后一个兄弟元素节点
说明:
  • 实时集合(Live):当文档结构变化时,集合会自动更新,反映最新状态。例如用 getElementsByClassName 获取后,后续添加的新元素如果符合条件,会自动出现在集合中。
  • 静态集合(Static)querySelectorAll 返回的 NodeList 是快照,不会随着 DOM 变化而自动更新。
  • 注1getElementsByName 返回的 NodeList 在大多数现代浏览器中是实时的(但规范未严格要求,实际以浏览器行为为准)。
  • 基于关系的属性(如 parentNodechildren)需要先获取到一个元素,再从该元素出发进行查找。
2.修改内容与样式(改)

抓到元素后,你可以给它“整容”。

以下是 DOM 中 修改内容与样式 的常用方法及属性表格:

类别 属性 / 方法 作用 说明
修改内容 textContent 获取或设置元素内的纯文本内容(不会解析 HTML) 推荐使用,性能好,避免 XSS 风险
innerText 类似于 textContent,但会考虑 CSS 样式(如 display: none)且会触发重排 有性能开销,尽量用 textContent
innerHTML 获取或设置元素内的 HTML 内容(会解析 HTML 标签) 有 XSS 风险,使用时需谨慎过滤
outerHTML 获取或设置元素自身及其子元素的 HTML 内容 替换整个元素时使用
修改属性 setAttribute(name, value) 设置元素的属性值(如 class, id, src 等) 通用方法
removeAttribute(name) 删除元素的指定属性
getAttribute(name) 获取属性值(读取) 属于“查”,但常与“改”配合
直接属性赋值(如 element.id = "newId" 直接修改标准属性 更简洁,推荐用于标准属性
修改类名 className 获取或设置元素的 class 属性(字符串形式) 注意是整个类名字符串
classList.add(className) 添加一个或多个类名 推荐,方便操作单个类
classList.remove(className) 删除一个或多个类名
classList.toggle(className) 如果存在则删除,不存在则添加
classList.replace(oldClass, newClass) 替换类名
classList.contains(className) 检查是否存在(属于“查”)
修改样式 element.style.property = value 修改内联样式(如 element.style.color = "red" 属性名用驼峰式(如 backgroundColor
element.style.cssText = "..." 批量设置内联样式字符串 会覆盖原有内联样式
window.getComputedStyle(element) 获取最终计算样式(只读,属于“查”)
修改表单值 value 属性 获取或设置输入框、文本域等表单元素的值(如 input.value = "新值" 通用
checked 属性 获取或设置复选框/单选框的选中状态(boolean
selectedIndex / selected 控制下拉列表的选中项
修改元素是否存在 remove() 删除当前元素 现代方法
parentNode.removeChild(child) 从父节点中删除子元素 传统方法
appendChild(node) 在父节点末尾添加子节点
insertBefore(newNode, refNode) 在参考节点前插入新节点
replaceChild(newNode, oldNode) 替换子节点
innerHTML = "" 清空元素内容 简单但可能造成内存泄漏
textContent = "" 清空文本内容,保留元素

注意:表格中部分方法也常用于“增”或“删”,因为“改”内容往往涉及替换、删除原有内容再新增。上述内容聚焦于修改的典型操作。

以下是使用的大概示例:

const title = document.querySelector('h1');

// 修改文字内容
title.textContent = "你好,DOM!"; 

// 修改 HTML 结构
title.innerHTML = "<span>带有标签的文字</span>";

// 修改 CSS 样式 (通过 style 属性)
title.style.color = "red";
title.style.fontSize = "30px";

// 修改类名 (最推荐的样式修改方式)
title.classList.add('active'); // 添加类名
title.classList.toggle('highlight'); // 切换类名(有则删,无则加)
3.创建与添加节点(增)

动态地往页面里塞新东西。

以下是 创建与添加节点(增) 常用的方法及属性表格:

类别 方法 / 属性 作用 示例
创建节点 document.createElement(tagName) 创建一个指定标签名的元素节点 const div = document.createElement('div');
document.createTextNode(text) 创建一个文本节点 const text = document.createTextNode('Hello');
document.createDocumentFragment() 创建一个文档片段(批量添加时提升性能) const fragment = document.createDocumentFragment();
cloneNode(deep) 克隆一个节点(deep=true 深度克隆子节点) const copy = element.cloneNode(true);
设置内容 textContent 属性 设置节点的纯文本内容 div.textContent = '文本';
innerHTML 属性 设置节点的 HTML 内容(可创建子节点) div.innerHTML = '<span>hi</span>';
setAttribute(name, value) 设置属性 div.setAttribute('class', 'box');
直接属性赋值 为标准属性赋值 div.id = 'myId';
添加节点 parent.appendChild(child) 将子节点添加到父节点的末尾 list.appendChild(newItem);
parent.insertBefore(newNode, refNode) 在参考节点之前插入新节点 list.insertBefore(newItem, firstItem);
parent.append(...nodesOrStrings) 现代方法,在末尾添加(可接受多个参数,自动转文本) div.append('text', spanEl);
parent.prepend(...nodesOrStrings) 开头添加 list.prepend(newItem);
refNode.before(...nodesOrStrings) 在参考节点之前插入(无需父节点引用) newItem.before(anotherItem);
refNode.after(...nodesOrStrings) 在参考节点之后插入 newItem.after(anotherItem);
refNode.replaceWith(...nodesOrStrings) 替换当前节点 oldItem.replaceWith(newItem);

补充说明:

  • 文档片段(DocumentFragment):常用于批量添加多个节点,避免多次重排。例如:
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 100; i++) {
        const li = document.createElement('li');
        li.textContent = i;
        fragment.appendChild(li);
    }
    ul.appendChild(fragment); // 一次性添加
    
  • appendprependbeforeafterreplaceWith 是较新的 API(现代浏览器均支持),比传统的 appendChild/insertBefore 更灵活。
4. 删除节点(删)

让某个元素消失。

示例在上面有。

const unwanted = document.querySelector('.bad-ad');
unwanted.remove(); // 直接自我毁灭

三、事件(Events)

DOM 操作的核心是事件驱动,即通过触发元素的特定行为来执行对应的代码,从而实现网页的动态交互。

1. 事件的三要素

处理任何事件,都需要先明确以下三个核心点:

  1. 事件源:触发事件的 DOM 元素(如按钮、div、输入框等)。
  2. 事件类型:触发的具体行为(如点击、鼠标经过、键盘按下等)。
  3. 事件处理程序:事件触发后执行的代码(通常是一个回调函数)。
// 示例:点击按钮弹出提示
const btn = document.querySelector('button');  // 1. 事件源
btn.addEventListener('click', function() {     // 2. 事件类型:'click'
    alert('按钮被点击了');                     // 3. 事件处理程序
});

2. 常用的事件类型

以下是常见的 DOM 事件类型分类表格:

常见事件类型

分类 事件名称 触发时机
鼠标事件 click 鼠标点击(按下并松开)
dblclick 鼠标双击
mousedown 鼠标按钮被按下
mouseup 鼠标按钮被松开
mousemove 鼠标在元素上移动
mouseenter 鼠标进入元素区域(不冒泡)
mouseleave 鼠标离开元素区域(不冒泡)
mouseover 鼠标进入元素或其子元素(冒泡)
mouseout 鼠标离开元素或其子元素(冒泡)
contextmenu 右键菜单弹出前
wheel 鼠标滚轮滚动
键盘事件 keydown 键盘按键被按下
keyup 键盘按键被松开
keypress 按键按下并产生字符(已不推荐)
表单事件 submit 表单提交时
reset 表单重置时
input 输入框值改变(实时,包括粘贴、剪切)
change 表单元素值改变且失去焦点时
focus 元素获得焦点(不冒泡)
blur 元素失去焦点(不冒泡)
focusin 元素获得焦点(冒泡)
focusout 元素失去焦点(冒泡)
select 输入框中的文本被选中
窗口/文档事件 load 页面或资源加载完成
DOMContentLoaded DOM 树构建完成(不等图片等资源)
beforeunload 页面即将卸载(可提示用户)
unload 页面正在卸载
resize 窗口大小改变
scroll 滚动条滚动
hashchange URL 哈希值(#后面的部分)改变
pageshow / pagehide 页面显示/隐藏(包括 bfcache 场景)
触摸/指针事件 touchstart 手指触摸屏幕
touchmove 手指在屏幕上移动
touchend 手指离开屏幕
touchcancel 触摸被中断(如弹窗弹出)
pointerdown 指针按下(鼠标/触摸/笔统一)
pointermove 指针移动
pointerup 指针松开
拖拽事件 dragstart 开始拖拽元素
drag 拖拽过程中(持续触发)
dragend 拖拽结束
dragenter 拖拽进入目标区域
dragover 拖拽在目标区域上移动
dragleave 拖拽离开目标区域
drop 在目标区域释放拖拽
剪贴板事件 copy 复制操作
cut 剪切操作
paste 粘贴操作
动画/过渡事件 animationstart CSS 动画开始
animationend CSS 动画结束
transitionstart CSS 过渡开始
transitionend CSS 过渡结束
其他常用 error 资源加载失败或脚本错误
abort 图片或资源加载中断
loadstart / progress 资源加载进度相关(如 XMLHttpRequest)
online / offline 浏览器网络状态改变
visibilitychange 页面可见性改变(切换标签页)

注意:部分事件(如 keypress)已废弃,建议使用 keydown 代替。触摸事件主要用于移动端,指针事件是跨设备的统一方案。

3. 事件绑定的三种方式

A. 行内绑定(不推荐)

直接在 HTML 标签中写 JS 代码。耦合度过高,不利于后期维护。

<button onclick="alert('点击了')">点我</button>

B. 属性绑定(传统方式 / DOM 0 级)

通过获取 DOM 元素后,给其 on 开头的属性赋值。

btn.onclick = function() { console.log('第一次绑定'); };
btn.onclick = function() { console.log('第二次绑定'); }; // 会覆盖前者,只执行这个

特点:简单直观,但同一个元素的同一个事件只能绑定一个处理函数

C. 事件监听器(现代标准 / 强烈推荐)

使用 addEventListener 方法绑定。

btn.addEventListener('click', function() { console.log('处理程序 A'); });
btn.addEventListener('click', function() { console.log('处理程序 B'); });
// 两个函数都会按绑定顺序依次执行,互不干扰

语法元素.addEventListener('事件类型', 回调函数, [可选:是否在捕获阶段执行])

4. 事件对象 (Event Object)

当事件触发时,浏览器会自动创建一个事件对象,包含了事件发生时的所有“现场信息”(如鼠标位置、按下的按键等)。这个对象会作为参数,自动传递给你的事件处理函数。

btn.addEventListener('click', function(e) {   // e(或 event)就是事件对象
    console.log(e.type);       // 输出事件类型:"click"
    console.log(e.target);     // 真正触发事件的最深层元素(目标元素)
    console.log(e.clientX);    // 鼠标点击处相对于视口左上角的 X 坐标(像素)
    console.log(e.clientY);    // 对应的 Y 坐标
});

最常用的属性:

  • e.target:事件实际发生的元素(事件源)。
  • e.currentTarget:当前绑定了监听器的元素(通常等同于 this)。
  • e.key:键盘事件中按下的具体键值(如 "Enter""Escape")。

5. 事件流:捕获与冒泡

当你点击一个嵌套在内部的按钮时,你实际上也点击了它的外层容器、<body> 乃至整个 window。浏览器处理这个事件传递的过程分为三个阶段:

  1. 捕获阶段 (Capturing):事件从 window → document → → → … → 目标元素的父元素,逐级向下传递。这个阶段一般很少用到,但可以通过 addEventListener 的第三个参数 true 来监听捕获阶段的事件。
  2. 目标阶段 (Target):事件到达真正触发的元素。
  3. 冒泡阶段 (Bubbling):事件从目标元素开始,一层层向上返回到 window

提示:默认情况下,addEventListener 监听的是冒泡阶段。如果想在捕获阶段拦截事件,需要传入第三个参数 true(例如:parent.addEventListener('click', fn, true))。

6. 核心控制方法:阻止冒泡与阻止默认行为

这两个方法是处理复杂交互时的“利器”,务必区分清楚:

A. 阻止事件冒泡:e.stopPropagation()

切断事件流,阻止事件继续向上(或向下)传播,防止意外触发父元素上的同类事件。

btn.addEventListener('click', function(e) {
    e.stopPropagation();   // 事件到此为止,父元素的 click 不会被触发
    console.log('按钮被点击,冒泡已切断');
});

B. 阻止默认行为:e.preventDefault()

拦截浏览器对当前元素的自带行为(如:点击链接不跳转、提交表单不刷新页面、右键不弹出菜单)。它不会影响事件的冒泡传播

const link = document.querySelector('a');
link.addEventListener('click', function(e) {
    e.preventDefault();    // 链接点击后不再跳转网页
    console.log('链接被点击,但默认跳转已被阻止');
});

7. 综合实战:表单验证与错误提示

结合以上所有知识点,实现一个拦截空表单提交的常见需求:

<form id="myForm">
    <input type="text" id="username" placeholder="请输入用户名">
    <button type="submit">提交</button>
</form>
<div id="errorMsg" style="color: red;"></div>

const form = document.getElementById('myForm');
const username = document.getElementById('username');
const errorDiv = document.getElementById('errorMsg');

// 监听表单的提交事件
form.addEventListener('submit', function(e) {
    // 1. 阻止表单提交导致页面刷新的默认行为
    e.preventDefault();
    
    // 2. 验证逻辑
    if (username.value.trim() === '') {
        errorDiv.innerText = '用户名不能为空!';
    } else {
        errorDiv.innerText = '验证通过,准备发送数据...';
        // 此处可编写 AJAX 请求逻辑发送数据
    }
});

// 监听输入框获得焦点事件
username.addEventListener('focus', function() {
    errorDiv.innerText = '';   // 用户重新输入时,清空之前的错误提示
});

8. 总结:核心概念速查

概念 核心意义 关键代码/方法
事件源 触发事件的源头元素 e.target
事件类型 用户执行的动作名称 clickmouseoverkeydown
事件处理 如何给元素绑定响应代码 推荐 addEventListener
事件流 事件在 DOM 树中传递的路径 捕获阶段 → 目标阶段 → 冒泡阶段
控制流向 阻止事件向父元素传递 e.stopPropagation()
控制行为 阻止浏览器的自带默认动作 e.preventDefault()

💡 黄金记忆口诀:
不想跳转不刷新,快用 preventDefault
避免父级受牵连,记得 stopPropagation

更多推荐