:enabled能匹配的,其实是那些原生就支持disabled属性、并且当前DOM上没有写disabled的表单控件,比如input(不含hidden)、button、select、optgroup、textarea。反过来说,像div、p这类元素,或者自定义组件上手动加的disabled属性,都不会被它识别。还有一个很容易混淆的点:如果子元素处在被禁用的fieldset里,虽然已经无法交互,但它们依然会匹配:enabled。

:enabled 只匹配真正原生支持 disabled 属性、且当前 DOM 上**没有 disabled 属性**的表单控件——不是“看起来能点”,而是浏览器底层认定“可聚焦、可输入、可提交”的元素。
哪些元素能被 :enabled 匹配
仅限以下原生表单控件(且排除特例):
(type="hidden"除外)和
以下写法完全无效:
—— 浏览器不认,:enabled直接跳过整条规则或 Vue/React 中:disabled="false"但未渲染出 DOM 属性 —— CSS 无法感知响应式变量,只看真实 DOM——p标签原生不支持disabled,伪类无意义
:enabled 在 fieldset[disabled] 下的行为
这是最容易误判的场景:一个 没写 disabled,但被包裹在 里,它依然会匹配 :enabled —— 因为它的 DOM 上确实没 disabled 属性。
但用户实际无法操作它。这意味着:
- CSS 样式会按“启用”渲染(比如亮色背景),但交互被阻断
- 这不是 bug,是规范行为::enabled 描述的是元素自身的启用状态,不是祖先控制下的可用性
- 若需视觉同步,必须额外用
fieldset[disabled] input覆盖样式,或改用 JS 控制子元素的disabled属性
为什么你的 :enabled 样式没生效
常见失效路径:
- 目标元素不是上述原生控件(比如用了
contenteditable的div) - JS 动态设置
el.disabled = true,但没触发属性同步(旧版 Safari 或某些框架中,需显式调用el.setAttribute('disabled', '')) - 写了
input:hover:enabled—— 伪类顺序错误,部分浏览器(尤其 Safari)不识别,应写成input:enabled:hover - Vue 中
:disabled="loading",但loading初始为undefined→ 渲染时没生成disabled属性 →:enabled默认命中,造成“本该禁用却显示启用样式”
和 :not(:disabled) 的关键区别
表面等价,但语义与兼容性不同:
:enabled是语义化伪类,只作用于表单控件;:not(:disabled)是通用否定逻辑,对不支持disabled的元素(如p)也会匹配- IE11 中
:not(:disabled)对支持不稳定,而:enabled稳定 - 当元素被
fieldset[disabled]禁用时::enabled仍匹配(DOM 无属性),:not(:disabled)同样匹配 —— 二者在此场景下行为一致,但前者意图更清晰
真正容易被忽略的是焦点残留问题:用户 tab 进入一个刚被 JS 设为 disabled 的 input,焦点还在,但 :enabled:focus 不再生效,此时仅靠 CSS 无法还原视觉反馈,必须用 JS 监听 focusin 并手动移出焦点或加 class 补偿。
