CSS选择器:is()

简化冗余选择器

如果你有像这样的冗余的 CSS 选择器:

1
2
3
4
5
6
/* 😕 */
.active a,
.active button,
.active label {
color: steelblue;
}

你知道你可以像这样重写它吗?

1
2
3
4
/* 🤩 */
.active :is(a, button, label) {
color: steelblue;
}

没错,:is() 伪类现在已经内置到普通的 CSS 中了。

你可以使用 :is() 来组合选择器的任何部分,例如,你可以类似地转换这个:

1
2
3
4
5
.section h2,
.aside h2,
.nav h2 {
color: steelblue;
}

为仅仅这样:

1
2
3
:is(.section, .aside, .nav) h2 {
color: steelblue;
}

:is() 不仅对于父元素和子元素有用,它也可以选择多个相邻的选择器,比如:

1
2
3
4
5
6
7
button:is(:focus, :hover, :active) {
color: steelblue;
}

button:is(.active, .pressed) {
color: lightsteelblue;
}

其行为等同于:

1
2
3
4
5
6
7
button:focus, button:hover, button:active {
color: steelblue;
}

button.active, button.pressed {
color: lightsteelblue;
}

:where() 的比较

:where() 是一个非常类似的伪类,与 :is() 值得一提。它们看起来非常相似:

1
2
3
:where(.section, .aside, .nav) h2 {
color: steelblue;
}

但区别在于 :where 的特异性为0,而 :is() 总是具有列表中最具体选择器的特异性。

那么,在这个 CSS 中,你知道按钮将会是什么颜色吗?

1
2
3
4
5
6
7
:is(html) button {
color: red;
}

:where(html) button {
color: blue;
}

在上面的例子中,尽管以 :where() 开头的块位于以 :is() 开头的块下方,但 :is() 块具有更高的特异性(对于 html 标签,+1 对于按钮),而下方的块具有较低的特异性(对于 html 是0,因为它在 :where 中,+1 对于按钮)。

:has() 的比较

一个相关但非常不同的伪类是 :has():has() 允许你选择包含匹配选择器(或一组选择器)的父元素。

:has() 的一个示例用例是不要给包含图像或视频的链接添加下划线:

1
2
3
4
5
6
a { text-decoration: underline }

/* 链接会有下划线,除非它们包含图像或视频 */
a:has(img, video) {
text-decoration: none;
}

现在,如果我们的 a 标签默认有下划线的文本,但我们的其中一个里面有图像或视频,对于任何匹配的锚点元素,下划线都将被移除。

你也可以将其与 :is() 结合使用:

1
2
3
:is(a, button):has(img, video) {
text-decoration: none;
}

请注意,虽然 :has() 还不是所有主要浏览器都支持,因此请谨慎使用。

我们还需要预处理器吗?

现在你可能正在读这篇文章,并说“SCSS可以做到这一点!,你甚至可能更喜欢它的语法:

1
2
3
4
5
.active {
button, label, a {
color: steelblue;
}
}

你说得对,这确实非常优雅。但似乎每一天 CSS 在原生环境中都获得了我们曾经需要 SCSS(或其他预处理器)的功能。

CSS 变量也是CSS自身的另一个不可思议的添加,这就引出了一个问题,你到底何时或多久需要预处理器:

1
2
3
4
5
/* 纯粹的现代 CSS*/
.active :is(a, button, label) {
--color: steelblue;
color: var(--steelblue);
}

这并不是说预处理器不再有用和有价值。

但我认为在某个时候,它们确实是处理任何非常用 CSS(至少是优雅地处理)的必备工具,而现在情况已经不那么明显了。

最后一个惊喜…

CSS Working Group 正在积极努力将嵌套选择器直接添加到 CSS 中。他们正在积极地在三种可能的语法之间做出决定(称为选项“3”,“4”和“5”):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* 选项 3 */
article {
font-family: avenir;
& aside {
font-size: 1rem;
}
}

/* 选项 4 */
article {
font-family: avenir;
} {
aside {
font-size: 1rem;
}
}

/* 选项 5 */
@nest article {
& {
font-family: avenir;
}
aside {
font-size: 1rem;
}
}

你最喜欢哪一个?你的选择是否与官方调查中的获胜者相匹配?

剧透 : #3 赢得了调查,所以我们可能会很快得到一个非常类似于 SCSS 的嵌套语法,这是CSSWG选择了调查获胜者的情况下的锦上添花。

选项 4 简直是一个绝对的灾难,需要在火焰中燃烧。

浏览器支持

:is() 和 :where() 的浏览器支持

:is() 和 :where() 伪类在所有主要浏览器中都得到支持:

:has() 的浏览器支持

请注意,我们在这里提到的 :has() 伪类并没有同样级别的支持,因此请谨慎使用 :has()

下次你的代码中有冗余选择器时,不要忘记使用方便的:is()伪类。