容错恢复不是“猜”,而是按HTML Living Standard状态机执行:解析器依据插入模式和开放元素栈确定性处理未闭合标签,如<p>hello<div>中因<div>是块级元素且<p>禁止嵌套块级元素,立即隐式闭合<p>;<p>等特殊元素遇块级兄弟节点即闭合,<div>等普通元素由栈机制强制收尾,<img/>合法而<div/>的/被丢弃。

容错恢复不是“猜”,而是按HTML Living Standard状态机执行
浏览器解析器从不靠启发式猜测来闭合标签,它严格依据HTML Living Standard定义的插入模式(insertion modes)和开放元素栈(stack of open elements)推进。遇到未闭合标签时,不是“补上一个
”,而是根据当前栈顶元素类型和下一个token的语义,决定是否生成隐含结束标签(generate implied end tags)。比如在<p>hello<div>中,<div>是块级元素,而<p>不允许包含块级子元素,所以解析器立刻弹出<p>并隐式闭合它——这个动作由状态机驱动,每一步都可复现。
不同元素触发容错的规则完全不同
元素分类直接决定容错行为:
-
<p>、<dt>、<li>属于“特殊元素”,遇块级兄弟节点(如另一个<p>或<div>)立即隐式闭合 -
<div>、<span>等普通流元素不会主动触发前序元素闭合,但会在父元素闭合或文档结束时被栈机制强制收尾 -
<img>、<br>是void element,<img/>合法;而<div/>中的/会被直接丢弃,等价于<div>,后续内容全归入其子树
Gumbo和html5-parser怎么记录错误却不中断解析
它们的错误恢复不是“跳过错误”,而是“登记+状态重置”:
- 每个错误(如
GUMBO_ERR_MISSING_ATTRIBUTE_VALUE)通过parser_add_parse_error()写入parser->errors数组 - 同时根据当前插入模式切换内部状态,确保后续token仍能进入正确处理分支
- 例如在
<table>上下文中遇到<div>,解析器不会终止,而是登记错误、保持<table>栈帧,并将<div>当作“忽略内容”处理 - Python的
html5-parser通过max_errors参数控制是否继续,但默认始终尽力构造DOM树
为什么你不能用try-catch捕获HTML解析错误
因为HTML解析阶段根本不在JavaScript执行上下文中运行:
立即学习“前端免费学习笔记(深入)”;
-
window.onerror、addEventListener('error')只响应资源加载失败、JS异常、Promise rejection -
DOMParser().parseFromString(htmlStr, 'text/html')永远返回Document对象,哪怕输入是<foo><bar> - 所谓“页面白屏”或“布局错乱”,根源是解析后DOM结构与预期不符,而非错误被抛出——你看到的是容错结果,不是错误本身
- 真正有效的防控只有两类:开发期用
html-validate静态检查,运行时靠doc.body.children.length === 0等简单反推做兜底
<header><nav><ul>后面没写</ul></nav></header>,浏览器会自动补全,但屏幕阅读器依赖的嵌套层级可能已错位。这种问题无法靠运行时捕获,只能靠开发期工具链卡死。



















