屏幕阅读器“读偏”或跳过正文的根本原因是页面大纲断裂或失真。它依赖语义标签层级和标题嵌套构建阅读流,而非视觉位置;缺失主区域、标题跳级、未加aria-label等均会破坏大纲;修复须守住三条硬线:全页仅一个且包裹核心内容、区域职责不越界、标题严格递进;伪语义结构需替换为真实语义标签并补全上下文;验证可用H/M键测试导航及检查可访问性地标。
根本原因不是代码写错了,而是页面大纲(outline)断裂或失真。屏幕阅读器依赖 DOM 中的语义标签层级和标题嵌套来构建阅读流,不是靠视觉位置或 class 名猜测结构。<main></main> 缺失、<h1></h1> 到 <h3></h3> 跳级、多个 <nav></nav> 没加 aria-label,都会导致它从页眉开始一路朗读广告、侧边栏、重复导航,直到用户手动下拉 10 次才摸到正文。
这三条不是建议,是屏幕阅读器解析逻辑的底层规则,绕不开:
<main></main> 全页只能出现一次,且必须包裹真正核心内容(如文章正文、商品列表),不能含广告、推荐位、客服入口<header></header> 和 <footer></footer> 内部禁止包裹非本区域职责的内容——比如页脚里塞一个“热门分类”<nav></nav>,会被当成页脚导航,M 键跳入后用户第一句听到的是“热门分类”,而非“版权信息”<h1></h1> → <h2></h2> → <h3></h3>;用 <h2></h2> 直接跟在 <h1></h1> 后面可以,但 <h1></h1> 后接 <h4></h4> 就会破坏大纲,NVDA/VoiceOver 会跳过中间层级,直接把 <h4></h4> 当成新章节起点很多项目里堆着这样的“假结构”:<div class="page-header">、<code><div id="content-wrap">、<code><section class="main-section"></section>。它们对屏幕阅读器完全静默,必须替换成真实语义标签,并补全缺失上下文:
<div class="page-header"> 改成 <code><header></header>,且确保里面第一个子元素是 <h1></h1> 或带 aria-labelledby 的标题容器<div id="content-wrap"> 这类包裹层,90% 可直接删掉;如果它真承担“主体内容”职责,就换为 <code><main></main>,别加 class<section class="product-list"></section> 必须紧随一个 <h2></h2>(如 <h2>精选商品</h2>),否则屏幕阅读器不认为它是独立章节,也不会在章节导航中列出不用装插件、不跑 Lighthouse,现场就能验:
立即学习“前端免费学习笔记(深入)”;
H 键循环跳标题,看是否能按预期层级进入各区块;按 M 键应直接跳入 <main></main> 开头,而不是卡在某个 <div> 里<li>在浏览器开发者工具里右键 → “检查可访问性” → 展开“Landmarks”,确认 <code><nav></nav>、<main></main>、<footer></footer> 都存在且数量合理(如 <nav></nav> 出现 3 次,但没加 aria-label,就会全部朗读为“导航”,无法区分)最易被忽略的点:修复了标签,但忘了删掉旧的 role 属性。比如把 <div role="main"> 改成 <code><main></main> 后,role="main" 必须一并删除——否则双重声明会导致部分读屏器行为异常。