为什么 :is(::before, ::after) 不能工作?

在 CSS 中,:is() 是一个非常实用的函数型伪类,它可以帮助我们简化和合并选择器。很多 Web 开发者都会用它来减少重复代码,让选择器更简洁、更易读。

例如:

CSS 复制代码
button.large,
button.small {
    /* CSS */
}

可以使用 :is() 改写为:

CSS 复制代码
button:is(.large, .small) {
    /* CSS */
}

这样既保持了相同的效果,又让代码更加紧凑。因此,很多人会把 :is() 理解为一种 "合并选择器"的工具

不过,当你对 :is() 越来越熟悉时,可能会产生一个看起来很合理的想法:既然 :is() 可以合并选择器,那是否也可以用来同时选择元素的伪元素?例如:

CSS 复制代码
button:is(::before, ::after) {
    /* CSS */
}

从直觉上看,这似乎是在说:选择 button ::before ::after 。但实际上,这段代码不会生效 。浏览器会直接忽略它,因为在 CSS 规范中,伪元素是不允许写在 :is() 里的

这就引出了一个非常有意思的问题:为什么 :is(::before, ::after) 不能工作? 要理解这个问题,我们需要先弄清楚一件事: :is() 在 CSS 选择器中到底是如何工作的。 很多开发者对它的理解,其实和浏览器真正的解析方式并不完全一样。

在接下来的内容中,我们会一步一步拆解:

  • :is() 的真实作用是什么

  • 为什么伪元素不能出现在 :is()

  • 为什么 :is(:hover, :focus) 是合法的,而 :is(::before, ::after) 却不是

  • 以及如何正确地编写涉及伪元素的选择器

理解这些规则之后,你不仅能避免一个常见的 CSS 坑,还能更准确地理解 CSS 选择器的工作方式

:is() 的真实作用是什么?

很多人第一次接触 :is() 时,都会把它理解为一种用来合并选择器的语法糖。例如:

CSS 复制代码
button.large,
button.small {
    /* CSS */
}

可以写成:

CSS 复制代码
button:is(.large, .small) {
    /* CSS */
}

从结果上看,两种写法确实是等价的,因此很容易让人产生一个印象: :is() 的作用就是把多个选择器合并成一个。但实际上,这只是表面现象。

在 CSS 选择器中,:is() 的真实作用并不是"展开选择器",而是为当前元素增加匹配条件 。换句话说,:is() 本身是一个伪类(pseudo-class) 。它的行为和 .class#id:hover 这些选择器类似,都是在为元素增加匹配规则。

例如:

CSS 复制代码
button:is(.large, .small) {
    /* CSS */
}

正确的理解方式是:选择所有 既是 button 元素,同时又匹配 :is() 内部条件的元素。也就是:

  • 元素必须是 button

  • 同时具有 .large .small

因此,它并不是先被浏览器转换成:

CSS 复制代码
button.large,
button.small {
    /* CSS */
}

再去匹配元素,而是浏览器直接按照选择器规则进行匹配判断

理解这一点非常重要,因为它会影响我们如何阅读选择器。例如:

CSS 复制代码
:is(.card, .panel) {
    /* CSS */
}

表示:选择任何 匹配 .card .panel 的元素。 而如果写成:

CSS 复制代码
button:is(.card, .panel) {
    /* CSS */
}

含义就变成:选择 既是 button 元素,同时又匹配 .card .panel 的元素。

因此可以总结为一句话: :is() 用来为当前元素增加一组"可选的匹配条件"。 它并不会改变选择器的目标,也不会去"选择"括号里的内容,而只是判断当前元素是否满足其中任意一个条件

正是因为这个机制,才会导致一个容易让人困惑的现象:伪元素(例如 ::before ::after )不能写在 :is() 里面。

为什么伪元素不能出现在 :is() 中?

理解这个问题的关键,仍然是 :is() 的作用。正如前面提到的,:is() 是一个伪类,它只是为当前元素增加匹配条件 。也就是说,它是在判断:当前元素是否满足括号中的某个选择器

例如:

CSS 复制代码
button:is(.large, .small) {
    /* CSS */
}

意思是:选择既是 button 元素,同时又匹配 .large.small 的元素。换句话说,:is() 只是用来判断 button 元素是否符合这些条件

现在再来看这个选择器:

CSS 复制代码
button:is(::before, ::after) {
    /* CSS */
}

如果按照同样的逻辑去阅读,它的含义就变成:选择既是 button 元素,同时又匹配 ::before ::after 的元素

问题就在这里,元素不可能是伪元素:

  • button 是一个 真实的 DOM 元素

  • ::before::after伪元素

伪元素并不是独立的节点,而是附属于某个元素生成的内容。因此,一个元素不可能同时既是元素又是伪元素。所以这个选择器的条件永远不可能成立,浏览器也就不会匹配到任何内容。

这也是为什么 :is() 中只允许写普通选择器或伪类 ,而不能写伪元素。例如下面的写法是完全合法的:

CSS 复制代码
button:is(:hover, :focus, :active){
    /* CSS */
}

它的意思是:选择所有 处于 :hover :focus :active 状态的 button 元素 。因为 :hover:focus:active 都是伪类,只是描述元素状态,因此可以作为匹配条件使用。

简单来说,可以记住这样一条规则: :is() 用来给元素增加匹配条件,而不是用来选择伪元素。 同样的限制也适用于另外两个伪类 :not():where()

温馨提示:有关于 :is():not():where() 选择更详细的介绍,请移步阅读《CSS 选择器::where() vs. :is()》!

如何正确地编写涉及伪元素的选择器?

既然 :is() 不能包含伪元素,那么当我们需要同时为多个伪元素编写样式时,应该怎么写?最常见、也是最推荐的方法,就是使用逗号分隔的选择器列表

例如:

CSS 复制代码
button::before,
button::after {
    content: "";
    position: absolute;
}

这种写法虽然稍微长一点,但它清晰、直观,而且完全符合 CSS 规范。

如果多个选择器的主体部分相同,而只有伪元素不同,也通常只能这样写:

CSS 复制代码
.card::before,
.card::after {
    content: "";
    position: absolute;
}

这里实际上是在选择.card::before.card::after。而不是试图把伪元素放进 :is() 中。

不过,我们仍然可以在元素部分使用 :is() ,然后在最后添加伪元素。例如:

CSS 复制代码
:is(button, a)::before {
    content: "";
}

这个选择器的意思是:选择 buttona 元素的 ::before 伪元素。同样的思路也可以用于类选择器:

CSS 复制代码
:is(.card, .panel)::after {
    content: "";
}

表示 .card::after.panel::after

因此,可以记住一个简单的经验法则: :is() 可以用来匹配元素,但伪元素必须写在选择器的最后。 例如:

CSS 复制代码
/* ✅ 正确写法 */
:is(button, a)::before {
    /* CSS */
}

/* ❌ 错误写法 */
button:is(::before, ::after) {
    /* CSS */
}

总结

:is() 是一个非常强大的 CSS 工具,它可以帮助我们减少重复选择器,让代码更简洁。但需要记住两点关键规则:

  • :is() 用于匹配元素条件,而不是选择伪元素

  • 伪元素必须始终写在选择器的最后

理解这一点之后,你不仅可以避免 :is(::before, ::after) 这样的常见错误,还能更深入地理解 CSS 选择器的匹配机制

相关推荐
aXin_li2 小时前
从原子化到工程化:Tailwind CSS 的深层价值与实践思考
前端·css
IT_陈寒2 小时前
用Python爬虫抓了100万条数据后,我总结了这5个反封禁技巧
前端·人工智能·后端
qq_411262422 小时前
AP模式中修改下wifi名称就无法连接了,分析一下
java·前端·spring
BUG创建者2 小时前
uniapp 开发app时播放实时视频海康ws的流数据
前端·javascript·vue.js·uni-app·html·音视频
我是苏苏2 小时前
Web开发:使用MediatR包实现中介者模式,避免组件之间直接通信
前端·中介者模式
Highcharts.js2 小时前
数据可视化不仅属于金融、互联网|农业数据可视化设计:Farmable与Highcharts的前端设计
前端·信息可视化·数据可视化·highcharts·农业可视化
JuneXcy2 小时前
node(2)
开发语言·前端·javascript·http·node.js
A923A2 小时前
【Vue3大事件 | 项目笔记】第四天
前端·vue.js·笔记·前端项目
木斯佳2 小时前
前端八股文面经大全:拓竹科技前端一面(2026-03-15)·面经深度解析
前端·css·面试·vue