CSS:has()指南

6/6/2024 CSS

# 写在前面

本篇文章是翻译学习原文链接 (opens new window)。查看原文可以更好的查看相应的属性效果。

# 介绍

我们一直希望在 CSS 中能够根据元素的后代来设置元素的样式。直到所有主流浏览器都支持 CSS :has() 后,这才得以实现。在本文中,我将探讨这个问题,并展示 CSS :has() 的一些有趣用例。

# 问题

假设我们有一个 figure 标签,如果它有一个 figcaption 子元素,我们想给它设置不同的样式。如何在 CSS 中实现?

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

参见以下演示。尝试切换“显示标题”。

点击查看效果 (opens new window)

当有标题时,我希望图形具有以下内容:

  • padding 边距
  • background 背景色
  • shadow 阴影

CSS 中唯一可能的方法是给 figure 赋予一个类,然后从该类中选择 figcaption。

<template>
    <div class="figure-wrap">
        <el-checkbox v-model="showCaption">展示标题</el-checkbox>
        <figure :class="[showCaption ? 'with-caption' : '']">
            <img class="img" src="@/assets/images/logo.jpeg" alt="" />
            <figcaption v-if="showCaption">A great looking tart.</figcaption>
        </figure>
    </div>
</template>
<script lang="ts" setup>
    const showCaption = ref(false)
</script>
<style lang="less" scoped>
    .figure-wrap {
        display: flex;
        flex-direction: column;
        align-items: center;
        background-color: #2196f317;
        padding: 1rem;
        border-radius: 8px;
        min-height: 180px;
        .img {
            width: 300px;
            height: 300px;
        }
    }
    figure.with-caption {
        padding: 0.5rem;
        background-color: #fff;
        box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.1);
        border-radius: 8px;
    }
</style>
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
27
28
29
30
31
32
33

# 解决

使用 CSS :has() 选择器,这是可能的。我们可以执行以下操作。这就像说如果 figure 包含 figcaption,则按该方式设置样式。 当 figcaption 显示时,figure 就会设置相应样式。

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

这只是简单介绍了我们可以使用 CSS :has() 解决的问题的表面。

# css 选择器回顾

在深入研究 :has() 的一些有趣的 CSS 选择器之前,让我们先快速回顾一下它们。

# 相邻兄弟选择器

要选择元素的下一个同级元素,我们可以使用相邻同级选择器 (+)。

.book {
    opacity: 0.2;
}

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

# 通用兄弟选择器

要选择元素的所有下一个兄弟元素,我们可以使用通用兄弟选择器 (~)。

.book {
    opacity: 0.2;
}

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

# 前一个兄弟选择器

使用 CSS :has(),我们可以进一步利用上述内容,选择一个元素的前一个兄弟元素。

.book {
    opacity: 0.2;
}

.book:has(+ .frame) {
    opacity: 1;
}
1
2
3
4
5
6
7

在上述内容的基础上,我们可以选择特定元素的所有前面的元素。在我们的例子中,我们想要选择 .frame 元素之前的所有 .book。

.book {
    opacity: 0.2;
}

.book:has(~ .frame) {
    opacity: 1;
}
1
2
3
4
5
6
7

# 子组合选择器

> 选择器仅在元素是直接子元素时才选择该元素。例如,使用 .section > h1 仅当 h1 是该节的直接子元素时,才会选择该元素。 例如

<div class="box">
    <div class="book"></div>
    <div class="book"></div>
    <div class="book"></div>
</div>
1
2
3
4
5
.box > .book {
    opacity: 1;
}
1
2
3

# :not 伪类

CSS 中的 :not() 选择器可用于从选择中排除元素。例如,.section:not(.featured)。下面的演示就是选择除 .blue 之外的所有元素。

<div class="book"></div>
<div class="book"></div>
<div class="book blue"></div>
<div class="book"></div>
1
2
3
4
.book:not(.blue) {
    opacity: 1;
}
.blue {
    background-color: blue;
}
1
2
3
4
5
6

以上就是选择器回顾的全部内容。

# CSS :has() selectors matching

学习阅读 CSS :has() 选择器很有用。在本节中,我将介绍几个示例并向您展示如何使用它们。

# 带图像的卡片

在此示例中,我们有一张卡片,其中包含一个图像作为子元素。我们可以使用 CSS :has() 进行检查。

<div class="card-img-wrap">
    <el-checkbox v-model="showImg">展示图片</el-checkbox>
    <div class="card">
        <img v-if="showImg" src="@/assets/images/login_human.png" alt="" />
        <div class="card-content">
            <span>.card:has(> img)</span>
            <i :class="['iconfont', showImg ? 'icon-success' : 'icon-fail']"></i>
        </div>
    </div>
</div>
1
2
3
4
5
6
7
8
9
10
.card {
    text-align: center;
    .card-content {
        i {
            color: red;
        }
    }
}
.card:has(img) {
    .card-content {
        span {
            font-weight: bold;
            color: #2f91f4;
        }
        i {
            color: green;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

如果我们想要一个选择器,只有当图像是卡片的直接子元素时才匹配,该怎么办?当存在 .card-thumb 元素时,它不会匹配。

.card:has(> img) {
    .card-content {
        span {
            font-weight: bold;
            color: #2f91f4;
        }
        i {
            color: green;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 不带图像的卡片

CSS :has() 可以与 :not() 选择器结合使用。在这种情况下,卡片选择器只有在没有图像的情况下才会匹配。

.card:not(:has(img)) {
    .card-content {
        span {
            font-weight: bold;
            color: #2f91f4;
        }
        i {
            color: green;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 相邻兄弟和 :has

想要选择.frame 后面跟着.book-purple 的的 .shelf。

.shelf:has(.frame + .book-purple) {
}
1
2

举一反三,以下组合你都能知道什么意思吗?

.shelf:has(.box > .book) {
}
.box: has(.book: nth-last-child(n + 3)) {

}
1
2
3
4
5

# CSS 中的逻辑运算符 :has

使用 CSS :has(),我们可以模拟逻辑运算符,如“&&”和“||”。

  • &&
.shelf:has(.book-purple):has(.book-yellow) {
    outline: dashed 2px deeppink;
}
1
2
3
  • ||
.shelf:has(.book-purple, .book-yellow) {
    outline: dashed 2px deeppink;
}
1
2
3

# 查看 has()逻辑运算符实现的神奇功能

动态改变菜单布局,查看更多 🔗 (opens new window)

# 其他

CSS :has() 是一项强大的功能,它开启了许多以前不可能实现的可能性。它确实赋予了我们 CSS 超能力!我建议您今天就开始使用它并进行尝试。

# 参考文章

上次更新: 6/7/2024, 3:17:24 PM