正确结构必须是role="tablist"直接包裹role="tab",且每个tab通过aria-controls与对应role="tabpanel"显式关联;tab须为可聚焦元素(如button),并动态管理aria-selected、aria-hidden、tabindex等状态以支持键盘导航。

role="tablist" 必须包裹 role="tab" 和 role="tabpanel"
不满足这个结构,屏幕阅读器会跳过标签页或读错顺序。常见错误是把 role="tab" 放在 role="tablist" 外面,或者用 div 套 div 却漏掉 role 层级关系。
正确结构必须是:
<div role="tablist"> <button role="tab" aria-selected="true" aria-controls="panel-1">标签一</button> <button role="tab" aria-selected="false" aria-controls="panel-2">标签二</button> </div> <div role="tabpanel" id="panel-1" tabindex="0">内容一</div> <div role="tabpanel" id="panel-2" tabindex="0">内容二</div>
-
role="tablist"只能直接包含role="tab"(推荐用button),不能嵌套其他容器 -
role="tabpanel"必须与tab通过aria-controls和id显式关联 - 所有
tab需有aria-selected,且仅一个为true
tab 必须用 button 或带 tabindex 的可聚焦元素
单纯给 span 加 role="tab" 不行——它默认不可键盘聚焦,也无法触发 Enter / Space 事件。用户用键盘切换时会卡住。
- 优先用
<button role="tab">:天然可聚焦、可点击、语义清晰 - 若必须用
div或span,得加tabindex="0"和手动绑定click+keydown事件 - 别忽略
aria-disabled="true":禁用态 tab 要同步设tabindex="-1",否则键盘仍能聚焦
tabpanel 需要 tabindex="0" 且初始隐藏逻辑要一致
只靠 display: none 或 visibility: hidden 会让屏幕阅读器跳过内容,也影响键盘焦点流转。必须配合 aria-hidden="true" 控制可访问性状态。
立即学习“前端免费学习笔记(深入)”;
- 激活的
tabpanel:设aria-hidden="false"+tabindex="0"(方便键盘用户直接 Tab 进入内容) - 未激活的
tabpanel:设aria-hidden="true"+tabindex="-1"(禁用焦点,但保留 DOM 结构) - CSS 隐藏建议用
position: absolute; clip-path: inset(100%),比display: none更安全
键盘交互必须实现箭头键切换 + Home/End
仅支持鼠标点击不算完整 ARIA 标签页。用户按 → / ← 切换 tab 是强制要求,否则 WCAG 2.1 AA 不达标。
- 监听
keydown事件,在tablist上捕获ArrowRight/ArrowLeft/Home/End - 切换时更新两个状态:当前
tab的aria-selected和对应tabpanel的aria-hidden - 注意循环逻辑:
End到最后一个,Home到第一个,←在第一个时跳到最后一个
实际中容易忽略 tabindex 的动态重置——切换后,原激活 tab 要设 tabindex="-1",新 tab 设 tabindex="0",否则键盘焦点会“漂移”。



















