aria-disabled="true"不能保留聚焦,因为它仅声明语义禁用状态,若同时设tabindex="0"会导致屏幕阅读器读“已禁用”却可操作,违反wcag;正确做法是配tabindex="-1"并配合pointer-events: none等样式。

不能。aria-disabled="true" 本身不提供聚焦控制,且“保留聚焦”与“视觉变灰”在语义上冲突——如果元素仍可聚焦,它就不该被标记为禁用状态,否则会误导屏幕阅读器和键盘用户。
aria-disabled="true" 为什么不能保留聚焦
aria-disabled 是无障碍语义标记,告诉辅助技术“这个控件当前不可用”。一旦你声明 aria-disabled="true",屏幕阅读器就会读作“已禁用”,但如果你又允许它被 tab 聚焦,用户听到“已禁用”却能按回车触发操作,这就构成严重语义矛盾。
- 浏览器不会因 aria-disabled 改变焦点行为:div、span 等默认不可聚焦,加了 aria-disabled="true" 也不会自动获得 focus 能力
- 若你手动加 tabindex="0" 让它可聚焦,再配 aria-disabled="true",等于一边说“不能用”,一边说“请聚焦我”——WCAG 会报错
- 真正需要“视觉灰化但保持可聚焦”的场景,通常属于“临时不可用但状态待激活”,比如表单校验未通过时的提交按钮,此时应使用
disabled属性 + 自定义 :disabled 样式,而非 aria-disabled
想灰化又可聚焦?用 disabled + CSS 覆盖默认样式
原生 disabled 属性默认会移除焦点能力,但你可以用 tabindex="-1" 强制让它失去焦点,再用 tabindex="0" 恢复——但这违背禁用本意。更合理的做法是:不用禁用,改用其他状态标识。
- 不要给按钮设
aria-disabled="true",而用自定义 data 属性,比如data-validation-pending="true" - CSS 中用
button[data-validation-pending="true"]控制灰度、cursor: not-allowed,但不设tabindex或移除tabindex="0" - JS 中拦截 click 和 keydown(Enter/Space),并提示用户缺失字段,而不是假装“已禁用但可聚焦”
- 若必须保留 tab 键导航流,把焦点移到第一个错误字段,而不是停在“灰掉的按钮”上
aria-disabled 配 tabindex="-1" 是标准做法,不是可选项
当你真要用 aria-disabled="true"(例如在 <div role="button"> 上),必须同步设置 <code>tabindex="-1"。这不是“为了兼容”,而是语义强制要求:禁用状态意味着不可交互,而可聚焦就等于可交互入口。
-
tabindex="-1"不仅阻止 tab 进入,也防止 keyboard 用户用方向键或回车意外触发 - 漏掉这一步,键盘用户 tab 到该元素后按回车,JS 里没拦截的话,照样执行 click handler
- CSS 必须配套
[aria-disabled="true"] { pointer-events: none; opacity: 0.5; cursor: not-allowed; },否则鼠标仍能点 - React/Vue 中绑定
aria-disabled={isDisabled}时,确保 isDisabled 为 false 时彻底移除该属性,而不是渲染aria-disabled="false"——后者会被读屏器误读
最常被忽略的不是怎么写 aria-disabled,而是禁用状态是否随业务逻辑实时同步:比如用户填完字段,JS 忘记移除 aria-disabled="true" 和 tabindex="-1",焦点就卡死在不可操作节点上,整个流程中断。这种问题无法靠样式修复,只能靠状态管理闭环。











