纯静态 index.html 中不能用 JS 解析 URL 生成面包屑,因 pathname 切分失真、多语言误判、URL 重写失效,且违反语义化与可访问性要求;必须手写或构建时注入。

纯静态 index.html 里不能靠 JS 自动解析 URL 生成面包屑——它不可靠、不语义、还破坏可访问性。必须手写或构建时注入,否则“首页 > 产品 > 小部件”可能和真实信息架构完全对不上。
为什么不能用 window.location.pathname 切分生成
直接在 index.html 里运行 JS 解析路径,看似省事,实则埋雷:
-
/products/widget/index.html会被切成["", "products", "widget", "index"],把index当成一个独立层级,但用户理解中它是“小部件”的默认页,不该单独出现 - URL 重写后(如 Nginx 配置了
try_files $uri $uri/ /index.html),pathname已失真,无法反推业务层级 - 多语言站点如
/zh/products/,硬切会把zh当成一级导航,而实际“首页”仍是根路径/ - 键盘用户 Tab 进来时,读屏器会读出“第1项:空 → 第2项:products → 第3项:widget”,语义断裂且误导
<nav aria-label="Breadcrumb"> 必须包裹 <ol>,不是 <ul> 或 <div>
搜索引擎和读屏器只认这个组合。用错标签等于扔掉语义:
-
<ul class="breadcrumb">:Lighthouse 会报“语义缺失”,NVDA 不会读出层级深度 -
<div class="breadcrumb">:键盘用户 Tab 到这儿时完全静默,听不到任何导航提示 -
<ol>表达的是“首页 → 产品 → 当前页”这种严格先后关系,即使你用 CSS 隐藏序号(list-style: none),辅助技术仍能感知顺序 - 错误写法:
<nav aria-current="page"><ol></ol></nav>——aria-current只能加在<li>上,加在<nav>或<ol>上无效
aria-current="page" 加在哪?值必须是 "page"
这是当前页可被读屏器识别的唯一可靠方式,CSS 类名(如 class="active")完全无效:
立即学习“前端免费学习笔记(深入)”;
- 只加在当前页对应的
<li>上,例如<li aria-current="page">无线耳机</li> - 值必须是
"page",写成"true"、"current"或"location"都不会被 JAWS/NVDA/VoiceOver 正确识别 - 当前页项不能带
href,否则键盘用户按 Enter 会刷新或跳转失败;也不该加tabindex="0",它本就不该聚焦 - 上级页链接必须用绝对路径(以
/开头),比如href="/products/",而不是./products.html—— 后者在子目录下(如/blog/)会错指向/blog/products.html
分隔符必须用 ::after 伪元素,且设 aria-hidden="true"
把 / 或 > 直接写进 HTML,读屏器会连读成“首页斜杠产品斜杠当前页”,打断语义流:
- 正确写法:
nav[aria-label="Breadcrumb"] li:not(:first-child)::before { content: "/"; aria-hidden: "true"; } - 禁用
.breadcrumb-item::before { content: ">" !important; }—— 它和 Bootstrap 5 的变量冲突,且!important增加维护成本 - 别用 SVG、图标字体或背景图做分隔符:高对比度模式下可能消失,也增加渲染负担
- 移动端窄屏时,避免用
text-overflow: ellipsis截断中间项——最该保留的是当前页名,而它恰好在末尾,常被误截
最稳的方案:5 个以内固定页面,直接手写 HTML 结构;超过这个量,用 Hugo/Jekyll 的 {{ site.url }} 或 Vite 的 templateParameters 注入预置路径对象。动态生成不是懒,而是错——错在把语义交给了不可控的运行时环境。



















