CSS3之has函数的使用
# W3C的选择器
# 相关的html新标签
figure、figcaption、pre、blockquote
<figure>
<img src="image.png" alt="">
<figcaption>caption and descriptions</figcaption>
</figure>
<figure>
<figcaption>code</figcaption>
<pre>
function log(val) {
console.log(val)
}
log('hello world')
</pre>
</figure>
<figure>
<figcaption>Shakespeare: </figcaption>
<blockquote>Nutrition books in the world. There is no book in life, there is no sunlight; wisdom without
books, as if the birds do not have wings.</blockquote>
</figure>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# :has的使用
用一句话来描述: :has()
选择器是一个关系型伪类选择器,也被称为函数型伪类选择器,它和 :is()
、:not()
以及 :where()
函数型选择器被称为 CSS 的逻辑组合选择器。
/* 卡片不带标题的样式规则 */
.card {
display: flex;
border-radius: 10px;
max-width: 40vw;
background-color: #F5F5F5;
}
.card img {
display: block;
max-width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
object-position: center;
}
/* 卡片带标题的样式规则 */
.card:has(figcaption) {
flex-direction: column;
gap: 1rem;
padding: 20px;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
有趣的知识:对比一下区别
section:not(:has(h1, h2, h3, h4, h5, h6))
section:has(:not(h1, h2, h3, h4, h5, h6))
1
2
2
section:not(:has(h1, h2, h3, h4, h5, h6))
语法分析:
- section:选择所有 section 元素。
:not()
:是一个排除伪类,它会排除符合条件的元素。:has(h1, h2, h3, h4, h5, h6)
:表示选中那些包含<h1>
到<h6>
的 section 元素。
所以这个选择器的意思是:
- 选择那些 没有包含任何
<h1>
到<h6>
元素的 section 元素。
关键点:
- 它选择的是 不含标题标签(即
<h1>
至<h6>
)的 section 元素。
section:has(:not(h1, h2, h3, h4, h5, h6))
语法分析:
section:选择所有 section 元素。
:has()
:这个伪类用来选中那些包含符合特定条件的子元素的父元素。:not(h1, h2, h3, h4, h5, h6)
:表示选中那些 不包含<h1>
到<h6>
元素的子元素。
所以这个选择器的意思是:选择那些 包含至少一个不属于
<h1>
到<h6>
的子元素 的 section 元素。
关键点:
- 它选中的是那些**含有其他元素(非标题元素)**的 section,并不要求这些 section 内部完全没有
<h1>
到<h6>
,但是要求必须有其他非标题元素。
举个更明确的例子
<section>
<p>This section has no headings, just a paragraph.</p>
</section>
<section>
<h2>Title</h2>
<p>This section has a heading.</p>
</section>
<section>
<h1>Title</h1>
<div>Some other content</div>
</section>
<section>
<h1>Title</h1>
<h3>Another Title</h3>
</section>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
第一个选择器:section:not(:has(h1, h2, h3, h4, h5, h6))
- 会选中 第一个 section,因为它没有任何标题标签(没有
<h1>
到<h6>
)。 - 第二个 section 被排除,因为它含有
<h2>
,符合:has()
的条件。 - 第三个 section 被排除,因为它包含
<h1>
,符合:has()
的条件。 - 第四个 section 被排除,因为它包含
<h1>
和<h3>
,符合:has()
的条件。
第二个选择器:section:has(:not(h1, h2, h3, h4, h5, h6))
- 会选中 第一个 section,因为它包含了
<p>
,这是一个不是标题的元素。 - 第二个 section 被选中,因为它有
<p>
,并且<p>
不是标题元素。 - 第三个 section 被选中,因为它包含
<div>
,而<div>
不是标题元素。 - 第四个 section 不会被选中,因为它只有标题元素(
<h1>
和<h3>
),没有其他非标题元素。
主要区别:
section:not(:has(h1, h2, h3, h4, h5, h6))
:它选择的是没有包含任何标题元素的 section 元素。section:has(:not(h1, h2, h3, h4, h5, h6))
:它选择的是 包含其他类型元素(非标题)的 section 元素。
总结:
- 第一个选择器更严格,它排除了所有包含标题的 section 元素。
- 第二个选择器则是宽松一点,它只要求 section 中必须有其他类型的元素,而不要求标题必须不存在。
# 组合选择器
“组合选择器”是一种特殊字符,表示选择器之间的关系类型。常见的组合选择器主要有:
- 空格字符,例如 a b ,称之为后代组合选择器,匹配直接或嵌套的子元素;
>
字符,例如a > b
,称之为直接子元素组合选择器,仅匹配顶层未嵌套的子元素;+
字符,例如a + b
,称之为相邻兄弟组合选择器,仅匹配紧随其后的下一个兄弟元素;~
字符,例如a ~ b
,称之为通用兄弟组合选择器,匹配基础选择器(a)
之后的一个或多个兄弟元素(b)
。
# 伪类选择器
伪类元素使用了两个冒号 (::)
而不是一个冒号 (:)
,这是 CSS3 规范中的一部分要求,目的是为了区分伪类和伪元素,大多数浏览器都支持下面这两种表示方式。
- 状态伪类选择器,比如
:hover
、:active
、:visited
、:target
和:focus
等; - 焦点伪类选择器,比如
:focus-within
和:focus-visible
等; - 结构型伪类选择器,比如
:nth-child
、:nth-last-child
、:first-child
、:last-child
、:only-child
、:nth-of-type
、:nth-last-of-type
、:first-of-type
、:last-of-type
和:only-of-type
等; - 表单伪类选择器,比如
:autofill
、:enabled
、:disabled
、:read-only
、:read-write
,:placeholder-shown
、:default
、:checked
、:indeterminate
、:valid
、:invalid
、:in-range
、:out-of-range
、:required
和:optional
等; - 目标伪类选择器,比如
:target
; - 语言伪类选择器,比如
:dir()
和:lang()
等; - 函数伪类选择器,比如
:not()
、:is()
和:where()
等。
has的使用,另外,需要注意的是,CSS 工作组决定禁止在 :has()
内部使用所有现有的伪元素,比如 ::before
、::after
、::marker
和 ::first-line
等。
/* ul 包含最多 3 个( 3 个或更少,不包括 0 ) li 项 */
ul:has(> :nth-child(-n+3):last-child) {
border: 3px solid palegreen;
}
/* ul 包含最多 3 个( 3 个或更少,包括 0 个) li 项 */
ul:not(:has(> :nth-child(3))) {
border: 3px solid limegreen;
}
/* ul 正好包含 5 个 li 项 */
ul:has(> :nth-child(5):last-child) {
border: 3px solid plum;
}
/* ul 至少包含 10 个 (10 个或更多)li 项 */
ul:has(> :nth-child(10)) {
border: 3px solid pink;
}
/* ul 包含 7 ~ 9 个 li 项*/
ul:has(> :nth-child(7)):has(> :nth-child(-n+9):last-child) {
border: 3px solid tomato;
}
/** 不支持:has时的备选方案 */
@supports not selector(:has(works)) {
.card {
flex-direction: column;
align-items: flex-start;
}
}
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
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
# :target
实现模态框
<a href="#target-content" id="button">Open CSS Modal via <code>:target</code></a>
<div id="target-content">
<a href="#" class="close"></a>
<div id="target-inner">
<h2>CSS Modal</h2>
</div>
</div>
<style>
#target-content {
pointer-events: none;
opacity: 0;
transition: opacity 200ms;
}
#target-content:target {
pointer-events: all;
opacity: 1;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 选择多个范围的组
<ul>
<li class="rect"></li>
<li class="rect"></li>
<li data-range></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li data-range></li>
<li class="star"></li>
<li class="star"></li>
<li class="star"></li>
<li class="rect"></li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
/**选中 data-range 开始和结束之间所有 li (即所有 .circle 元素) */
[data-range] ~ :has(~ [data-range]) {
width: 100px;
border: 2px solid #09f;
outline: 4px solid rgb(0 0 0 / .5);
}
/**
* [data-range]:has(~ [data-range]) 给范围起始元素(第一个设置 data-range 的 li 元素)设置样式*/
[data-range]:has(~ [data-range]) {
background-color: #987;
outline: 2px solid red;
}
/* [data-range] ~ [data-range] 给范围结束元素(第二个设置 data-range 的 li 元素)设置样式*/
[data-range] ~ [data-range] {
background-color: #90f;
outline: 2px solid orange;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 多个范围选择
<ul>
<li data-range="start"></li>
<li class="rect"></li>
<li class="rect"></li>
<li data-range="end"></li>
<li data-range="start"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li class="circle"></li>
<li data-range="end"></li>
<li data-range="start"></li>
<li class="star"></li>
<li class="star"></li>
<li class="star"></li>
<li data-range="end"></li>
<li class="rect"></li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 选择范围组中开始和结束元素 */
[data-range] {
box-shadow: 0 0 0 6px red;
border-radius: 2px;
}
/* 选择范围组中起始元素 */
[data-range="start"] {
outline:3px solid yellow;
}
/* 选择范围组中结束元素 */
[data-range="end"] {
outline: 3px solid #e90;
}
/* 选择范围组内的第一个元素 */
[data-range="start"] + :has(~ [data-range="end"]):not([data-range]) {
width: 80px;
border: 4px solid #09f;
}
/* 选择范围组内的最后一个元素 */
[data-range="start"] ~ :has(+ [data-range="end"]):not([data-range]) {
width: 80px;
border: 4px solid;
}
/* 选择范围组内的所有元素 */
[data-range="start"] ~ :has(~ [data-range="end"]):not([data-range]) {
width: 80px;
border: 4px solid;
box-shadow: 0 0 0 4px rgb(0 0 0 / .125);
}
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
34
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
34
上次更新: 2025/04/07, 01:42:58