必须用<ol>制作面包屑,因其唯一符合语义、可访问性与SEO要求:表达不可颠倒的层级顺序,支持读屏器播报序号,配合<nav>和itemscope可被搜索引擎识别为BreadcrumbList结构化数据。

用 <ol> 制作面包屑不是“能用就行”,而是唯一符合语义、可访问性和 SEO 要求的写法——<ul> 或 <div> 堆砌路径,等于主动放弃屏幕阅读器支持和搜索引擎结构化识别。
为什么必须用 <ol> 而不是 <ul>
<ol> 表达的是“有明确先后顺序的层级路径”,比如 首页 → 分类 → 子类 → 当前页,这个顺序不可颠倒,也不可跳级。辅助技术(如 NVDA、VoiceOver)会读出“第 1 项:首页,第 2 项:分类……”,帮助用户快速定位深度。
-
<ul>暗示“无序集合”,读屏器不报序号,用户无法感知当前在第几层 - 用
<div>+<a>拼接,即使加了aria-label,也丢失“列表”和“顺序”两重语义 - 搜索引擎(尤其是 Google)依赖
<nav>+<ol>+itemscope组合来提取 BreadcrumbList 结构化数据
<ol> 里每个 <li> 怎么写才合法
关键不在视觉怎么排,而在每个 <li> 的角色是否准确:
- 上级页面(可点击):用
<a href="/products">产品</a>,href必须是绝对路径(以/开头),避免子目录下相对路径错乱 - 当前页面(不可点击):用
<li aria-current="page">手机详情</li>,里面不放<a>,也不加href="#"或tabindex="-1" - 禁止写
<li class="active">或<li aria-disabled="true">——前者无语义,后者含义错误(当前页不是“被禁用”,而是“终点”) -
aria-current="page"只能加在<li>上,值必须是"page"(不是"true"、"current"或"location")
CSS 怎么隐藏编号又不破坏语义
默认 <ol> 会显示 1. 2. 3.,但面包屑不需要数字——不能靠 list-style: none 就完事,还得确保分隔符不被读屏器误读:
立即学习“前端免费学习笔记(深入)”;
- 用伪元素生成分隔符:
nav[aria-label="Breadcrumb"] li:not(:last-child)::after { content: "/"; margin: 0 0.5em; } - 必须加
aria-hidden="true"到伪元素对应的父<li>,或确保伪元素本身不进入可访问树(现代浏览器中::after默认aria-hidden="true",但显式声明更稳妥) - 别用
<span>/</span>硬写进 HTML,否则读屏器会读成“首页 斜杠 产品 斜杠 ……” - 移动端建议加
white-space: nowrap; overflow-x: auto;,防止长路径折行错位
动态生成时路径解析最容易踩的坑
前端用 window.location.pathname 自动拼面包屑,看似省事,实则埋雷:
- 直接
split("/")会得到空字符串(如"/product//detail/"→["", "product", "", "detail", ""]),得先清洗:pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean) - 带查询参数的 URL(如
/blog/post?id=123)不该把id=123当作层级,应只取pathname部分 - i18n 前缀(如
/zh/blog/)若未配置映射表,会把zh错当第一级 - 单页应用(Vue/React)应从路由元信息(如
route.meta.breadcrumb)取文案,而非硬拆 path——文案和路径可能不一一对应(如/u/123显示“用户张三”)
最稳的方式仍是后端注入结构化数组,比如 const breadcrumbs = [{text: "首页", href: "/"}, {text: "文档", href: "/docs"}, {text: "面包屑", href: null}];前端只是渲染,不负责推导逻辑。



















