Article

DOM 操作速查文档

更新于:2026-07-08

一、DOM 基础概念

1.1 什么是 DOM

概念名称说明注意事项
DOM(Document Object Model)文档对象模型,是 HTML 和 XML 文档的编程接口。它将整个文档表示为一个由节点和对象组成的树结构,允许程序或脚本动态访问和更新文档的内容、结构和样式。DOM 不是 JavaScript 的一部分,而是由浏览器实现的 Web API;不同浏览器对 DOM 的实现可能略有差异。
DOM 与 HTML 的关系HTML 是静态标记语言,而 DOM 是浏览器在加载 HTML 后构建的动态内存中的对象模型。JavaScript 通过操作 DOM 来改变页面内容。修改 DOM 会触发页面重排(reflow)和重绘(repaint),影响性能,应尽量减少频繁操作。
DOM 标准由 W3C 制定,定义了节点、方法、属性等规范。现代浏览器遵循 DOM Level 4 或更高标准。开发时建议查阅 MDN Web Docs 获取最新兼容性信息。

1.2 DOM 树结构与节点类型

节点类型说明注意事项
Document 节点整个文档的根节点,代表 HTML 文档本身。可通过 document 全局变量访问。只有一个 Document 节点,是所有其他节点的祖先。
Element 节点表示 HTML 元素(如 <div><p>)。是最常用的节点类型。所有 Element 节点都继承自 HTMLElement 接口,具有 idclassNamestyle 等属性。
Text 节点表示元素内的文本内容。例如 <p>Hello</p> 中的 “Hello” 是一个 Text 节点。空白字符(如换行、缩进)也会生成 Text 节点,遍历时需注意过滤。
Comment 节点表示 HTML 注释(<!-- comment -->)。通常用于调试或标记,不会渲染到页面上。
Attribute 节点(已弃用)在早期 DOM 中,属性被视为节点,但现代 DOM 中属性通过 Element 的方法(如 getAttribute)访问,不再作为独立节点。不建议使用 attributes 集合作为主要操作方式,优先使用属性方法。
DocumentFragment 节点轻量级文档容器,用于临时存放多个节点,避免多次重排。插入后自身不保留,只插入其子节点,常用于批量 DOM 操作优化。

1.3 节点关系(父子兄弟)

关系属性/方法语法用途注意事项
parentNodenode.parentNode返回当前节点的父节点。若节点为 Document,则返回 null。
childNodesnode.childNodes返回包含所有子节点(包括 Text、Comment 等)的 NodeList。NodeList 是动态集合,DOM 变化时自动更新;包含非元素节点,需用 nodeType 过滤。
firstChild / lastChildnode.firstChild
node.lastChild
分别返回第一个和最后一个子节点。可能返回 Text 节点(如空白),若只想获取元素节点,应使用 firstElementChild / lastElementChild
nextSibling / previousSiblingnode.nextSibling
node.previousSibling
返回下一个或上一个同级节点。同样可能指向非元素节点,建议使用 nextElementSibling / previousElementSibling 获取元素兄弟。
childrennode.children返回仅包含 Element 子节点的 HTMLCollection。childNodes 更常用,因为只包含元素节点。
parentElementnode.parentElement返回当前节点的父元素节点(仅 Element 类型)。对于 Document 或 DocumentFragment 节点,返回 null。

二、元素选择与查询

2.1 通过 ID 获取元素

方法名称语法用途代码示例注意事项
getElementByIddocument.getElementById(id)根据唯一 ID 获取单个元素节点。const el = document.getElementById('myDiv');ID 在文档中应唯一,若重复,只返回第一个匹配项
返回 null 若未找到
不区分大小写在 HTML 中无效(HTML 规范要求 ID 区分大小写)

2.2 通过标签名、类名获取元素

方法名称语法用途代码示例注意事项
getElementsByTagNamedocument.getElementsByTagName(tagName)
element.getElementsByTagName(tagName)
根据标签名获取所有匹配的元素集合(HTMLCollection)。const divs = document.getElementsByTagName('div');返回动态 HTMLCollection(DOM 变化时自动更新)
tagName 不区分大小写(如 'DIV' 等同于 'div'
可在任意 Element 上调用,仅搜索其子树
getElementsByClassNamedocument.getElementsByClassName(classNames)
element.getElementsByClassName(classNames)
根据一个或多个类名获取元素集合(HTMLCollection)。const items = document.getElementsByClassName('item active');classNames 是空格分隔的字符串,表示同时包含这些类
返回动态 HTMLCollection
不支持复杂选择器(如伪类、属性等)
IE9+ 支持

2.3 使用 CSS 选择器查询(querySelector / querySelectorAll)

方法名称语法用途代码示例注意事项
querySelectordocument.querySelector(selector)
element.querySelector(selector)
使用 CSS 选择器语法获取第一个匹配的元素。const firstBtn = document.querySelector('.btn.primary');返回 null 若未找到
支持所有 CSS3 选择器(如 :nth-child[attr=value]>+ 等)
性能略低于 getElementById,但功能强大
querySelectorAlldocument.querySelectorAll(selector)
element.querySelectorAll(selector)
使用 CSS 选择器语法获取所有匹配的元素。const allInputs = document.querySelectorAll('input[type="text"]');返回静态 NodeList(不会随 DOM 更新而变化)
即使只有一个元素也返回 NodeList,需用 [0] 或解构访问
不支持 CSS 伪元素(如 ::before
选择器语法错误会抛出 DOMException

三、节点内容与属性操作

3.1 innerHTML / innerText / textContent

属性名称语法用途代码示例注意事项
innerHTMLelement.innerHTML
element.innerHTML = htmlString
获取或设置元素的 HTML 内容(包含标签)。div.innerHTML = '<p>Hello <strong>World</strong></p>';设置时会解析 HTML 字符串并重建子节点
存在 XSS 风险,不可用于插入用户输入内容
获取时返回序列化的 HTML 字符串
innerTextelement.innerText
element.innerText = text
获取或设置元素的”可见”文本内容(受 CSS 影响,如 display:none 的内容不包含)。span.innerText = 'New visible text';受样式影响(如隐藏元素的文本不会被读取)
设置时会转义 HTML 特殊字符(安全)
非标准起源(IE),但现代浏览器广泛支持
会触发重排(reflow)
textContentelement.textContent
element.textContent = text
获取或设置元素的所有文本内容(包括 script/style 和隐藏元素),忽略 HTML 标签。code.textContent = '<script>alert(1)</script>'; // 显示为纯文本不解析 HTML,完全安全
不受 CSS 影响(即使元素隐藏也会读取)
性能优于 innerText(不触发重排)
推荐用于纯文本操作

3.2 操作元素属性(getAttribute / setAttribute / removeAttribute)

方法名称语法用途代码示例注意事项
getAttributeelement.getAttribute(name)获取指定属性的字符串值。const href = link.getAttribute('href');返回字符串,即使属性是布尔值(如 disabled 返回 "disabled" 或 null)
可获取自定义属性(如 data-*
setAttributeelement.setAttribute(name, value)设置指定属性的值(自动转为字符串)。img.setAttribute('src', 'image.jpg');若属性已存在则覆盖,否则新增
布尔属性(如 checked)需设为任意非空字符串才生效(建议用 true/false 字符串)
removeAttributeelement.removeAttribute(name)移除指定属性。button.removeAttribute('disabled');移除后属性不再存在,hasAttribute 返回 false
对布尔属性特别有效(如移除 disabled 使按钮可用)
hasAttributeelement.hasAttribute(name)检查元素是否具有指定属性。if (input.hasAttribute('required')) { ... }返回布尔值
常用于条件判断

3.3 操作样式(style / classList)

(1)直接操作内联样式(style 属性)

用法语法用途代码示例注意事项
设置单个样式element.style.property = value设置内联 CSS 样式(驼峰命名)。div.style.backgroundColor = 'red';
div.style.fontSize = '16px';
属性名需用 camelCase(如 background-colorbackgroundColor
仅影响内联样式,优先级高但难以维护
获取时只返回内联样式,不反映外部 CSS 计算值
获取计算样式window.getComputedStyle(element)获取元素最终应用的所有 CSS 属性值。const styles = getComputedStyle(div);
console.log(styles.color);
返回只读 CSSStyleDeclaration 对象
包含继承和外部样式表计算后的值
性能开销较大,避免频繁调用

(2)操作 CSS 类(classList 属性)

方法名称语法用途代码示例注意事项
addelement.classList.add(className)添加一个或多个 CSS 类。btn.classList.add('active', 'highlight');自动去重,重复添加无效
支持多个参数(ES6+)
removeelement.classList.remove(className)移除一个或多个 CSS 类。btn.classList.remove('disabled');若类不存在,静默忽略
可传多个类名
toggleelement.classList.toggle(className, [force])切换类的存在状态(有则删,无则加)。menu.classList.toggle('open');第二个参数 force 为布尔值:true 强制添加,false 强制移除
返回操作后是否包含该类(true/false)
containselement.classList.contains(className)检查是否包含指定类。if (el.classList.contains('error')) { ... }返回布尔值
常用于状态判断
replaceelement.classList.replace(oldClass, newClass)替换一个类为另一个。item.classList.replace('loading', 'loaded');oldClass 不存在,则不添加 newClass
IE 不支持(需 polyfill)

四、创建与插入节点

4.1 创建元素节点与文本节点

方法名称语法用途代码示例注意事项
createElementdocument.createElement(tagName)创建一个新的 HTML 元素节点。const div = document.createElement('div');tagName 不区分大小写
返回的元素尚未加入 DOM 树,需通过插入方法添加
可随后设置属性、内容等
createTextNodedocument.createTextNode(text)创建一个包含指定文本的文本节点。const textNode = document.createTextNode('Hello');text 会被转义为纯文本(安全)
常用于避免 innerHTML 的 XSS 风险
可作为子节点插入到元素中
createDocumentFragmentdocument.createDocumentFragment()创建一个轻量级的文档片段容器,用于临时存放多个节点。const frag = document.createDocumentFragment();
frag.appendChild(div);
插入到 DOM 时只插入其子节点,自身不保留
批量操作可减少重排次数,提升性能

4.2 插入节点(appendChild / insertBefore)

方法名称语法用途代码示例注意事项
appendChildparent.appendChild(child)将子节点添加到父节点的最后一个子节点之后。container.appendChild(newDiv);若 child 已存在于 DOM 中,则会移动而非复制
返回被插入的节点
支持 DocumentFragment(插入其所有子节点)
insertBeforeparent.insertBefore(newNode, referenceNode)在指定参考节点之前插入新节点。list.insertBefore(newItem, list.firstChild);referenceNode 不能为 null(否则行为未定义)
referenceNode 为第一个子节点,则 newNode 成为新的第一个子节点
newNode 已存在,则先移除再插入
prepend (现代方法)parent.prepend(...nodes)将节点或字符串插入到父节点的开头。header.prepend(titleNode, ' - ');支持多个参数(节点或字符串)
字符串自动转为 Text 节点
IE 不支持,需 polyfill 或使用 insertBefore
append (现代方法)parent.append(...nodes)将节点或字符串插入到父节点的末尾。footer.append(copyrightText);类似 appendChild,但支持多个参数和字符串
比 appendChild 更灵活
IE 不支持

4.3 替换与克隆节点

方法名称语法用途代码示例注意事项
replaceChildparent.replaceChild(newChild, oldChild)用新节点替换父节点中的旧子节点。container.replaceChild(updatedDiv, oldDiv);oldChild 必须是 parent 的直接子节点,否则抛出异常
返回被替换的旧节点
newChild 若已存在于 DOM 中,会被先移除再插入
cloneNodenode.cloneNode([deep])克隆一个节点。const copy = original.cloneNode(true);参数 deep 为布尔值:true 表示深拷贝(包括所有子节点),false 仅克隆当前节点
不会复制事件监听器(除非使用 addEventListener 添加且未标记 passive 等特殊情况)
克隆后的节点 ID 可能重复,需手动处理
remove (现代方法)node.remove()从 DOM 中移除当前节点。obsoleteElement.remove();等效于 node.parentNode.removeChild(node)
更简洁,无需访问父节点
IE 不支持,传统方式需用 removeChild

五、删除与遍历节点

5.1 删除节点(removeChild / remove)

方法名称语法用途代码示例注意事项
removeChildparent.removeChild(child)从父节点中移除指定的子节点。const removed = container.removeChild(item);child 必须是 parent 的直接子节点,否则抛出 NotFoundError
返回被移除的节点
移除后该节点仍存在于内存中,可重新插入
removenode.remove()直接从其父节点中移除当前节点(现代方法)。obsoleteDiv.remove();无需显式访问父节点,语法更简洁
等效于 node.parentNode?.removeChild(node)
IE 不支持,在旧项目中需使用 removeChild

5.2 遍历子节点与兄弟节点

遍历方式语法用途代码示例注意事项
childNodesnode.childNodes获取所有子节点(包括 Text、Comment 等)的 NodeList。for (let i = 0; i < el.childNodes.length; i++) { ... }包含非元素节点(如空白文本)
是动态集合(DOM 变化时自动更新)
遍历时建议检查 nodeType === Node.ELEMENT_NODE(值为 1)过滤
childrennode.children获取仅包含 Element 子节点的 HTMLCollection。Array.from(el.children).forEach(child => ...)只包含元素节点,更常用
动态集合
不包含 Text/Comment 节点
firstChild / lastChildnode.firstChild
node.lastChild
获取第一个或最后一个子节点(任意类型)。const first = list.firstChild;可能返回 Text 节点(如换行符)
若只想获取元素节点,应使用 firstElementChild / lastElementChild
firstElementChild / lastElementChildnode.firstElementChild
node.lastElementChild
获取第一个或最后一个元素子节点。const firstItem = ul.firstElementChild;若无元素子节点,返回 null
推荐用于结构化遍历
nextSibling / previousSiblingnode.nextSibling
node.previousSibling
获取下一个或上一个同级节点(任意类型)。let sibling = current.nextSibling;可能指向空白 Text 节点
建议使用 nextElementSibling / previousElementSibling 获取元素兄弟
nextElementSibling / previousElementSiblingnode.nextElementSibling
node.previousElementSibling
获取下一个或上一个元素兄弟节点。while (el = el.nextElementSibling) { ... }忽略非元素节点,适合 DOM 结构遍历
若无对应兄弟,返回 null

5.3 文档片段(DocumentFragment)优化批量操作

操作步骤操作细节注意事项
创建文档片段使用 document.createDocumentFragment() 创建一个轻量级容器。DocumentFragment 不是真实 DOM 节点,不会触发重排/重绘
初始时无子节点
向片段添加节点使用 appendChildappend 等方法将多个节点加入片段。所有操作在内存中进行,性能高
可混合添加元素、文本等节点
将片段插入 DOM将整个 DocumentFragment 插入目标父节点(如 parent.appendChild(fragment))。插入时只插入片段的子节点,DocumentFragment 自身不保留
仅触发一次重排,显著优于逐个插入
适用于列表渲染、动态表单生成等批量场景
方法/属性语法用途代码示例注意事项
createDocumentFragmentconst frag = document.createDocumentFragment();创建文档片段实例。const frag = document.createDocumentFragment();返回一个空的 DocumentFragment 对象
appendChild (到片段)frag.appendChild(node)向片段添加子节点。items.forEach(item => frag.appendChild(createItem(item)));节点被”转移”到片段中(若原在 DOM 中则被移除)
append (到片段,现代)frag.append(...nodes)批量添加节点或文本到片段。frag.append(div1, textNode, 'static text');支持字符串(自动转为 Text 节点)
更简洁,但 IE 不支持

六、事件处理基础

6.1 添加/移除事件监听(addEventListener / removeEventListener)

方法名称语法用途代码示例注意事项
addEventListenertarget.addEventListener(type, listener, [options])为元素注册一个事件监听器。button.addEventListener('click', handleClick);type 是不带 “on” 前缀的事件名(如 'click'
listener 必须是函数(不能是字符串)
支持多个监听器绑定同一事件
默认在冒泡阶段触发
removeEventListenertarget.removeEventListener(type, listener, [options])移除已注册的事件监听器。button.removeEventListener('click', handleClick);必须传入完全相同的函数引用才能移除
匿名函数无法被移除
options 参数必须与 addEventListener 中一致(若使用)
options 参数(可选){ capture: boolean, once: boolean, passive: boolean }控制监听器行为。el.addEventListener('scroll', handler, { passive: true });capture: true 表示在捕获阶段调用(默认 false)
once: true 表示触发一次后自动移除
passive: true 表示监听器不会调用 preventDefault()(提升滚动性能)

6.2 常见 DOM 事件类型(click, input, load 等)

事件类型触发时机典型使用场景注意事项
click用户点击元素(按下并释放)按钮点击、菜单交互在 touch 设备上可能有 ~300ms 延迟(可通过 CSS touch-action: manipulation 或库解决)
dblclick用户双击元素打开/编辑项不常用,需注意与 click 冲突(双击会触发两次 click)
input<input><textarea><select> 的值发生变化时实时搜索、表单验证在用户输入过程中持续触发(比 change 更即时)
不冒泡(但实际浏览器中通常冒泡)
change表单控件失去焦点且值已改变提交表单前校验<select> 选择即触发;对文本输入需失焦才触发
load资源(如 window、img、script)加载完成图片懒加载完成、页面初始化window.onload 等待所有资源(包括图片)加载完毕
<img>.onload 可用于检测图片是否加载成功
DOMContentLoadedHTML 文档完全加载和解析完成(不等待样式表、图片等)初始化 DOM 操作window.onload 更早触发
推荐在此事件中执行 JS 初始化逻辑
keydown / keyup键盘按键按下 / 释放快捷键、游戏控制keydown 可检测组合键(如 Ctrl+K)
event.keyevent.code 获取按键信息
focus / blur元素获得 / 失去焦点表单高亮、输入提示focus 不冒泡(但可用 focusin/focusout 替代)
只有可聚焦元素(如 input、button)能触发
submit表单提交时(通过按钮或回车)表单数据验证与提交可在监听器中调用 event.preventDefault() 阻止默认提交
绑定在 <form> 元素上

6.3 事件对象与冒泡/捕获机制简介

(1)事件对象(Event Object)常用属性与方法

属性/方法说明代码示例注意事项
event.type事件类型(如 'click'console.log(e.type);字符串,只读
event.target实际触发事件的元素(最底层元素)if (e.target.matches('.btn')) { ... }与 this(当前监听器绑定的元素)可能不同(因事件冒泡)
event.currentTarget当前事件监听器所绑定的元素console.log(e.currentTarget === this); // true始终等于监听器中的 this
event.preventDefault()阻止事件的默认行为e.preventDefault(); // 阻止链接跳转如阻止表单提交、链接跳转等
被标记为 passive: true 的监听器中调用无效
event.stopPropagation()阻止事件继续冒泡(或捕获)e.stopPropagation();不影响同一元素上的其他监听器
无法阻止已触发的默认行为
event.stopImmediatePropagation()阻止后续监听器执行 + 阻止冒泡e.stopImmediatePropagation();同一事件类型在同一元素上的后续监听器不会执行

(2)事件流:捕获 vs 冒泡

阶段顺序说明使用方式
捕获阶段(Capture)从 window → document → … → 目标父元素事件向下传递到目标元素addEventListener(type, fn, true){ capture: true }
目标阶段(Target)到达目标元素执行目标元素上的监听器无论 capture 为何值都会执行
冒泡阶段(Bubble)从目标父元素 → … → document → window事件向上传播默认行为(capture: false)
注意事项
大多数事件支持冒泡(如 click、input),但有些不支持(如 focus、blur)
推荐优先使用冒泡阶段,除非有特殊需求(如全局拦截)
可通过 event.eventPhase 判断当前阶段(1=捕获,2=目标,3=冒泡)