aria-selected 仅对 role="tab" 元素有效,必须嵌套在 role="tablist" 内;切换时需同步更新 aria-selected、aria-hidden、tabindex 和焦点,并验证无障碍树实时更新。

aria-selected 切换必须配合 role="tab" 和 role="tablist"
直接给卡片按钮加 aria-selected="true" 不会被读屏识别,除非它明确是 role="tab",且包裹在 role="tablist" 容器里。常见错误是把卡片做成 <div> 或 <a>,没设角色,或者把 aria-selected 写在 tablist 上——这个属性只对单个 tab 元素有效。
实操建议:
立即学习“前端免费学习笔记(深入)”;
-
tablist必须是直接父容器,不能跨层;子元素每个都要是<button role="tab">(不用<a>,避免跳转干扰) - 每个
button需有唯一id,并用aria-controls指向对应面板的id - 切换时,旧
tab清除aria-selected(或设为"false"),新tab设为"true";别只改 CSS 类
切换真假时必须同步更新 aria-hidden 和焦点
只改 aria-selected,不碰面板的 aria-hidden 和 tabindex,读屏会读错内容。比如用户按 Tab 进入 tab 区域后,焦点落在已选中的 tab 上,但对应面板仍是 aria-hidden="true",屏幕阅读器就可能跳过整个内容区。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 激活的
tabpanel设aria-hidden="false",并确保tabindex="0"(或首次进入时.focus()到内部首个可交互元素) - 非激活面板必须用
hidden属性(不是display: none),CSS 中统一写[hidden] { display: none; } - 每次切换后,立即调用
button.focus(),让键盘焦点落到当前tab上——Safari + VoiceOver 尤其依赖这个
JavaScript 更新 aria-selected 的典型漏点
很多代码能视觉切换,但读屏静默,问题常出在事件处理链断裂:比如用了 preventDefault() 却没手动 focus(),或用 click 监听但没覆盖键盘触发(Enter/Space)。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 监听
keydown处理ArrowLeft/ArrowRight、Home/End,并event.preventDefault()防止页面滚动 -
Enter或Space触发时,复用同一段切换逻辑(别另写一遍),再补上element.focus() - 别用
document.querySelector('.active')找当前项——读屏不认类名;应遍历所有[role="tab"],查aria-selected="true"
卡片式 tab 里容易误用 aria-multiselectable
卡片选项卡默认是单选行为(一次只展开一个),如果强行加 aria-multiselectable="true",反而破坏语义:读屏会预期支持 Ctrl+Click 或 Shift+Arrow 多选,但你的卡片逻辑并不响应这些操作,导致反馈错乱。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 纯切换场景(如详情页、步骤引导)就用单选模式,不设
aria-multiselectable - 真要多卡片同时展开(比如筛选条件组),改用
role="region"+aria-expanded,每个卡片独立控制,不要套tablist - 若坚持用
tablist实现“伪多选”,必须实现完整键盘多选逻辑(Ctrl+Click、Shift+Click、Ctrl+A),否则aria-selected值变来变去,读屏只会困惑
最常被忽略的是:切换后没验证无障碍树是否实时更新。打开 Chrome DevTools → Elements → Accessibility 标签,点开对应 tab 元素,确认 aria-selected 值和 tabindex 状态是否同步变化——靠肉眼或听读屏判断太慢,这一步省不得。



















