先记住这个答案
:enabled 和 :disabled 是一对状态伪类,专门选中可禁用的表单元素。HTML 规范里的可禁用元素主要是五种表单控件,分别是 <input> 和 <button>,以及 <select> 和 <textarea>,还有 <fieldset>。只有这些元素才响应 disabled 属性,也只有它们能被这对伪类匹配。<a> 标签不属于可禁用类型,即使手动写上 disabled,:disabled 对它也不起作用。
- 只对可禁用的表单元素生效
<a>写disabled不会被匹配<fieldset>禁用会连带子控件:enabled要求元素本身可禁用
表单控件和链接的对比
拿一个输入框和一个链接做对比,就能直接看到效果。输入框加上 disabled 后变灰,:disabled 的样式生效;链接写了 disabled 属性,:disabled 的样式完全不会应用。
disabledHTML<style>
input:enabled { border: 2px solid green; }
input:disabled { border: 2px solid gray; opacity: 0.5; }
/* 下面这条永远不会生效 */
a:disabled { color: red; text-decoration: line-through; }
</style>
<input type="text" placeholder="正常输入框">
<input type="text" disabled placeholder="已禁用">
<a href="/page" disabled>这个链接写了 disabled</a>打开开发者工具看 Styles 面板,input:disabled 的规则正常命中第二个输入框,而 a:disabled 虽然写了,但不会被任何元素匹配。链接看起来还是正常的蓝色可点击状态,disabled 属性在这里纯粹是多余的。
原因在于 HTML 规范里 <a> 没有 disabled 这个合法属性。浏览器虽然不会报错,但属性解析器不会把它当回事。CSS 选择器引擎在判断 :disabled 时,先看元素是不是可禁用类型,<a> 不在名单上,直接跳过。
<fieldset> 的连锁禁用
<fieldset> 比较特殊,它自己是可禁用元素,而且禁用之后,里面所有的表单控件都跟着变成禁用状态,不用一个个加 disabled。这在表单的"只读模式"场景下很有用,比如用户查看已提交的订单时,整个表单禁用就行。
<fieldset> 后子控件的表现HTML<fieldset disabled>
<legend>收货信息(不可编辑)</legend>
<input type="text" value="张三">
<select>
<option>北京</option>
</select>
<button type="button">修改</button>
</fieldset>这里 <input> 和 <select> 还有 <button> 都没写 disabled,但因为外面的 <fieldset> 禁用了,它们全部变成禁用状态,:disabled 都能匹配到。
有一个例外:第一个 <legend> 里面的控件不受影响。如果把输入框放在 <legend> 里,它依然是可用的。这个设计是为了让用户还能操作表单组的标题部分,比如切换展开或折叠。
容易答错的地方
- 以为给
<a>写了disabled就能用:disabled选中 disabled对<a>不是合法属性。浏览器不会因为手动加了它就让链接变成禁用状态。想让链接看起来不可点击,需要用其它方式:加pointer-events: none阻止鼠标点击,加aria-disabled="true"告诉辅助技术,再用属性选择器[aria-disabled="true"]写样式。- 以为
:enabled等于"没加disabled" :enabled只匹配可禁用元素中没有被禁用的那些。一个<div>既不匹配:enabled也不匹配:disabled,因为它压根不在可禁用元素的范围里。写div:enabled不会选中任何<div>。
面试官还会怎么问?
怎么用纯 CSS 让链接看起来被禁用了?
给链接加 aria-disabled="true" 属性,然后用 a[aria-disabled="true"] 选择器设置灰色文字和 pointer-events: none。视觉上不可点击,屏幕阅读器也能识别。不过键盘用户按 Enter 还是能触发导航,真正拦截需要在 JavaScript 里处理。
:disabled 和 [disabled] 属性选择器有什么区别?
:disabled 只匹配可禁用元素,语义更准确。[disabled] 是属性选择器,任何元素只要写了 disabled 属性都会被选中,包括 <a disabled> 这种不合法的用法。正常表单场景下两者效果一样,但 [disabled] 会"误选"非表单元素。
<fieldset> 禁用后,里面嵌套的 <fieldset> 还能单独启用吗?
不能。外层 <fieldset> 设了 disabled,里面所有控件都是禁用的,即使内层 <fieldset> 没写 disabled 也一样。内层 <fieldset> 本身也会被 :disabled 匹配。要让部分控件可用,只能把它们放到外层的第一个 <legend> 里面。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。