用 role="tab" 不能键盘左右切换是因为需手动监听 keydown 事件并实现焦点切换逻辑,同时必须正确设置 tabindex、aria-controls/aria-labelledby、focus() 及避免 display: none 隐藏面板。

为什么用 role="tab" 还不能键盘左右切换
因为 role="tab" 本身不自动绑定方向键行为——浏览器只保证 Tab 键在可聚焦元素间跳转,不会主动监听 ArrowLeft/ArrowRight。你得自己写事件监听,且必须用 keydown(不是 keyup),否则页面可能先滚动再执行你的逻辑。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 每个 tab 按钮必须有
tabindex="0"(当前激活)或tabindex="-1"(非激活),不能全设为0,否则焦点顺序错乱 - 监听
tablist容器的keydown,判断event.key是"ArrowLeft"还是"ArrowRight" - 切换时调用
event.preventDefault(),否则 Safari/Chrome 可能触发横向滚动 - 缓存所有 tab 元素数组,用索引计算下一个目标,别每次
querySelectorAll查 DOM
aria-controls 和 aria-labelledby 必须成对写对
漏掉任意一端,NVDA、VoiceOver 就无法建立 tab 与 panel 的语义关联。比如用户听到“首页标签”,点进去了,但读屏器不知道下面那段内容就是“首页”,会读成“未命名区域”。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- tab 按钮加
aria-controls="panel-1",对应面板的id必须完全一致(大小写敏感、不加空格) - 面板加
aria-labelledby="tab-1",指向控制它的 tab 的id,不是 class 或 text 内容 - 如果 tab ID 含点号(如
tab.home),CSS 中需写成#tab\.home,但 ARIA 属性里直接写tab.home即可 - 禁用
display: none隐藏非激活 panel——它会让屏幕阅读器彻底跳过内容,哪怕aria-hidden="false"也救不回来
切换后焦点没落到新 tab 上?那是没手动 .focus()
鼠标点击后焦点还卡在旧按钮上,是常见现象。这不是 bug,是你没接管焦点流。浏览器不会因为 aria-selected="true" 就自动把焦点移过去。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 每次方向键或点击切换 tab 后,立即对目标按钮调用
button.focus() - 如果新面板里有输入框等可聚焦子元素,且用户大概率要操作它,切换后应把焦点移到第一个可聚焦子项,而不是留在 tab 按钮上
- 别依赖
autofocus:它只在初始渲染生效,动态显示 panel 时无效 - 用
focus({ preventScroll: true })防止页面跳动;但 Safari 不支持该参数,得先检测:if ('preventScroll' in FocusOptions.prototype)
移动端和键盘用户的隐藏陷阱
很多“能用”的 tabs 在真机 VoiceOver 下会失效,核心问题不在 JS,而在 HTML 结构和尺寸。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 必须用
<button>做 tab 标签,别用<div role="tab">——后者在 iOS WebView 中常不响应Space,且无:active状态 - 触摸目标至少
48×48px,靠padding撑不够;图标按钮要显式设min-width: 48px; min-height: 48px - DOM 深度别超 6 层,否则 touchstart 延迟明显,VoiceOver 滑动时容易跳过某个 tab
- 禁用全局
* { outline: none }:它会干掉:focus-visible,键盘用户瞬间“失明”
真正难的不是让左右键动起来,而是确保每次焦点迁移都落在用户预期的位置,并且屏幕阅读器能同步读出“你正在首页标签,按回车可查看内容”。这要求你同时管住 HTML 结构、ARIA 属性、JS 焦点调度和 CSS 隐藏方式——少一个环节,可访问性就断链。



















