HTML 内容模型

用 WHATWG 内容模型理解元素可包含什么,解释非法嵌套时浏览器 DOM 修复为何让 CSS/JS 行为“幽灵化”。

#type / concept #status / growing #tech / dev / frontend #resource / html

[!info] 关联笔记

HTML 内容模型

这个概念为什么出现

源码写了 <p><div>...</div></p>,页面“还能显示”,但 DevTools 里 div 已经跑出 p。于是:

  • .desc .card 选不中
  • 事件委托挂错节点
  • 水合/SSR 警告 DOM mismatch

根因通常不是 CSS,而是违反内容模型后的解析修复

[!abstract] 一句话理解 每个元素都有“允许的子内容类别”;HTML 解析器会尽量修复非法树,修复后的 DOM 才是真相。

最小可观察示例

场景:商品描述组件把卡片塞进段落

<!DOCTYPE html>
<html lang="zh-CN">
  <body>
    <p id="desc" class="desc">
      SKU-42 说明
      <!-- 非法:div 不是 phrasing content,不能待在 p 里 -->
      <div class="card">详细参数表</div>
    </p>
    <script>
      // 教学点:打印真实父节点,而不是相信源码缩进
      const card = document.querySelector('.card')
      console.log(card.parentElement && card.parentElement.id)
      console.log(document.getElementById('desc').innerHTML)
    </script>
  </body>
</html>

建议运行:浏览器打开控制台。

期望观察(典型结果):

card 的 parent 往往不是 p#desc
#desc 的 innerHTML 在 div 前被提前关闭

结合场景关注点

  1. 源码树 ≠ DOM 树(修复后)。
  2. 选择器与 React/Vue hydrate 都以 DOM 为准。
  3. 组件库输出 HTML 也必须遵守模型。

核心概念与准确模型

常见内容类别(教学压缩)

类别直觉例子
Metadatahead 侧元数据link meta title
Flow大体 body 流p div section ul
Sectioning参与大纲分区article section nav aside
Heading标题h1-h6
Phrasing段落内文本级span a strong img
Embedded嵌入img video iframe
Interactive交互button input(上下文受限)

规范 vs 教学:完整类别与“可感知内容”细节以 HTML spec content models 为准;上表是工程导航模型。

高频非法嵌套

  1. p 内放 div/section/ul 等块级 flow
  2. a 内再嵌可聚焦交互(交互嵌套限制)
  3. button 内再放交互控件
  4. table 结构元素乱序(缺 tbody 时浏览器会补)

设计动机

若规范不约束子内容,每个浏览器“尽量显示”的修复策略会分叉,开发者无法共享同一 DOM 语义。内容模型是互操作合同。

边界情况

  • 透明内容模型(如 a 的部分情况):子内容要求随父上下文变化,不能死记硬背一个列表。
  • 模板/组件抽象:JSX 里“能编译”不等于 HTML 合法。
  • Markdown 渲染:错误插件可能产出非法嵌套。

常见误区

[!warning] 常见误区:页面能看见 = HTML 正确 解析修复会掩盖错误,直到选择器、a11y 树或 SSR hydrate 爆。

工程实践

  • 不确定就查 MDN 元素页的 “Permitted content/parents”。
  • 对关键页面做 HTML validator 或组件快照。
  • 在 UI 评审里同时审 DOM 结构,不只是视觉。

本节总结

内容模型是“树能否合法存在”的规则;调试前端诡异问题时,先看 DOM 是否被静默重写。

自测题

  1. 为什么 p > div 是经典陷阱?
  2. 框架组件如何避免产出非法嵌套?
参考答案
  1. p 只能主要包含 phrasing;遇到 div 解析器常提前关 p,导致父链变化。
  2. 设计 API 时限制 slot 可放内容类型;用 lint/测试检查输出 DOM;避免在 p/h* 等容器内无约束地渲染块级 children。

延伸阅读

资料类型支撑内容
Content models规范类别定义
MDN: Content categories文档可查表
创建于 2026/7/15 更新于 2026/7/15