main必须且仅能出现一次,是文档级唯一核心区域,须为body直接子元素;article要求内容独立可分发并含显式标题;nav仅识别含href的a、area或role="link"元素。

main、article、nav 这类语义容器不是“加了就有用”,浏览器解析时会严格按 HTML 标准白名单执行:只对约 110 个标准标签触发 sectioning root、landmark 注册、heading 层级重置等行为;其他标签哪怕加 role,也仅影响无障碍树局部描述,不改变 DOM 结构逻辑。
浏览器怎么识别 main 并赋予它 landmark 行为
解析器在 Tree Construction 阶段,一旦遇到 <main> 开始标签,会立即在内部 landmark registry 中注册一个 main 类型的区域节点,并绑定到当前 Document 实例。这个动作和 CSS 渲染无关,也不依赖 JS 执行——它发生在 DOM 构建完成前。
关键约束:
-
main必须是文档级直接子元素(不能嵌套在header、section或自定义标签内),否则注册失败,降级为普通HTMLElement - 多个
main会触发解析器静默标记“结构冲突”,不报错但 landmark registry 只保留第一个 -
main内部必须有至少一个标题级元素(h1–h6)作为内容锚点,否则部分爬虫判定为“空主区域”而跳过提取
article 触发 sectioning root 的真实条件
article 不是“写了就分节”,它触发 sectioning root 的前提是:该元素必须具备独立可分发性(self-contained),即能脱离当前页面被单独引用、聚合或 RSS 抓取。浏览器通过两个硬性信号判断:
立即学习“前端免费学习笔记(深入)”;
- DOM 中存在显式标题:
h1–h6或带aria-labelledby的元素,且该标题未被display: none或visibility: hidden隐藏 - 内部包含可索引正文内容(非纯 JS 占位符、非空
div),搜索引擎要求至少 50 字符以上有效文本
常见误用:<article><div id="post-root"></div></article> —— SSR 输出为空,爬虫拿到的是无文本容器,article 语义失效,退化为 div。
为什么 nav 里的链接必须是 a 或带 role="link"
解析器在构建 landmark 时,会对 nav 子树做轻量级语义验证:只把符合导航意图的节点计入“导航链接集合”。规则很简单:
- 直接子元素中,只有
a[href]、area[href]、或显式声明role="link"且含href属性(或模拟跳转行为的onclick)的元素才被识别 - 纯文本、
button、span即使加了role="link"但没href,会被忽略 - 嵌套超过一层(如
nav > div > a)仍可识别,但若中间层用了display: contents,部分旧引擎会丢失父子关系链
这不是规范“建议”,而是 Chrome/Firefox/Safari 当前实际实现逻辑:它们在 landmark registry 填充阶段就过滤掉了非导航意图节点。
自定义标签加 role="main" 为什么查不到
document.querySelector('main') 匹配的是标签名,不是 role。解析器根本不会把 role 属性纳入选择器匹配路径——这是 DOM 查询层的硬性隔离。
更关键的是,加了 role="main" 的自定义标签:
- 不会注册进 landmark registry(只认
<main>标签名) - 不触发 sectioning root,其内部
h1仍沿用全局层级计数 - 无障碍树中虽暴露
role="main",但无name或label,屏幕阅读器读作 “main group” 而非具体名称 - CSS 中
main伪类(如main:has(h1))完全不匹配它
真正容易被忽略的点:即使你用 ElementInternals.setRole() 在 Web Component 中手动设 role,也无法绕过标签名白名单限制——sectioning 和 landmark 是解析时确定的,运行时无法补救。



















