CSS :has() 交互指南


原文 CSS :has() Interactive Guide

1. 什么是 :has()?

:has() 常被称为 “父级选择器”。它允许你根据一个元素内部是否包含某些子元素(或符合某种条件的后代元素)来给该元素本身设置样式。 本文将会探讨此问题,并介绍 :has() 的一些常见用法。

1.1 痛点

假设我们有一个 <figure> 标签,当它包含 <figcaption> 标签时,我们希望给 <figure> 添加一个样式。我们应该如何实现呢?

1
2
3
4
<figure>
<img src="thumb.jpg" alt="" />
<figcaption>A great looking tart.</figcaption>
</figure>

当有说明文字时,我希望图片显示以下效果:

  • Paddding
  • Background
  • Shadow

在 CSS 中,唯一可行的方法是给 <figure> 标签手动添加一个类名(class),然后通过该类名来选择其中的 <figcaption>

1
2
3
4
5
6
figure.with-caption {
padding: 0.5rem;
background-color: #fff;
box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.1);
border-radius: 8px;
}

如果 HTML 是由后台编辑器或 Markdown 引擎动态生成的,你很难手动给包含标题的图片(figure)精准地加上 .with-caption 这个类。

1.2 解决办法

有了 CSS :has() 选择器,就好办多了。你可以像下面这样写 CSS:

1
2
3
4
5
figure:has(figcaption) {
padding: 0.5rem;
background-color: #fff;
border-radius: 8px;
}
1
2
3
4
<figure>
<img src="thumb.jpg" alt="" />
<figcaption>A great looking tart.</figcaption>
</figure>

而这仅仅是使用 CSS :has() 的冰山一角。

复习 CSS 选择器

在我们深入之前,先来复习一下 CSS 选择器。

2.1 相邻兄弟选择器

要选择元素的下一个同级元素,我们可以使用相邻兄弟选择器 (+)。用于选择紧接在另一个元素后的元素,且二者有相同的父元素。

1
2
3
4
5
6
7
.book {
opacity: 0.2;
}

.frame + .book {
opacity: 1;
}

2.2 通用兄弟选择器

要选择元素的下一个同级元素,我们可以使用通用兄弟选择器 (~)。用于选择紧接在另一个元素后的元素,且二者有相同的父元素。

1
2
3
4
5
6
7
.book {
opacity: 0.2;
}

.frame ~ .book {
opacity: 1;
}

2.3 前一个兄弟选择器

借由 :has(),我们现在可以实现选择 “后面紧跟 B 的 A 元素”,从而间接实现了前一个兄弟选择器的功能。

以下代码实现如下功能:选中一个 .book 元素,前提是它的紧后方(+)紧跟着一个 .frame 元素。

1
2
3
4
5
6
7
.book {
opacity: 0.2;
}

.book:has(+ .frame) {
opacity: 1;
}

基于上述,我们可以选择某个特定元素前的所有元素。以下代码实现选择 .frame 元素之前所有 .books 元素。

1
2
3
4
5
6
7
.book {
opacity: 0.2;
}

.book:has(~ .frame) {
opacity: 1;
}

2.5 :not 伪类

:not() 伪类选择器在排除某个元素时非常有用。比如,当我们想要选择所有不包含 .blue 的 .book 元素时,可以这样写:

1
2
3
.book:not(.blue) {
opacity: 1;
}