面包屑必须用 nav 包裹且带 aria-label="breadcrumb",内部用 ol 按层级顺序嵌套 li,当前页用 span+aria-current="page",结构化数据需服务端输出 json-ld 并与 html 文本严格一致。

面包屑必须用 nav 包裹,且需带 aria-label="Breadcrumb"
纯视觉的面包屑(比如只用 div + CSS)对屏幕阅读器完全不可见。搜索引擎和辅助技术依赖明确的语义容器识别导航意图。nav 是唯一被广泛支持的、表示“一组导航链接”的语义标签,但仅用 nav 不够——必须加 aria-label="Breadcrumb",否则部分读屏软件(如 NVDA 2025+)可能将其误判为页眉或主菜单。
常见错误现象:
- 用
ol或ul单独包裹路径项,外层没nav→ 读屏时读作“列表”,不提示“这是面包屑” - 写了
nav但漏掉aria-label→ Safari + VoiceOver 下静默跳过 - 把当前页链接也做成可点击的
a→ 违反 WCAG 2.4.4(链接目的需明确),应改用span并加aria-current="page"
正确结构示例:
<nav aria-label="Breadcrumb"><ol>
<li><a href="/">首页</a></li>
<li><a href="/products/">产品</a></li>
<li><span aria-current="page">WH-1000XM5 降噪耳机</span></li>
</ol></nav>
ol 比 ul 更合适,且 li 必须按顺序嵌套
面包屑本质是线性层级路径,ol(有序列表)天然表达“第1级→第2级→第3级”的先后关系;而 ul 表达并列关系,会削弱语义强度。更重要的是,某些旧版 JAWS 会忽略 ul 中的 aria-current 属性,导致当前页无法被准确定位。
使用要点:
- 每个
li只能直接包含一个a或span,不能套div或span再包链接——这会打断读屏器的自动列表项遍历逻辑 - 不能把所有
li写在同一行用 CSS 排列:虽然视觉上连贯,但读屏器仍会逐项朗读“项目1、项目2、项目3”,中间无分隔提示;建议保留默认块级换行,靠 CSSdisplay: flex或inline-flex控制视觉流 - 避免在
li上加role="none"或aria-hidden="true"——这会让整个列表项从可访问树中消失
结构化数据(JSON-LD)必须服务端输出,且与 HTML 面包屑文本严格一致
光有可访问的 HTML 面包屑还不够。Google 等爬虫不解析 DOM 结构推断层级,它只认 JSON-LD 中的 BreadcrumbList。如果服务端没吐出这段标记,或者客户端 JS 动态插入,首屏抓取时就是空的——等于没写。
关键校验点:
-
@type必须是"BreadcrumbList"(少个List就失效) - 每个
item的name必须和对应li中的可见文本**逐字一致**:包括中文标点、全角空格、大小写。例如 HTML 里写的是“WH-1000XM5 降噪耳机”,JSON-LD 里就不能写成"WH-1000XM5 降噪耳机 "(末尾多空格)或"wh-1000xm5 降噪耳机" - 当前页
item的@id必须是该页面的绝对 URL(如https://example.com/products/wh-1000xm5),不能是相对路径或#top -
position必须从1开始连续递增,不能跳号(如1, 2, 4)或从0起始
响应式断点下隐藏部分层级时,不能删 DOM,只能用 aria-hidden
移动端常因空间限制只显示最后两级(如“产品 → WH-1000XM5”),但若直接用 JavaScript 移除前面的 li,会导致读屏器丢失完整路径,也破坏结构化数据同步基础。
安全做法是保留全部 li,仅对需要隐藏的项添加 aria-hidden="true",并配合 CSS display: none 或 visibility: hidden(二者效果一致,但前者更推荐):
这样既满足视觉精简需求,又确保辅助技术仍能获取完整层级信息——因为 aria-hidden="true" 仅影响可访问树,不影响 DOM 存在性,结构化数据也能照常生成。
最易被忽略的一点:当面包屑动态渲染(如 Vue/React 组件)时,aria-current="page" 和 aria-label 必须随路由实时更新,不能靠初始值硬编码。否则用户前进/后退后,读屏器仍停留在旧状态。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











