chrome devtools中heading-level跳变因浏览器严格按w3c大纲算法解析:标题必须逐级递进或回退,跳级(如h2→h4)导致语义断裂、accessibility面板突变且大纲失效。

为什么 Chrome DevTools 里 heading-level 显示跳变
浏览器不是按视觉大小或 class 名称判断标题层级,而是严格执行 W3C Outline Algorithm:从第一个 <h1></h1> 开始,后续每个 heading 必须比前一个最多深一级、或回退任意级(<h2></h2>→<h3></h3> 合法,<h2></h2>→<h4></h4> 就算跳级)。一旦跳级,Accessibility 面板里的 heading-level 会突然从 2 变成 4,但语义上它仍被当作“<h3></h3> 的子项”处理,导致大纲断裂。
- 常见诱因:
<h1></h1>后直接写<h3></h3>;动态渲染时props.level传入了"4"却没校验范围 - 隐藏陷阱:用
display: none或aria-hidden="true"隐藏了<h1></h1>,工具仍尝试解析,但上下文已丢失 - 验证方式:右键标题 → “Reveal in Accessibility Tree”,看
heading-level是否连续、无突变
多个 <h1></h1> 为什么让 axe 报错“Document has more than one <h1></h1>”
W3C 规定单页文档有且仅有一个主主题,<h1></h1> 就是那个锚点。出现第二个 <h1></h1>,大纲算法就终止构建,后续所有 heading 全部降级为同级节点——相当于整本书突然多出一个“第零章”,后面所有章节编号全乱。
- 典型场景:Layout 组件硬编码
<h1>MySite</h1>,Page 组件又写一个<h1>订单确认</h1> - SPA 路由切换后未更新
<h1></h1>文本,残留上一页标题 - 修复关键:Layout 中改用
<header></header>或<div role="banner">;页面级 <code><h1></h1>必须由当前路由/数据驱动,且 SSR 首帧就得存在<section></section>真正的作用不是加样式,而是重置 heading 隐含层级<section></section>不生成大纲节点,但它是个“sectioning root”,会让内部的<h1></h1>–<h6></h6>重新计层。没有它,<h3></h3>必须紧跟在<h2></h2>后才合法;有了它,<h3></h3>可以作为该区块内最高标题使用。- 错误写法:
<h1>产品页</h1> <h2>规格</h2> <h3>尺寸</h3>→<h3></h3>被判定为跳级(<h1></h1>→<h3></h3>) - 正确写法:
<h1>产品页</h1> <section><h2>规格</h2> <h3>尺寸</h3></section>→<h2></h2>是该<section></section>的顶层,<h3></h3>是其合法子级 - 注意:
<nav></nav>、<aside></aside>、<article></article>同样是 sectioning root,但语义不可互换;滥用三层以上嵌套<section></section>会让屏幕阅读器退出困难
别用
<h5></h5>和<h6></h6>当小字或备注<h5></h5>和<h6></h6>语义是“最末级标题”,不是“小号文字”。读屏器会朗读为“标题五级”“标题六级”,用户听到一串重复的“标题五级”,根本分不清这是副标题、强调句还是随便加粗的一行字。- 95% 的业务页面根本不需要
<h5></h5>或<h6></h6>;法律条款、API 参数表等极度细分结构除外 - 纯视觉强调:用
<strong></strong>+ CSS 控制粗细/颜色 - 语义强调(重要内容):用
<strong></strong>,别套标题标签 - 需要结构化副标题:确保上面有
<h1></h1>或<h2></h2>,再用<h2></h2>或<h3></h3>
真正容易被忽略的点是:大纲不是写完 HTML 就自动对的。它依赖运行时实际渲染出的 DOM 结构,而 SSR 输出、JS 动态插入、条件渲染都可能悄悄破坏层级。每次改标题逻辑,必须用 VoiceOver 的
Ctrl + Option + Cmd + H或 axe-cli 跑一次heading-order检查——机器怎么读,才是最终答案。 - 错误写法:











