disabled属性使按钮完全退出Tab流且无法聚焦,若需视觉禁用但键盘可达,应改用aria-disabled="true"并配合tabindex="-1"、事件拦截和CSS样式。

disabled 属性会让按钮完全退出 Tab 流
原生 button 加了 disabled 属性后,它既不能被 Tab 切入,也无法被 focus() 主动聚焦——浏览器直接把它从可访问树中剔除。这不是 bug,是规范行为。
常见错误现象:给禁用按钮加了 tabindex="-1" 或 tabindex="0",结果发现 Tab 还是跳过它;或者用 JS 调 btn.focus() 没反应。
原因很简单:disabled 优先级高于 tabindex,所有聚焦相关能力都被压制。此时 DevTools 中该元素的 Focusable: false 是必然结果。
如果你真需要“视觉禁用但键盘可达”,就不能用 disabled,得换方案:
立即学习“前端免费学习笔记(深入)”;
- 移除
disabled,改用aria-disabled="true" - 同步设
tabindex="-1"(防止它进 Tab 流) - 手动监听
keydown和click,遇到aria-disabled="true"就e.preventDefault() - 用 CSS
[aria-disabled="true"]控制灰化、cursor: not-allowed等视觉状态
aria-disabled="true" 不等于 disabled,但必须配 tabindex="-1"
aria-disabled="true" 只是告诉辅助技术“这个控件当前不可用”,它本身不阻止聚焦、不拦截事件、也不影响 Tab 顺序——你得自己补。
最容易踩的坑:只加 aria-disabled="true" 却没设 tabindex="-1",结果键盘用户 Tab 到它就卡住,按空格/回车也没响应,纯属“假禁用”。
正确做法是:
- 触发焦点捕获逻辑前,确保目标元素有
tabindex="-1"(比如模态框关闭后把焦点还给触发按钮) - 对自定义组件(如
div role="button"),禁用时必须同时设aria-disabled="true"和tabindex="-1" - 别给
aria-disabled="true"的元素设tabindex="0"——这会让键盘用户停在不可用的地方,白等
示例:<div role="button" aria-disabled="true" tabindex="-1">删除</div>
禁用状态下 hover 提示失效,不是 CSS 写错了
禁用按钮(disabled)根本不会触发 mouseover、mouseenter 或 CSS :hover——这是浏览器强制行为,和选择器写法无关。
所以这类写法注定无效:button:disabled:hover { ... } 或 $("#myBtn").hover(...)。
可行解法只有两个方向:
- 放弃
disabled,用 class 模拟禁用状态(如class="btn--disabled"),再用 CSS.btn--disabled:hover+ JS 绑定事件 - 把悬停目标移到父容器上,比如给按钮外层
div加hover监听,再根据子按钮是否禁用来决定是否显示提示
注意:模拟禁用时,必须同步处理键盘交互——加 tabindex="-1"、监听 keydown 并 preventDefault(),否则可访问性断裂。
DevTools 里 Focusable: true 才算真能 Tab 到
别信视觉 outline,也别信自己点了鼠标再按 Tab 的测试结果。真实键盘可达性,只看 DevTools Elements 面板里选中元素后的 Accessibility 标签页:
-
Focusable: true→ 当前确实能被 Tab 切入(未被disabled、inert、visibility: hidden或aria-hidden="true"拦截) -
Focused: true→ 此刻真的获得了焦点(配合 Tab 键实时验证最准)
滚动容器里焦点“消失”,大概率是容器本身没设 tabindex="0",它不可聚焦,自然不滚;Tab 到最后一个可见项就停住,不是焦点丢了,是流到头了。
真正麻烦的是那些 Focusable: false 却看着像能点的元素——比如 opacity: 0 + pointer-events: none 的 div,它不影响聚焦,但用户根本不知道焦点在哪,也按不出任何反应。



















