DOM树构建从字节流开始,而非字符串;浏览器先解码字节流为字符,再分词生成token,最后构建节点并形成树结构。

DOM树构建从字节流开始,不是从字符串开始
浏览器拿到的原始数据是字节流(bytes),不是你复制粘贴的字符串。编码识别错误、BOM头残留、传输截断,都会让后续所有步骤失效。比如 UTF-8 编码下误用 GBK 解析,<div> 可能变成乱码 token,解析器直接放弃建树。
实操建议:
- 用
curl -sI URL检查响应头Content-Type是否含text/html; charset=utf-8,缺 charset 或值错误会触发默认编码(通常是 ISO-8859-1) - 用
curl -s URL | hexdump -C | head看前几个字节:EF BB BF 是 UTF-8 BOM,若源码没写 BOM 却出现,说明中间代理加了头 - 服务端返回前确保调用
response.setCharacterEncoding("UTF-8")(Java)或设置Content-Type: text/html; charset=utf-8(Node.js/Python)
tokenization 阶段出错,DOM树就永远建不起来
HTML 解析器第一步是把字节转字符,再切分成 token(如 startTag、endTag、character)。一旦 tokenizer 卡住——比如遇到 <script>alert( 后面突然断网,或 <!--[if IE]> 这类条件注释未闭合——整个解析流程就停在那,document.documentElement 为 null,连 <html> 都没创建。
常见现象:
立即学习“前端免费学习笔记(深入)”;
- Network 面板里 HTML 请求状态是 200,但 Elements 面板空空如也,Console 执行
document.documentElement返回null -
document.querySelector('script[src]')返回null,但源码明明写了外链脚本——说明 script 标签根本没被 tokenized - 页面只渲染了
<head>里的<title>,<body>内容完全消失
栈式语法分析器如何处理标签嵌套与纠错
DOM 构建核心是一个基于栈的语法分析器。它不校验语义合法性,只按规则“尽力而为”地拼出一棵树。例如 <p><div>test</div></p> 不会报错,而是立刻关闭 <p>,把 <div> 插到其后,再补一个空 <p></p>。
这意味着你写的结构 ≠ 浏览器生成的 DOM 结构。关键点:
- 所有非法嵌套(
<ul><div></div></ul>、<table><p></p></table>)都会触发静默修正,Elements 面板显示的是修正结果 - 用
document.querySelectorAll('*').forEach(el => !el.parentElement && console.log(el))能快速揪出被“踢出来”的孤立节点 - W3C Validator 报的首个
Element xxx not allowed as child of element yyy错误,必须优先修复——后面几十个 warning 很可能是连锁修正导致的
document 对象才是真·根节点,不是 <html>
很多人以为 <html> 是 DOM 树根,其实 document 才是。它是解析启动时第一个创建的对象(nodeType === 9),<html> 只是它的第一个子节点。这个细节影响所有动态操作逻辑。
典型陷阱:
-
document.body在<body>标签解析完成前为null;若同步脚本放在<head>里执行document.body.appendChild(...),会报错并中断解析 -
document.childNodes可能包含DocumentType节点(即<!DOCTYPE html>)、注释、<html>,顺序取决于源码书写,不能假设document.childNodes[0]就是<html> - 服务端库如
cheerio默认以<html>为顶层,cheerio.root()模拟document但不暴露doctype,和浏览器行为不一致



















