nav必须独立存在且不能与logo等混排,因JS定位和读屏器依赖语义分离;多标签页属视图切换,应使用tablist/tab/tabpanel而非nav,以符合ARIA规范并保障可访问性。

nav 必须独立存在,不能和 logo、搜索框、用户头像混在一个容器里——否则 JS 控制定位会失效,读屏器也分不清哪是导航、哪是操作。
为什么不能把多标签塞进同一个 nav
管理系统常见的“首页 / 工单 / 审批 / 设置”这类顶部标签页,本质是页面内视图切换,不是跨页面跳转。它不符合 nav 的语义定义(HTML5 明确要求 nav 用于“导航到不同文档或文档内锚点”的链接集合)。强行塞进去会导致:
- 屏幕阅读器播报为“导航”,但用户实际只是切换 tab,语义错位
- SEO 权重被错误分配,爬虫可能误判为多个入口页
- 键盘 Tab 焦点逻辑混乱:按 Tab 本该跳过非交互区域,但这些标签页是可点击的,又不该算作主导航
- 后续加下拉菜单或权限控制时,DOM 结构和 ARIA 属性难以统一管理
tablist + tab + tabpanel 是唯一合规方案
WAI-ARIA 1.2 规范明确定义了标签页组件的语义结构,浏览器和读屏器(NVDA、VoiceOver)都原生支持。必须用这组角色,而不是靠 CSS 模拟视觉效果:
-
role="tablist"套在容器上,表示一组标签页控制区 - 每个标签项用
role="tab",并设aria-selected="true"标记当前激活项 - 对应内容区用
role="tabpanel",并通过aria-labelledby指向其关联的tab元素 id - 禁用状态用
aria-disabled="true",同时移除tabindex
示例片段:
立即学习“前端免费学习笔记(深入)”;
<div role="tablist" aria-label="系统功能区"> <button role="tab" aria-selected="true" id="tab-home" tabindex="0">首页</button> <button role="tab" aria-selected="false" id="tab-ticket" tabindex="-1">工单</button> </div> <div role="tabpanel" aria-labelledby="tab-home">...首页内容...</div>
Flexbox 布局时容易忽略的三个细节
视觉上要让标签页横向铺开、等宽、居中对齐,但 Flexbox 配置稍有偏差就会在小屏或高缩放比下出问题:
- 别用
justify-content: space-between—— 当前页标签文字略长时,两端留白不均,视觉失衡;改用space-around或center更稳 - 禁止给容器设
white-space: nowrap—— 移动端会强制横向滚动,正确做法是flex-wrap: wrap+ 媒体查询控制换行阈值 - 每个
tab按钮必须设flex: 1或明确宽度,否则 Safari 在 flex 容器中可能压缩文本导致截断
JS 切换时最常漏掉的两件事
纯 HTML 只能静态标记当前页,真实系统需要 JS 响应点击并同步状态。但以下两点极易遗漏:
- 焦点管理:切换后必须调用
element.focus()到新激活的tab,否则键盘用户卡在旧位置 - ARIA 同步:不仅要改
aria-selected,还要同步tabpanel的hidden属性,并确保aria-hidden="true"正确应用到非激活面板
复杂点在于:如果标签页内容由异步加载,tabpanel 的 DOM 可能尚未插入,此时直接设 hidden 或操作 aria-hidden 会无效。得等内容挂载完成再触发属性更新。



















