aria-disabled="true"仅向屏幕阅读器声明禁用状态,不阻止交互,必须配合tabindex="-1"、pointer-events:none和js事件拦截才能真正禁用;原生disabled则自动屏蔽焦点、点击和提交。

aria-disabled 不是禁用开关,它只是“告诉屏幕阅读器:这个东西现在不能点”,但点击、聚焦、提交照样发生——真要拦住用户,必须手动补全三件事:tabindex="-1"、pointer-events: none、JS 事件拦截。
aria-disabled="true" 和 disabled 的根本区别
原生 disabled 是功能+语义一体的硬禁用:浏览器直接屏蔽焦点、点击、表单提交,样式自动灰化,屏幕阅读器读作“已禁用”。aria-disabled="true" 只是往无障碍 API 里塞一条状态提示,DOM 行为完全不受影响。
常见翻车现场:
- 给
<div> 加 <code>aria-disabled="true"就以为完事了,结果键盘用户 tab 进去按回车,照样触发 click - 在 React 里写
aria-disabled={isDisabled},布尔false渲染成aria-disabled="false"—— 这个值不仅没用,还可能误导读屏器 - 同时写
disabled和aria-disabled="true",造成冗余甚至冲突,某些读屏器会报“状态矛盾”警告 - 必须加
tabindex="-1",否则键盘用户仍能聚焦 - 必须用 CSS 写
[aria-disabled="true"] { pointer-events: none; opacity: 0.5; },否则鼠标还能点 - JS 中必须监听
click和keydown,检查el.getAttribute('aria-disabled') === 'true'后调用event.preventDefault() - React:
aria-disabled={isDisabled ? "true" : undefined}(别用String(isDisabled)) - Vue:
:attr="{ 'aria-disabled': isDisabled ? 'true' : null }"(别用v-bind:aria-disabled="isDisabled") - 永远避免在模板里硬编码
aria-disabled="false"
哪些元素必须用 aria-disabled 而不是 disabled
只有原生表单控件(button、input、select、textarea)才识别 disabled 属性;div、span、a、li 等非表单元素压根不响应 disabled,写了也白写。
这类元素想模拟禁用,aria-disabled="true" 是必要起点,但绝非充分条件:
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
React/Vue 动态绑定 aria-disabled 的坑
框架里容易把 aria-disabled 当普通布尔 prop 绑定,结果渲染出无效的 aria-disabled="false" 字符串。WAI-ARIA 规范明确:没有该属性等价于 false,显式写 "false" 反而可能被误读。
正确写法:
为什么 button 元素上不该用 aria-disabled
button 原生支持 disabled,直接用它最稳妥。加 aria-disabled="true" 不仅多余,还容易引发状态不同步——比如 JS 改了 disabled 属性,却忘了同步更新 aria-disabled,读屏器和视觉状态就对不上。
唯一例外是自定义组件封装了 button 但对外暴露了非原生接口(比如通过 role="button" 模拟),此时才需手动接管 aria-disabled 并配齐 tabindex、CSS、JS 三件套。
真正麻烦的从来不是怎么写属性,而是忘了它本身不拦交互——只要漏掉 tabindex="-1" 或 pointer-events: none,禁用就只是障眼法。










