正确写法是.btn:is(:hover, :focus, :active),因冒号紧贴类名表示按钮自身状态;加空格会误匹配内部元素,混写完整路径则丧失语义抽象,且需注意伪元素禁用、权重取最高值、Safari≤15.3整条规则丢弃等限制。

:is() 不能简化深层嵌套结构本身,但它能干净地合并同一层级上的伪类组合 —— 比如把 .btn:hover、.btn:focus、.btn:active 这类重复前缀的交互状态,压缩成一个可读、可维护的表达式。
为什么直接写 .btn:is(:hover, :focus, :active) 才正确?
常见错误是加空格:.btn :is(:hover, :focus)(带空格)会匹配「.btn 内部的 hover 元素」,根本不是按钮自身的状态;或者写成 :is(.btn:hover, .btn:focus),虽然合法,但失去语义抽象,改类名时仍要改多处。
-
.btn:is(:hover, :focus, :active)是标准写法:冒号紧贴类名,括号内是伪类列表,语义即“按钮的任一交互态” - 浏览器对伪类组合支持良好(Chrome 88+、Firefox 78+、Safari 15.4+),但 Safari ≤15.3 会整条规则丢弃
- 不能写
.btn:is(:hover::before)—— 伪元素不允许出现在:is()内,否则整条规则静默失效
:is() 套伪类时权重怎么算?
权重取括号内最高者。比如 .btn:is(:hover, :focus-visible, :active) 的权重等同于单独写 .btn:focus-visible(0,1,1),而不是平均或累加。这容易导致意外覆盖:
- 原本
.btn:hover(0,1,1)和.btn.disabled:hover(0,2,1)能分层控制,换成.btn:is(:hover, :focus)后,权重没变,但后续加.disabled:is(:hover, :focus)就得手动确保顺序和 specificity 对齐 - 如果混进 ID 伪类(极少但可能),比如
#main-btn:is(:hover, :focus),权重直接跳到 (0,1,1),和#main-btn:hover等价 —— 别指望它“变轻”
深层嵌套中如何安全用 :is() 组合状态?
在像 .card .content a:hover 这类多层路径里,:is() 只能作用于最末端或中间某一层,不能跨层“拉平”。典型安全用法:
立即学习“前端免费学习笔记(深入)”;
- 锚定末端状态:
.card .content a:is(:hover, :focus)—— 替换重复的a:hover/a:focus,不碰前面路径 - 合并父级容器 + 统一状态:
:is(.card, .modal) .header a:is(:hover, :focus)—— 要求 .card 和 .modal 下的 .header 结构一致,否则语义断裂 - 绝对避免:
:is(.card a:hover, .modal a:focus)—— 括号内是两个完整路径,浏览器当两个独立选择器处理,且任一非法(如旧 Safari 不认:focus)整条失效
真正容易被忽略的是兼容性兜底:Safari ≤15.3 不仅不支持 :is(),连 :matches() 都不支持伪类组合,所以关键交互样式必须写两遍 —— 先用传统逗号分隔,再用 :is() 覆盖,且不能依赖构建工具自动降级,因为 postcss-preset-env 默认转出的长列表反而增大体积。


















