非标标签指不符合html规范的写法,如(斜杠被丢弃等价于)、漏闭合、交叉嵌套、漏写等,其“看似正常”实为解析器依html living standard隐式修正dom所致。

浏览器能解析非标标签,但不是“接受”它——而是按 HTML Living Standard 的容错算法强制重排 DOM,结果不可控、不可预测,且不同阶段(服务端渲染 vs 客户端 hydration)表现不一致。
非标标签指什么?哪些算“非标”但实际被容忍
非标标签不单指自定义标签(如 <my-button></my-button>),更常见的是:<code><div></div> 写成 <div></div>、<p></p> 漏闭合、<b><i>text</i></b> 这类交叉嵌套、甚至 <table><tr><td></td></tr></table> 漏 <tbody>。其中部分写法在浏览器中“看起来正常”,是因为解析器做了隐式修正,而非语法合法。
<ul><li>
<code><div></div> 中的 / 被直接丢弃,等价于 <div>,后续靠栈机制收尾,DOM 结构取决于上下文
<li>
<code><b><i>text</i></b> 不是“允许交叉”,而是解析器发现 时,先弹出栈顶 <i></i>,再闭合 <b></b>,最后重开 <i></i> —— 顺序严格按 LIFO
<table><tr></tr></table> 必定补 <tbody>,但位置固定在 <code><table> 下第一层;而 <code><table>
<thead><tr></tr></thead>
<tr></tr>
</table> 会导致第二个 <tr> 被塞进 <code><tbody>,哪怕你没写
<h3>为什么 innerHTML += 非标片段会彻底乱套</h3>
<p>每次赋值 <code>innerHTML 都是一次全新解析,不继承前次状态。写 el.innerHTML = '<p>start';</p> 后,DOM 中没有“半开 <p></p>”,只有自动补全的 <p>start</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5806" title="html-deploy"><img
src="https://img.php.cn/upload/skill/000/000/081/179066538882434.jpg" alt="html-deploy" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5806" title="html-deploy" class="overflowclass">html-deploy</a>
<p class="overflowclass">使用 htmlcode.fun 将 HTML 内容或文件部署到网页,适用于用户要求“部署到网页”“托管此 HTML”“生成此前端...的实时链接”等场景。</p>
</div>
<a rel="nofollow" href="/xiazai/skill5806" title="html-deploy" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>。再执行 el.innerHTML += 'end',等于解析 '<p>start</p>end',第二个 因无匹配起始标签被忽略,最终得到 <p>start</p>end。
- 所有基于字符串拼接构造嵌套结构的操作都踩中这个坑
- 用
insertAdjacentHTML('beforeend', ...)或DocumentFragment才安全 - 服务端模板若用类似逻辑拼 HTML(如 PHP 的
.=),客户端 hydration 时必然 mismatch
非标标签如何让 SSR 和 hydration 失败
服务端生成的 HTML 字符串(如 <table><tr><td>A</td></tr></table>)不含 <tbody>,但浏览器解析后 DOM 中一定有。客户端 JS hydrate 时比对的是“含 <code><tbody> 的真实 DOM”和“不含 <code><tbody> 的原始字符串”,结构不一致直接触发 hydration error。
<ul>
<li>这类 mismatch 往往静默发生:页面能显示,但 React/Vue 的事件绑定、状态同步全部失效</li>
<li>
<code><p></p>
<div></div> 类非法嵌套更危险:服务端输出该字符串,浏览器解析为 <p></p>
<div></div>,JS 查询 p > div 永远为空
真正难处理的不是报错,而是浏览器默默修好却修得不对——DOM 树变了,但没人告诉你它怎么变的。验证必须落到真实 DOM(DevTools Elements 面板),不能只信源码或服务端字符串。










