aria-selected 属性必须用 JavaScript 动态更新,不能仅靠 HTML 写死;屏幕阅读器只识别真实写入的 aria-selected 值,需配合 focus() 和 aria-live 确保状态播报,多选时须声明 aria-multiselectable,CSS 和 JS 必须严格区分 "true"/"false" 字符串值。

aria-selected 属性必须用 JavaScript 动态更新
直接写死 aria-selected="true" 在 HTML 里只对初始状态有效,后续切换必须靠 JS 修改 DOM 属性。浏览器不会自动把 class="active" 或 data-selected="true" 映射为可访问语义——屏幕阅读器只认 aria-selected 这个属性本身是否被真实写入且值为 "true" 或 "false"。
常见错误现象:
- 点击后视觉高亮了,但 NVDA / VoiceOver 仍读“未选中”
- 键盘按
Space切换,焦点到了,但没播报状态变化 - 用
element.classList.add("selected")替代setAttribute("aria-selected", "true"),结果无障碍树里该属性压根没变
实操建议:
- 每次切换前,先遍历所有同类子项(如所有
role="tab"),统一设aria-selected="false" - 再给目标元素调用
el.setAttribute("aria-selected", "true") - 紧接着执行
el.focus(),确保键盘焦点同步到位 - 别用
removeAttribute("aria-selected")来“取消”,显式设"false"更稳妥(部分读屏对缺失属性不敏感)
更新时机不对,屏幕阅读器根本不会播报
aria-selected 写进 DOM 后,不会自动触发语音反馈。DOM 更新 ≠ 辅助技术感知——尤其在初始化或异步加载后手动设值时。
立即学习“前端免费学习笔记(深入)”;
常见错误现象:
- 页面加载完立刻
document.getElementById("tab-1").setAttribute("aria-selected", "true"),但用户第一次Tab进来时才听到“已选中” - 下拉列表用
innerHTML替换内容后,重新设aria-selected,但读屏静音
实操建议:
- 纯初始化(如默认第一个 tab):直接设属性即可,等用户聚焦时由焦点自然触发播报
- 交互中动态更新(如点击切换):除了改属性,还要确保
focus()落到目标元素上——这是触发播报的关键信号 - 需要立即语音反馈(比如搜索后自动跳转到结果 tab):加一个
<div aria-live="polite" aria-atomic="true"></div>,并在 JS 中写入提示文本,例如liveRegion.textContent = "已切换至结果标签页"
多选场景下,aria-multiselectable 容器必须提前声明
如果想支持 Ctrl+Click 或 Shift+Arrow 多选,光给多个子项设 aria-selected="true" 没用。屏幕阅读器看到多个 "true" 却没看到父容器的 aria-multiselectable="true",会当成逻辑错误或直接忽略后续项。
实操建议:
- 容器(如
<div role="listbox">)必须带aria-multiselectable="true" - 每个可选项必须是
role="option"(listbox)或role="row"(grid),不能只是普通<div> - Shift+↓ 连续选中时,中间所有项都得同步更新
aria-selected,不能只改首尾 - 避免在同一个
listbox里混用单选和多选逻辑——ARIA 不支持运行时切换aria-multiselectable值
CSS 样式钩子要严格匹配字符串值
用 CSS 控制视觉反馈时,[aria-selected="true"] 是安全写法;但 [aria-selected] 会匹配 "true"、"false" 甚至空字符串,导致样式错乱。
常见错误现象:
- 未选中的项也高亮了,因为 CSS 用了
[aria-selected],而它本来就有这个属性(值为"false") - 用
aria-selected做动画触发器,结果"false"也被当真值处理
实操建议:
- 样式规则必须写成
[aria-selected="true"] { ... },区分真假值 - JS 更新时,务必设成字符串
"true"或"false",不要传布尔值true/false(setAttribute会转成字符串,但某些封装库可能出问题) - 不要依赖
:focus或.active类做无障碍状态判断——读屏器完全看不见这些



















