← [ BACK_TO_NOTES ]

DOM(文档对象模型)

DOM 是浏览器把文档暴露为节点树的 Web Platform 模型;本页维护节点、树关系、变更和事件目标等稳定概念,JavaScript 操作细节由专门笔记承接。

#frontend #browser #concept #growing

[!info] related notes

DOM(文档对象模型)

定义

DOM(Document Object Model)是 Web Platform 用对象和节点关系表示文档的编程模型。HTML 解析通常会产生一棵 DOM 树,但 DOM 不等同于“HTML 源码本身”,也不等同于某一种编程语言的 API 调用方式。

边界:DOM 模型 vs JavaScript 操作

本页回答“文档树由哪些节点组成、节点之间是什么关系、变更和事件目标如何依附在树上”。JavaScript DOM 操作 回答“在 JavaScript 代码里具体怎样查询、创建、插入节点,以及怎样处理动态脚本加载”。

因此:

  • Document / Node / Element 的关系属于本页;
  • querySelector()、createElement() 的工程用法主要属于 JavaScript DOM 操作;
  • DOM 变化可能导致后续 style/layout/paint 工作,但 DOM 本身不是渲染树,见 浏览器渲染流程;
  • 事件传播以 DOM 节点关系为基础,但 capture/bubble 规则统一看 前端事件传播。

DOM 树结构

HTML 文档被解析为一棵节点树:

<html>
  <head>
    <title>页面标题</title>
  </head>
  <body>
    <h1>标题</h1>
    <p class="intro">一段文字</p>
  </body>
</html>

对应的 DOM 树:Document → html → {head → title → "页面标题", body → {h1 → "标题", p.intro → "一段文字"}}。

节点类型

类型nodeType示例
Element1<div>, <p>
Text3文字内容
Comment8<!-- 注释 -->
Document9document
DocumentFragment11文档片段
Attr2class="card"

获取元素

// 推荐:现代 API
document.querySelector('.card')         // 第一个匹配
document.querySelectorAll('.card')      // 所有匹配(NodeList)

// 传统 API(更快,但灵活性低)
document.getElementById('app')
document.getElementsByClassName('card')  // HTMLCollection(动态)
document.getElementsByTagName('div')
document.getElementsByName('username')

querySelectorAll 返回静态 NodeList,不会随 DOM 变化;getElementsByClassName 返回动态 HTMLCollection。

遍历 DOM

const el = document.querySelector('.card')

// 父节点
el.parentNode          // 父节点(可能是 Text 节点)
el.parentElement       // 父元素节点

// 子节点
el.childNodes          // 所有子节点(包括 Text、Comment)
el.children            // 只有元素子节点
el.firstChild          // 第一个子节点
el.firstElementChild   // 第一个元素子节点
el.lastChild           // 最后一个子节点

// 兄弟节点
el.nextSibling
el.nextElementSibling
el.previousSibling
el.previousElementSibling

修改 DOM

const div = document.createElement('div')
div.textContent = '新内容'
div.className = 'card'

// 插入
parent.appendChild(child)
parent.insertBefore(newNode, referenceNode)
parent.append(child1, child2)         // 可追加多个,可追加字符串
parent.prepend(child)
parent.after(sibling)
parent.before(sibling)

// 删除
parent.removeChild(child)
child.remove()                        // 更简洁

// 替换
parent.replaceChild(newChild, oldChild)

// 修改属性
el.setAttribute('id', 'app')
el.getAttribute('id')
el.removeAttribute('id')
el.dataset.userId = '123'             // data-user-id="123"

// 修改样式
el.style.color = 'red'
el.classList.add('active')
el.classList.remove('active')
el.classList.toggle('active')
el.classList.contains('active')

DocumentFragment

DocumentFragment 是轻量的文档片段,不会触发重排,适合批量插入。

const fragment = document.createDocumentFragment()

for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li')
  li.textContent = `Item ${i}`
  fragment.appendChild(li)
}

// 一次性插入,只触发一次重排
ul.appendChild(fragment)

事件委托

利用事件冒泡机制,将事件监听器绑定在父元素上,通过 event.target 判断实际触发的子元素。

// 不推荐:给每个按钮都绑定事件
document.querySelectorAll('.btn').forEach(btn => {
  btn.addEventListener('click', handleClick)
})

// 推荐:事件委托
document.querySelector('.btn-container').addEventListener('click', (e) => {
  if (e.target.matches('.btn')) {
    handleClick(e)
  }
})

优势:

  • 减少事件监听器数量
  • 动态添加的子元素自动被处理
  • 内存占用更少

详见 前端事件传播。

innerHTML vs textContent

el.innerHTML = '<b>粗体</b>'   // 解析 HTML,有 XSS 风险
el.textContent = '<b>粗体</b>' // 纯文本,安全
el.innerText = '内容'          // 受 CSS 影响(display: none 的不返回)

textContent 性能更好且更安全,优先使用。

边界

  • DOM 操作是昂贵的,频繁操作会触发 reflow/repaint
  • 批量操作时使用 DocumentFragment 或 innerHTML 一次性写入
  • querySelectorAll 返回的 NodeList 不是数组,需要用 Array.from() 或展开运算符转数组
  • DOM 事件监听器在元素移除后不会自动销毁(可能内存泄漏),需要手动 removeEventListener 或使用 AbortController
  • document.readyState 可以检查 DOM 是否加载完成

一句话记忆法

DOM 是浏览器暴露出来的文档对象树;它定义“页面结构是什么”,而 JavaScript DOM API 负责“代码怎样操作这棵树”。

学习检查

  1. 为什么 DOM 不能简单等同于 HTML 源码?
  2. Node、Element、Document 分别处于什么抽象层?
  3. DOM 树和渲染树为什么不是同一个数据结构?
  4. 事件冒泡为什么依赖节点关系,但又不属于“DOM 查询 API”本身?
  5. 如果问题是“怎样动态加载一个 <script> 并处理失败”,为什么应该转到 JavaScript DOM 操作?
创建于 2026/5/27 更新于 2026/10/1
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本