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

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



















