dom 是浏览器解析 html 后构建的内存对象树,document 为根节点(nodetype===9),html、body、head 均非其直接子节点;childnodes 包含所有节点类型,children 仅含元素节点,推荐用 children.length 判断子元素存在性。

DOM 不是 HTML 文档的“副本”,而是浏览器解析 HTML 后构建的一套内存中可操作的对象树;你写的 document.getElementById 或 element.children 都是在跟这棵树打交道,不是在读源码。
document 才是根节点,不是 html
很多人用 document.childNodes 查看时发现开头有空白或注释节点,就以为是写错了——其实这是正常现象。因为 document 是真正的根(nodeType === 9),而 document.documentElement(即 )只是它的一个子元素(nodeType === 1)。document.body 和 document.head 更是“快捷属性”,实际是 document.documentElement 的子元素,不是 document 的直接子节点。
这意味着:
- 遍历
document.childNodes时,第一个节点可能是文本节点(比如 DOCTYPE 前的换行或空格),node.textContent可能为空字符串或null,直接取值容易报错 - 想判断一个容器有没有子元素,别用
childNodes.length > 0,改用children.length > 0——它只返回元素节点,更可靠 -
document.querySelector('html')和document.documentElement等价,但前者多走一次 CSS 解析,没必要
childNodes 和 children 的差异不只是“要不要文本”
这个区别在调试时经常被轻视,但一到真实项目里就出问题。比如你写了个函数,想清空某个 <div> 下所有“可见内容”,用了 <code>el.childNodes.forEach(n => n.remove()),结果发现事件监听器还在、表单值没重置——因为 childNodes 包含了文本节点和注释节点,但不包含动态插入的 DOM 节点(如通过 innerHTML 插入后又修改的),而且移除文本节点不会触发元素自身的重绘逻辑。
更关键的是:
-
children是实时集合(HTMLCollection),只含nodeType === 1的元素节点,适合做结构判断 -
childNodes是普通 NodeList,包含所有类型节点;IE 下甚至可能包含 CDATA 节点 - 想过滤掉空白文本节点?别手写
filter(n => n.nodeType === 1),直接用children更简洁、性能更好
为什么 getElementById 快,但不该盲目依赖它
浏览器对 ID 查找做了底层哈希索引,所以 document.getElementById 几乎是 O(1);而 querySelector 要走完整的 CSS 解析 + 树遍历,开销明显更大。但这不等于你应该把它当首选。
现实问题是:
- ID 在 SPA 或组件化场景下极易重复(比如多个 React 组件都渲染了
id="modal"),getElementById只返回第一个匹配项,且不报错 -
querySelector支持作用域查找:比如container.querySelector('.item[data-id="42"]'),天然隔离局部上下文 - 需要向上找父级时,
element.closest('.form')比反复调用parentNode安全得多,也更语义化
innerHTML 和 textContent 修改内容时,副作用完全不同
两者看起来都是“改文字”,但底层机制完全不在一个量级。
innerHTML 会触发 HTML 解析 → 创建新节点 → 替换旧子树 → 销毁原事件监听器 → 触发重排重绘;textContent 只是把节点内所有子文本节点合并成一个纯字符串再设进去,不重建节点,也不影响事件绑定。
所以:
- 只想更新显示文字?优先用
textContent,防 XSS 且零副作用 - 要插入带标签的富文本?必须用
innerHTML,但记得先做白名单过滤,别直接拼接用户输入 - 用
innerHTML += ...是典型反模式:每次执行都会重建整个子树,性能爆炸,还丢事件
DOM 结构的真实复杂性,往往藏在看似最简单的节点关系里——比如你以为 document.body 是 document 的孩子,其实它中间隔了一层 ;又比如你删光了 childNodes,却忘了 children 还在,表单控件状态依然存在。这些不是边缘情况,而是日常踩坑的高频点。











