button:disabled 样式失效的根源在于 disabled 属性语义缺失或选择器不匹配:仅原生可禁用元素支持该伪类;空 disabled=""、自定义标签、子元素未继承等均导致失效;需显式设置 cursor: not-allowed 并避免 pointer-events: none;视觉、语义、行为必须统一由 disabled 属性驱动。

disabled 属性本身不渲染样式,真正起作用的是 :disabled 伪类——但前提是元素合法、属性真实存在、且未被更高优先级规则覆盖。
button:disabled 样式没生效?先确认 DOM 层是否真禁用
浏览器只对原生可禁用元素(<button></button>、<input type="button">、<select></select>、<textarea></textarea>、<fieldset></fieldset>)响应 :disabled。以下情况样式必然无效:
<div disabled> 或自定义组件上加 <code>disabled属性——HTML 不识别语义,CSS 完全不匹配- React/Vue 中写了
disabled={isLoading},但isLoading是undefined或null,导致 DOM 上残留空disabled="",按钮真被禁用但逻辑错乱 - 用了
<fieldset disabled></fieldset>,子<input>没带disabled属性,此时得写fieldset:disabled input而非input:disabled - 写成
button:disabled { cursor: not-allowed; },不能依赖全局重置或父级样式继承 - 旧版 Safari 对该规则支持不稳定,生产环境可加
!important(仅此处合理) - 避免同时用
pointer-events: none——它会阻止focus/blur,键盘用户无法跳过,违反无障碍要求 - 不改变颜色对比度,高对比度模式下文字可能完全不可读
- 不消除
:focus样式,键盘用户 Tab 过去时仍亮焦点框,造成“能操作”错觉 - 老版 Safari 解析
filter: grayscale()异常(比如全黑),且不联动光标和语义 - 启用时必须用
el.removeAttribute('disabled'),而非设为false字符串 - React 中避免
disabled={isLoading ? true : false},直接写disabled={isLoading} - Vue 中用
v-bind:disabled="isDisabled",并确认最终渲染的<button></button>上有该属性 - 禁用后若用户已聚焦该按钮,需手动调
el.nextElement.focus()移走焦点,否则键盘用户卡住
验证方法:打开 DevTools,手动删掉 DOM 中的 disabled 属性,看样式是否同步消失。
必须显式设置 cursor: not-allowed
:disabled 只控制交互行为(点击/聚焦/提交被阻断),不改光标。漏掉这行,用户看到灰按钮却仍显示手型,会误判可操作性。
别只靠 opacity 做禁用态
opacity: 0.6 看似简单,但问题集中:
推荐组合写法:button:disabled { background-color: #f0f0f0; color: #999; border-color: #ccc; cursor: not-allowed; outline: none; },再加 button:disabled:focus { box-shadow: none; } 彻底关闭干扰动效。
JS 动态控制时,el.disabled = true 是唯一可靠写法
用 setAttribute('disabled', 'true') 或 setAttribute('disabled', '') 在部分老浏览器中不触发行为,也不保证与 IDL 属性同步。
最常被忽略的一点:禁用状态的视觉、语义、行为三者必须由同一源头驱动——也就是 disabled 属性本身。任何用 class 模拟、靠 pointer-events 遮盖、或 JS 临时拦截 click 的做法,在键盘导航、表单提交、屏幕阅读器场景下都会暴露缺陷。











