第7章 组合选择器与伪类

第七章:组合选择器与伪类

如果说基础选择器是"单兵作战",那组合选择器就是"联合作战"。学会了组合选择器,你就能选中那些"爸爸的儿子的孙子"——精确到骨头里去。而伪类呢,就是给元素加上各种"状态滤镜":第一个孩子要表扬、鼠标悬停的要高亮、选中的要打勾… CSS 伪类就是来干这个的。

7.1 组合选择器

7.1.1 后代选择器(空格)——div p,选中 div 内所有 p

后代选择器(Descendant Selector)用空格连接两个选择器,表示"选中第一个选择器的后代中符合第二个选择器的所有元素"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/* 选中所有在 article 里面的 p 标签 */
article p {
  line-height: 1.8;
  margin-bottom: 15px;
}

/* 选中所有在 .sidebar 里面的链接 */
.sidebar a {
  color: #3498db;
  text-decoration: none;
}

.sidebar a:hover {
  text-decoration: underline;
}

/* 选中所有在 nav 里面的 ul 里面的 li */
nav ul li {
  display: inline-block;
  padding: 10px 15px;
}
1
2
3
4
5
6
7
8
9
<!-- HTML 结构示例 -->
<article class="blog-post">
  <h2>文章标题</h2>
  <p>第一段内容,<strong>这段是粗体</strong>,也是后代。</p>
  <div class="quote">
    <p>引用段落,也是后代。</p>
  </div>
  <p>第二段内容。</p>
</article>
1
2
3
/* article p 会选中所有这3个 p 标签 */
/* 因为它们都是 article 的后代 */
/* 不管嵌套多深都行 */

后代选择器的应用场景:

 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
/* 1. 文章内容样式 */
.article-content p {
  font-size: 16px;
  line-height: 1.8;
  color: #333;
  margin-bottom: 1em;
}

.article-content h2 {
  font-size: 24px;
  color: #2c3e50;
  margin-top: 2em;
  margin-bottom: 0.5em;
}

.article-content ul {
  padding-left: 25px;
  margin-bottom: 1em;
}

.article-content li {
  margin-bottom: 0.5em;
}

/* 2. 卡片内的标题和内容 */
.card-title {
  font-size: 20px;
}

.card-content p {
  font-size: 14px;
  color: #666;
  line-height: 1.6;
}

7.1.2 子选择器(>)——div > p,只选中直接子元素 p

子选择器(Child Selector)用 > 连接,表示"只选中直接子元素",不包含孙子及更深层的后代。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 后代选择器(空格):选中所有后代 */
.article-content p {
  color: #333;
}

/* 子选择器(>):只选中直接子元素 */
.article-content > p {
  font-size: 18px;
  font-weight: bold;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- HTML 结构 -->
<article class="article-content">
  <p>直接子元素 p ← 子选择器会选中这个</p>

  <div class="wrapper">
    <p>孙子元素 p ← 子选择器不会选中这个!</p>
  </div>

  <div>
    <div>
      <p>曾孙子元素 p ← 子选择器更不会选中!</p>
    </div>
  </div>

  <p>另一个直接子元素 p ← 子选择器会选中这个</p>
</article>

子选择器的应用场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* 1. 只选中第一层的直接列表项 */
.nav > ul > li {
  display: inline-block;
}

/* 不包括嵌套在 li 里面的 ul 中的 li */

/* 2. 表单的直接子输入框 */
.form > input {
  border: 2px solid #3498db;
}

/* 不包括 form 里面 div 里面的 input */

7.1.3 相邻兄弟选择器(+)——h1 + p,选中 h1 后紧跟的 p

相邻兄弟选择器(Adjacent Sibling Selector)用 + 连接,表示"选中紧跟在第一个选择器后面的那个元素"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* 选中紧跟在 h1 后面的 p */
h1 + p {
  font-size: 18px;
  color: #666;
  margin-top: 10px;
}

/* 选中紧跟在 .intro 后面的段落 */
.intro + p {
  color: #2c3e50;
  font-weight: 500;
}
1
2
3
4
5
6
7
8
<h1>文章标题</h1>
<p>← 相邻兄弟选择器会选中这个 p</p>  <!-- 紧跟在 h1 后面 -->

<p>← 相邻兄弟选择器不会选中这个 p</p>  <!-- 没有紧跟 h1 -->

<div>中间插了一脚</div>

<p>← 相邻兄弟选择器不会选中这个 p</p>  <!-- h1 和 p 之间隔了 div -->

相邻兄弟选择器的实际应用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* 1. 文章标题和正文的间距 */
h1 + p {
  margin-top: 20px;
  font-size: 1.2em;
  color: #666;
}

/* 2. 列表项之间的分隔线 */
.list-item + .list-item {
  border-top: 1px solid #eee;
  margin-top: 10px;
  padding-top: 10px;
}

/* 3. 第一个输入框不需要上边距 */
.input-group + .input-group {
  margin-top: 15px;
}

7.1.4 通用兄弟选择器(~)——h1 ~ p,选中 h1 后所有 p

通用兄弟选择器(General Sibling Selector)用 ~ 连接,表示"选中第一个选择器后面所有符合条件的兄弟元素"。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* 选中 h1 后所有的 p */
h1 ~ p {
  color: #666;
  line-height: 1.6;
}

/* 选中 .title 后面所有的 .item */
.title ~ .item {
  background: #f9f9f9;
  padding: 10px;
  margin-bottom: 5px;
}
1
2
3
4
5
6
<h1>大标题</h1>
<p>← 通用兄弟选择器会选中这个</p>  <!-- 紧跟 -->
<p>← 通用兄弟选择器会选中这个</p>  <!-- 隔一个也选 -->
<p>← 通用兄弟选择器会选中这个</p>  <!-- 隔 N 个也选 -->
<div>不是 p,不会被选中</div>
<p>← 通用兄弟选择器会选中这个</p>  <!-- 还是选 -->

组合选择器对比总结:

选择器语法选中范围
后代div pdiv 里面所有 p(包含孙子)
div > pdiv 的直接子元素 p
相邻兄弟h1 + ph1 后面紧跟的 p
通用兄弟h1 ~ ph1 后面所有 p
graph LR
    A["组合选择器"] --> B["后代(空格)"]
    A --> C["子(>)"]
    A --> D["相邻兄弟(+)"]
    A --> E["通用兄弟(~)"]

    B --> B1["所有后代"]
    C --> C1["只有直接子元素"]
    D --> D1["紧跟的下一个"]
    E --> E1["后续所有兄弟"]

7.2 结构性伪类

7.2.1 :root——文档根元素(HTML 中是 html),权重高于 html 选择器,常用于定义全局 CSS 变量

:root 是一个超级有用的伪类,它选中文档的根元素——在 HTML 中就是 <html> 标签。但是它的权重比直接写 html {} 更高,所以用它来定义 CSS 变量是最佳实践。

 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
35
36
37
38
39
/* 用 :root 定义全局 CSS 变量 */
:root {
  /* 颜色变量 */
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --danger-color: #e74c3c;
  --warning-color: #f39c12;

  /* 字体变量 */
  --font-family: "Microsoft YaHei", "Segoe UI", sans-serif;
  --font-size-base: 16px;
  --line-height: 1.6;

  /* 间距变量 */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 16px;
  --spacing-lg: 24px;
  --spacing-xl: 32px;

  /* 圆角变量 */
  --border-radius-sm: 4px;
  --border-radius-md: 8px;
  --border-radius-lg: 16px;

  /* 阴影变量 */
  --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2);
}

/* 使用变量 */
.button {
  background-color: var(--primary-color);
  font-family: var(--font-family);
  padding: var(--spacing-md);
  border-radius: var(--border-radius-md);
  box-shadow: var(--shadow-sm);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* :root vs html 的权重对比 */

/* html 标签选择器,权重 = 1 */
html {
  font-size: 16px;
  color: #333;
}

/* :root 伪类选择器,权重 = 0-1-0(高于普通元素选择器) */
:root {
  font-size: 18px;  /* 会覆盖上面的 16px,因为伪类权重更高 */
  color: #666;       /* 会覆盖上面的 #333 */
}

7.2.2 :first-child——第一个子元素

:first-child 选中父元素下的第一个子元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* 选中每个列表的第一个项 */
li:first-child {
  font-weight: bold;
  color: #3498db;
}

/* 选中每个 .card 的第一个子元素 */
.card:first-child {
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
}

/* 选中 article 里面第一个 p */
article p:first-child {
  font-size: 18px;
  font-weight: 500;
}
1
2
3
4
5
<ul>
  <li>← :first-child 会选中这个 ← :first-child 会选中这个</li>  <!-- 第一个 -->
  <li>第二个</li>
  <li>第三个</li>
</ul>

7.2.3 :last-child——最后一个子元素

:last-child 选中父元素下的最后一个子元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 选中每个列表的最后一项 */
li:last-child {
  border-bottom: none;  /* 列表最后一项不加底边 */
}

/* 选中 .menu 的最后一个链接 */
.menu a:last-child {
  color: #e74c3c;  /* 最后一项标红 */
}

/* 选中 article 里面最后一个 p */
article p:last-child {
  margin-bottom: 0;  /* 最后一段落不加下边距 */
}

7.2.4 :first-of-type——同类型子元素的第一个

:first-of-type:first-child 类似,但它是选中同类型子元素中的第一个。

1
2
3
4
5
6
7
<div class="container">
  <p>第一个 p ← :first-child 和 :first-of-type 都选中它</p>
  <p>第二个 p</p>
  <span>第一个 span ← :first-of-type 会选中它</span>
  <p>第三个 p</p>
  <span>第二个 span</span>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* :first-child vs :first-of-type 的区别 */

.container > :first-child {
  /* 选中.container的直接子元素中的第一个(不管是什么标签) */
  background: #f0f0f0;
}

.container > :first-of-type {
  /* 选中.container的同类型子元素中的第一个 */
  background: #e0e0e0;
}

7.2.5 :last-of-type——同类型子元素的最后一个

:last-of-type 选中同类型子元素中的最后一个。

1
2
3
4
5
6
7
8
/* 选中每种类型子元素的最后一个 */
p:last-of-type {
  margin-bottom: 0;
}

h2:last-of-type {
  margin-top: 30px;
}

7.2.6 :only-child——父元素中唯一的子元素

:only-child 选中父元素中唯一的一个子元素

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* 选中只有一个子元素的父容器 */
.card:only-child {
  /* 整个卡片居中 */
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 200px;
}

/* 选中 .avatar-list 中唯一的后代 */
.avatar-list:only-child {
  margin: 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<div class="card">
  <p>← :only-child 会选中这个 ← :only-child 会选中这个</p>
  <!-- 只有一个子元素 -->
</div>

<div class="card">
  <p>第一个</p>
  <p>第二个</p>
  <!-- 有两个子元素,:only-child 不生效 -->
</div>

7.2.7 :only-of-type——父元素中同类型唯一的子元素

:only-of-type 选中父元素中同类型唯一的子元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* 选中唯一一张图片 */
article img:only-of-type {
  width: 100%;
  border-radius: 12px;
}

/* 选中唯一一个小标题 */
section h3:only-of-type {
  font-size: 28px;
  color: #3498db;
}

7.2.8 :nth-child()——选中第 n 个子元素

:nth-child() 是最强大的结构性伪类,可以选中第 n 个子元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 选中第 3 个子元素 */
li:nth-child(3) {
  background: #3498db;
  color: white;
}

/* 选中前 3 个子元素(通过公式)*/
li:nth-child(-n+3) {
  font-weight: bold;
}

/* 选中从第 4 个开始的所有子元素 */
li:nth-child(n+4) {
  color: #999;
}

7.2.8.1 常见写法——:nth-child(2)(第2个)、:nth-child(odd)(奇数)、:nth-child(even)(偶数)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* 选中第 2 个 */
li:nth-child(2) {
  background: #2ecc71;
}

/* odd = 奇数 */
li:nth-child(odd) {
  background: #f5f5f5;
}

/* even = 偶数 */
li:nth-child(even) {
  background: #fff;
}

/* 斑马条纹效果 */
tr:nth-child(odd) {
  background: #f9f9f9;
}

tr:nth-child(even) {
  background: #fff;
}

7.2.8.2 公式写法——:nth-child(2n+1)(第1、3、5…个)、:nth-child(3n)(第3、6、9…个)

 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
/* 2n+1 = 1, 3, 5, 7... (奇数位置)*/
/* 等同于 odd */
li:nth-child(2n+1) {
  font-weight: bold;
}

/* 2n = 2, 4, 6, 8... (偶数位置)*/
/* 等同于 even */
li:nth-child(2n) {
  background: #f0f0f0;
}

/* 3n = 3, 6, 9, 12... (3的倍数)*/
li:nth-child(3n) {
  color: #e74c3c;  /* 每第三个高亮 */
}

/* 3n+1 = 1, 4, 7, 10... (3的倍数加1)*/
li:nth-child(3n+1) {
  padding-left: 0;
}

/* 3n+2 = 2, 5, 8, 11... (3的倍数加2)*/
li:nth-child(3n+2) {
  padding-left: 20px;
}

/* -n+3 = 前 3 个(-0+3, -1+3, -2+3)*/
li:nth-child(-n+3) {
  font-weight: bold;
  color: #3498db;
}

:nth-child() 常用场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/* 1. 网格布局的每一行第一个和最后一个特殊处理 */
.grid-item:nth-child(3n+1) {
  /* 每一行的第一个 */
  clear: left;
}

/* 2. 表单奇偶行不同背景 */
.form-row:nth-child(odd) {
  background: #f9f9f9;
}

/* 3. 步骤指示器的进度点 */
.step:nth-child(1) { /* 第一步 */ }
.step:nth-child(2) { /* 第二步 */ }
.step:nth-child(3) { /* 第三步 */ }

/* 4. 删除线效果 */
li:nth-child(3n) {
  text-decoration: line-through;
  opacity: 0.7;
}

7.2.9 :nth-last-child()——从最后一个开始计数

:nth-last-child():nth-child() 类似,但是从最后一个开始计数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 选中倒数第 1 个 = 最后一个 */
li:nth-last-child(1) {
  border-bottom: none;
}

/* 选中倒数第 3 个 */
li:nth-last-child(3) {
  color: #e74c3c;
}

/* 选中倒数前 3 个 */
li:nth-last-child(-n+3) {
  font-weight: bold;
}

/* 斑马条纹(从后往前数)*/
tr:nth-last-child(odd) {
  background: #f9f9f9;
}

7.2.10 :nth-child vs :nth-of-type——前者从所有子元素计数,后者只计同类型

1
2
3
4
5
6
7
<div class="container">
  <p>第1个p</p>
  <span>第1个span</span>
  <p>第2个p</p>
  <span>第2个span</span>
  <p>第3个p</p>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* :nth-child(2) - 从所有子元素中数第2个 */
.container > :nth-child(2) {
  color: red;  /* 选中"第1个span",因为它是第二个子元素 */
}

/* :nth-of-type(2) - 从同类型子元素中数第2个 */
.container > p:nth-of-type(2) {
  color: blue;  /* 选中"第2个p" */
}

.container > span:nth-of-type(2) {
  color: green;  /* 选中"第2个span" */
}

7.2.11 :empty——没有子元素(也不含文本节点)的元素

:empty 选中完全空白的元素——既没有子元素,也没有文本。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 选中空的占位元素 */
.placeholder:empty {
  display: none;
}

/* 选中没有内容的提示框 */
.alert:empty {
  display: none;
}

/* 选中空的单元格 */
td:empty {
  background: #f5f5f5;
  /* 给空单元格一个特殊背景色 */
}
1
2
3
4
5
6
7
8
<div class="card">
  <!-- :empty 会选中这个 -->
</div>

<div class="card">
  <p>有内容的卡片</p>
</div>
<!-- :empty 不会选中 -->

7.3 链接与交互伪类

7.3.1 链接四状态——:link(未访问)、:visited(已访问)、:hover(悬停)、:active(按下),LVHA 顺序不能乱

链接有四个状态,对应四个伪类,是前端工程师必须掌握的基础知识。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 链接的四个状态 */

/* :link - 未访问的链接 */
a:link {
  color: #3498db;
  text-decoration: none;
}

/* :visited - 已访问的链接(浏览器记录过的)*/
a:visited {
  color: #9b59b6;  /* 传统上用紫色表示已访问 */
}

/* :hover - 鼠标悬停 */
a:hover {
  color: #2980b9;
  text-decoration: underline;
}

/* :active - 鼠标按下的一瞬间 */
a:active {
  color: #e74c3c;
  transform: scale(0.98);  /* 按下时轻微缩小 */
}

LVHA 顺序法则: 记住这个口诀:LoVe HAte = LiVe HA**te

 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
/* 正确顺序::link → :visited → :hover → :active */
a {
  color: #3498db;
}

a:link {
  color: #3498db;
}

a:visited {
  color: #9b59b6;
}

a:hover {
  color: #2980b9;
  text-decoration: underline;
}

a:active {
  color: #e74c3c;
}

/* ⚠️ 错误示范:顺序乱了会导致样式失效 */
/*
a:hover {
  color: red;
}
a:link {
  color: blue;  ← 这个:hover 可能永远没机会生效
}
*/

链接样式的现代写法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 现代写法:把基础样式写在 a 标签上,然后用伪类覆盖 */
a {
  color: #3498db;
  text-decoration: none;
  transition: color 0.2s ease;
}

a:hover {
  color: #2980b9;
  text-decoration: underline;
}

a:visited {
  color: #8e44ad;
}

a:active {
  color: #c0392b;
}

7.3.2 :focus——获得焦点时(点击输入框、Tab 键聚焦)

:focus 选中获得焦点的元素,比如点击输入框时。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 输入框获得焦点时的样式 */
input:focus,
textarea:focus {
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
  outline: none;  /* 去掉浏览器默认的蓝色轮廓 */
}

/* 按钮获得焦点时的样式 */
button:focus {
  outline: 2px solid #3498db;
  outline-offset: 2px;
}

/* 链接获得焦点时的样式 */
a:focus {
  outline: 2px solid #3498db;
  outline-offset: 2px;
}

7.3.3 :focus-visible——只有键盘聚焦时触发,区分鼠标和键盘

:focus-visible 是一个更智能的 :focus——它只在键盘操作(Tab 键)时生效,鼠标点击时不生效。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 键盘聚焦时显示轮廓(对无障碍访问很重要)*/
button:focus-visible {
  outline: 2px solid #3498db;
  outline-offset: 2px;
}

/* 鼠标点击时不显示轮廓 */
button:focus:not(:focus-visible) {
  outline: none;
}

/* 现代 CSS 的简洁写法 */
button:focus {
  outline: none;  /* 默认不显示 */
}

button:focus-visible {
  outline: 2px solid #3498db;  /* 只有键盘聚焦时才显示 */
}

为什么 :focus-visible 很重要?

graph TD
    A[":focus-visible 的价值"] --> B["鼠标用户"]
    A --> C["键盘用户"]

    B --> B1["点击输入框"]
    B1 --> B2["不需要看到轮廓"]
    B2 --> B3[":focus-visible 不触发 ✓"]

    C --> C1["Tab 键导航"]
    C1 --> C2["需要看到焦点在哪"]
    C2 --> C3[":focus-visible 触发 ✓"]

7.3.4 :focus-within——选中包含焦点子元素的父元素

:focus-within 非常实用——当父元素内部有元素获得焦点时,父元素也会被选中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 当表单内有输入框获得焦点时,整个表单高亮 */
.form-group:focus-within {
  border-color: #3498db;
  background-color: #f8f9fa;
}

/* 导航下拉菜单 */
.nav-item:focus-within .dropdown {
  display: block;  /* 子菜单获得焦点时显示下拉菜单 */
}

/* 输入框组 */
.input-group:focus-within {
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}
1
2
3
4
5
<div class="form-group">
  <label>用户名</label>
  <input type="text" />
  <!-- 当 input 获得焦点时,.form-group 也会应用 :focus-within 样式 -->
</div>

7.3.5 :target——URL 锚点指向的元素

:target 非常酷——当 URL 中有锚点(#xxx)指向某个元素时,该元素就会应用 :target 样式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 被锚点指向的元素高亮显示 */
:target {
  background-color: #fffacd;
  border-left: 3px solid #ffd700;
  padding-left: 15px;
}

/* 单页应用中切换内容 */
.section {
  display: none;
}

.section:target {
  display: block;  /* 被锚点指向时显示 */
}

.section:target + .section {
  display: none;  /* 隐藏其他 */
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- 导航 -->
<nav>
  <a href="#section1">第一部分</a>
  <a href="#section2">第二部分</a>
  <a href="#section3">第三部分</a>
</nav>

<!-- 内容 -->
<section id="section1">第一部分内容</section>
<section id="section2">第二部分内容</section>
<section id="section3">第三部分内容</section>

<!-- 当 URL 是 page.html#section2 时,id="section2" 的元素就会应用 :target 样式 -->

7.4 表单伪类

表单伪类是 CSS 中最实用的选择器之一,可以根据表单字段的状态自动应用不同样式。

7.4.1 :checked——选中的复选框/单选框

 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
35
36
37
38
39
40
41
42
43
44
45
/* 复选框选中时的样式 */
input[type="checkbox"]:checked {
  background-color: #3498db;
  border-color: #3498db;
}

/* 自定义复选框 */
.checkbox-wrapper {
  display: flex;
  align-items: center;
  cursor: pointer;
}

.checkbox-wrapper input[type="checkbox"] {
  display: none;  /* 隐藏原生复选框 */
}

.checkbox-wrapper .custom-checkbox {
  width: 20px;
  height: 20px;
  border: 2px solid #ddd;
  border-radius: 4px;
  margin-right: 10px;
  transition: all 0.2s;
}

.checkbox-wrapper input[type="checkbox"]:checked + .custom-checkbox {
  background-color: #3498db;
  border-color: #3498db;
}

/* 单选框选中样式 */
input[type="radio"]:checked {
  border-color: #3498db;
}

input[type="radio"]:checked::after {
  content: "";
  display: block;
  width: 10px;
  height: 10px;
  background: #3498db;
  border-radius: 50%;
  margin: 3px;
}

7.4.2 :valid——通过验证的表单字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 输入框内容合法时显示绿色边框 */
input:valid {
  border-color: #2ecc71;
  background-color: rgba(46, 204, 113, 0.1);
}

/* 邮箱格式正确时 */
input[type="email"]:valid {
  border-color: #2ecc71;
}

/* URL 格式正确时 */
input[type="url"]:valid {
  border-color: #2ecc71;
}

7.4.3 :invalid——未通过验证的表单字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* 输入框内容非法时显示红色边框 */
input:invalid {
  border-color: #e74c3c;
  background-color: rgba(231, 76, 60, 0.1);
}

/* 输入框内容为空且必填时(不填写就验证失败)*/
input:invalid:not(:placeholder-shown) {
  border-color: #e74c3c;
}

/* 错误提示 */
input:invalid:not(:placeholder-shown)::after {
  content: "请输入有效的内容";
  color: #e74c3c;
  font-size: 12px;
}

7.4.4 :required——必填的表单字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 必填字段添加星号标记 */
input:required {
  border-left: 3px solid #e74c3c;
}

/* 必填字段聚焦时的高亮 */
input:required:focus {
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}

7.4.5 :optional——非必填的表单字段

1
2
3
4
5
/* 可选字段使用不同的样式 */
input:optional {
  border-style: dashed;  /* 虚线边框表示可选 */
  border-color: #ccc;
}

7.4.6 :disabled——禁用的表单字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/* 禁用状态 */
input:disabled {
  background-color: #f5f5f5;
  color: #999;
  cursor: not-allowed;
  opacity: 0.7;
}

/* 禁用状态的按钮 */
button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
  opacity: 0.6;
}

/* 禁用状态的 select */
select:disabled {
  cursor: not-allowed;
}

7.4.7 :enabled——启用的表单字段(默认)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 启用状态的字段 */
input:enabled {
  background-color: white;
  cursor: text;
}

/* :enabled 和 :disabled 对比 */
input:enabled {
  border-color: #3498db;
}

input:disabled {
  border-color: #ddd;
  background: #f5f5f5;
}

7.4.8 :placeholder-shown——占位符文字显示时的输入框

 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
35
36
37
38
39
40
41
42
43
44
45
/* 占位符显示时隐藏输入框边框 */
input:placeholder-shown {
  border-color: #ccc;
  background-color: #f9f9f9;
}

/* 占位符显示时隐藏 label */
.input-group:has(input:placeholder-shown) label {
  opacity: 0.5;
}

/* 浮动标签效果 */
.input-group {
  position: relative;
}

.input-group input {
  padding: 20px 16px 6px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 16px;
}

.input-group input:focus {
  border-color: #3498db;
}

.input-group label {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  color: #999;
  transition: all 0.2s;
  pointer-events: none;
}

/* 输入内容时标签上移 */
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label {
  top: 8px;
  font-size: 12px;
  color: #3498db;
  transform: none;
}

7.4.9 :read-only——只读字段

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* 只读输入框 */
input:read-only {
  background-color: #f5f5f5;
  color: #666;
  cursor: not-allowed;
}

/* contenteditable 的只读状态 */
[contenteditable="true"]:read-only {
  background-color: #f9f9f9;
  outline: none;
}

7.4.10 :read-write——非只读字段(默认)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* 可编辑状态 */
input:read-write {
  background-color: white;
  border-color: #ddd;
}

/* 可编辑时聚焦高亮 */
input:read-write:focus {
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}

7.4.11 :in-range——值在 min/max 范围内的输入框

1
2
3
4
5
6
/* 值在范围内的输入框 */
input[type="number"]:in-range,
input[type="range"]:in-range {
  border-color: #2ecc71;
  background-color: rgba(46, 204, 113, 0.1);
}
1
2
<input type="number" min="1" max="10" value="5" />
<!-- 当值在 1-10 范围内时,:in-range 生效 -->

7.4.12 :out-of-range——值超出 min/max 范围的输入框

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
/* 值超出范围的输入框 */
input[type="number"]:out-of-range,
input[type="range"]:out-of-range {
  border-color: #e74c3c;
  background-color: rgba(231, 76, 60, 0.1);
}

/* 超出范围时显示警告 */
input:out-of-range::after {
  content: "请输入 1-10 之间的数字";
  color: #e74c3c;
  font-size: 12px;
}

表单伪类实战示例:

 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
35
36
37
38
39
40
41
42
43
44
45
46
47
/* 完整的表单验证样式 */
.form-field {
  margin-bottom: 20px;
}

.form-field label {
  display: block;
  margin-bottom: 8px;
  font-weight: 500;
}

.form-field input {
  width: 100%;
  padding: 12px 16px;
  border: 2px solid #ddd;
  border-radius: 8px;
  font-size: 16px;
  transition: all 0.2s;
}

/* 聚焦状态 */
.form-field input:focus {
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
  outline: none;
}

/* 验证状态 */
.form-field input:valid:not(:placeholder-shown) {
  border-color: #2ecc71;
}

.form-field input:invalid:not(:placeholder-shown) {
  border-color: #e74c3c;
}

/* 必填标记 */
.form-field input:required + .label::after {
  content: " *";
  color: #e74c3c;
}

/* 禁用状态 */
.form-field input:disabled {
  background-color: #f5f5f5;
  cursor: not-allowed;
}

7.5 逻辑伪类

7.5.1 :not()——否定伪类,:not(.active) 选中不含 .active 类的元素

:not() 是 CSS 中最强大的逻辑伪类,它可以对选择器取反——选中"不符合条件"的元素。

 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
/* 选中所有非 .active 的按钮 */
button:not(.active) {
  opacity: 0.6;
  cursor: pointer;
}

/* 选中所有非第一个的 li */
li:not(:first-child) {
  border-top: 1px solid #eee;
}

/* 选中所有不是链接的元素 */
:not(a) {
  /* 这里是除了链接以外的所有元素 */
}

/* 组合使用:选中既不是 .primary 也不是 .danger 的按钮 */
button:not(.primary):not(.danger) {
  background: #95a5a6;
}

/* 选中不是第一个也不是最后一个的 li */
li:not(:first-child):not(:last-child) {
  font-style: italic;
}
1
2
3
4
5
<ul>
  <li class="active">第一项 ← :not(.active) 不选这个</li>
  <li>第二项 ← :not(.active) 选中这个</li>
  <li>第三项 ← :not(.active) 选中这个</li>
</ul>

7.5.2 :is()——简化复杂选择器,如 :is(h1, h2, h3) { },匹配任一选择器

:is() 可以把多个选择器合并成一个,简化代码。

 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
/* 之前:需要写三遍 */
h1 { font-size: 32px; color: #333; }
h2 { font-size: 28px; color: #333; }
h3 { font-size: 24px; color: #333; }

/* 现在:用 :is() 一行搞定(但通常还是分开写以保持语义) */
:is(h1, h2, h3) {
  color: #333;
  margin-bottom: 15px;
}

/* 如果确实要统一字号 */
:is(h1, h2, h3) {
  font-size: 2em;  /* 相对单位更灵活 */
  color: #333;
}

/* 更复杂的例子 */
:is(.card, .modal, .dialog) h2 {
  font-size: 24px;
  color: #2c3e50;
}

/* 表单元素的统一样式 */
:is(input, textarea, select):focus {
  border-color: #3498db;
  box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
}

7.5.3 :where()——同 :is(),但优先级恒为 0,:where() 的特异性始终为零

:where():is() 用法完全一样,但有一个关键区别:优先级为 0

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* :is() 有正常的优先级计算 */
:is(#header, .sidebar) h1 {
  color: blue;  /* 权重 = ID(100) + 元素(1) = 101 */
}

/* :where() 优先级永远是 0 */
:where(#header, .sidebar) h1 {
  color: red;  /* 权重 = 0 + 元素(1) = 1 */
}

/* 实际应用:第三方样式覆盖 */
.third-party-button:where(.btn) {
  /* 可以用 :where() 来降低选择器优先级 */
  /* 更容易被覆盖 */
}
graph TD
    A[":is() vs :where()"] --> B[":is()"]
    A --> C[":where()"]

    B --> B1["有正常优先级"]
    B --> B2["按最高选择器计算权重"]
    B --> B3["无法被轻易覆盖"]

    C --> C1["优先级 = 0"]
    C --> C2["始终为零"]
    C --> C3["容易被覆盖"]

    style B fill:#e74c3c,stroke:#333
    style C fill:#2ecc71,stroke:#333

7.5.4 :has()——父选择器,div:has(.child) 选中包含 .child 的 div,是目前功能最强的伪类

:has() 是 CSS 选择器史上最强大的伪类!它实现了真正的"父选择器"功能。

 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
35
/* 选中包含 .child 的 .parent */
.parent:has(.child) {
  background: #f9f9f9;
}

/* 选中包含图片的卡片 */
.card:has(img) {
  border: 2px solid #3498db;
}

/* 选中包含图标的按钮 */
.button:has(.icon) {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* 选中包含错误提示的表单组 */
.form-group:has(.error-message) {
  border-color: #e74c3c;
}

.form-group:has(.error-message) label {
  color: #e74c3c;
}

/* 选中包含选中复选框的行 */
.table-row:has(input[type="checkbox"]:checked) {
  background: rgba(46, 204, 113, 0.1);
}

/* 选中不包含任何子元素的 div */
div:has(> *:first-child:last-child) {
  /* 这会选择只有唯一一个子元素的 div */
}
1
2
3
4
5
6
7
8
<div class="parent">
  <div class="child">← .parent:has(.child) 会选中这个父 div</div>
</div>

<div class="parent">
  <p>没有 .child</p>
</div>
<!-- 这个不会被选中 -->

:has() 的实际应用:

 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
/* 表单验证:必填字段为空时父容器变红 */
.form-group:has(input:invalid:not(:placeholder-shown)) {
  border-color: #e74c3c;
  background-color: rgba(231, 76, 60, 0.05);
}

/* 购物车有物品时显示数量 */
.cart-icon:has(.cart-count) {
  position: relative;
}

.cart-count {
  position: absolute;
  top: -5px;
  right: -5px;
  background: #e74c3c;
  color: white;
  font-size: 12px;
  padding: 2px 6px;
  border-radius: 50%;
}

/* 表格行选中效果 */
.table-row:has(input[type="checkbox"]:checked) {
  background: rgba(52, 152, 219, 0.1);
}

/* 导航当前页面高亮 */
.nav-item:has(a.current) {
  background: #3498db;
}

7.6 语言与方向伪类

7.6.1 :lang()——根据语言属性选中元素,如 :lang(en)、:lang(zh-CN)

:lang() 根据 HTML 元素的 lang 属性来选择元素,非常适合多语言网站。

 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
/* 为英文内容设置特殊字体 */
:lang(en) {
  font-family: "Georgia", serif;
  font-style: italic;
}

/* 为中文内容设置特殊样式 */
:lang(zh-CN) {
  font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
  line-height: 1.8;
}

/* 为日文内容设置特殊样式 */
:lang(ja) {
  font-family: "Hiragino Sans", "Yu Gothic", sans-serif;
}

/* 引用符号样式 */
q:lang(en) {
  quotes: '"' '"';
}

q:lang(zh-CN) {
  quotes: '"' '"';
}

q:lang(ja) {
  quotes: "「" "」";
}
1
2
3
<p lang="en">This is an English paragraph.</p>
<p lang="zh-CN">这是一段中文内容。</p>
<p lang="ja">これは日本語の段落です。</p>

7.6.2 :dir()——根据文本方向选中元素,:dir(ltr)(从左到右)、:dir(rtl)(从右到左)

:dir() 根据文本方向选择元素,是 RTL(从右到左)语言(如阿拉伯语、希伯来语)的最佳拍档。

 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
/* 从左到右的文字(中文、英文等)*/
:dir(ltr) {
  text-align: left;
}

/* 从右到左的文字(阿拉伯语、希伯来语等)*/
:dir(rtl) {
  text-align: right;
}

/* RTL 布局中的导航 */
:dir(rtl) .nav {
  padding-right: 20px;
  padding-left: 0;
}

/* RTL 布局中的图标翻转 */
:dir(rtl) .icon-arrow {
  transform: scaleX(-1);  /* 箭头图标反转 */
}

/* RTL 布局中的浮动方向 */
:dir(rtl) .float-left {
  float: right;
}

:dir(rtl) .float-right {
  float: left;
}

7.7 伪元素

伪元素和伪类很像,但有一个关键区别:伪元素创建并选中文档中不存在的新元素。伪元素的写法是 ::(两个冒号),但单冒号 : 也能正常工作(历史兼容)。

7.7.1 ::before——元素内容前插入虚拟元素,需配合 content 属性

::before 在元素内容之前插入一个虚拟元素。

 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
/* 在链接前加图标 */
a::before {
  content: "🔗 ";
  margin-right: 5px;
}

/* 在标题前加装饰线 */
h2::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 20px;
  background: #3498db;
  margin-right: 10px;
  vertical-align: middle;
}

/* 在卡片前加引号 */
blockquote::before {
  content: """;
  font-size: 48px;
  color: #3498db;
  font-family: Georgia, serif;
  line-height: 1;
  display: block;
  margin-bottom: 10px;
}

7.7.2 ::after——元素内容后插入虚拟元素,需配合 content 属性

::after 在元素内容之后插入一个虚拟元素。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 在按钮后加箭头 */
.btn::after {
  content: " →";
}

/* 清除浮动(经典用法)*/
.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

/* 给必填项加星号 */
label.required::after {
  content: " *";
  color: #e74c3c;
}

/* 给外链加图标 */
a[href^="http"]::after {
  content: " ↗";
  color: #3498db;
  font-size: 12px;
}

7.7.3 ::first-line——元素的第一行,只能用于块级元素

::first-line 选中元素内容的第一行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* 首行特殊样式 */
p::first-line {
  font-weight: bold;
  color: #2c3e50;
}

/* 首行更大字号 */
.intro::first-line {
  font-size: 1.5em;
  font-weight: 600;
}

/* 首行首字母特殊处理 */
p::first-letter {
  font-size: 3em;
  float: left;
  line-height: 1;
  padding-right: 8px;
  color: #e74c3c;
}

7.7.4 ::first-letter——元素的第一个字母,只能用于块级元素

::first-letter 选中元素的第一个字母/汉字,常用于实现"首字下沉"效果。

 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
/* 首字下沉效果 */
p::first-letter {
  font-size: 3em;
  float: left;
  line-height: 1;
  padding-right: 10px;
  color: #e74c3c;
  font-weight: bold;
}

/* 杂志风格的第一个字母 */
.article-title::first-letter {
  font-size: 4em;
  float: left;
  line-height: 0.8;
  padding-right: 12px;
  color: #3498db;
  font-family: Georgia, serif;
}

/* 图标风格的第一个字符 */
.icon-label::first-letter {
  content: "🔍";
  font-size: 1.5em;
  margin-right: 5px;
}

7.7.5 ::selection——自定义选中文字样式,可设 color 和 background

::selection 选中用户用鼠标选中的文字。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/* 选中文字高亮 */
::selection {
  background: rgba(52, 152, 219, 0.3);
  color: #2c3e50;
}

/* 暗黑风格的选中效果 */
.dark-theme ::selection {
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}

/* 品牌色的选中效果 */
.brand-theme ::selection {
  background: #3498db;
  color: white;
}

7.7.6 ::placeholder——自定义占位符样式,需加前缀(::-webkit-input-placeholder 等)

::placeholder 自定义输入框占位符的样式。

 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
35
36
/* 基础占位符样式 */
input::placeholder {
  color: #999;
  font-style: italic;
}

/* 高级占位符样式 */
input::placeholder {
  color: #ccc;
  font-size: 14px;
  font-style: normal;
}

/* 占位符聚焦效果 */
input:focus::placeholder {
  color: #3498db;
  opacity: 0.8;
}

/* 不同浏览器的兼容写法 */
input::placeholder {
  color: #999;
}

::-webkit-input-placeholder {
  color: #999;
}

::-moz-placeholder {
  color: #999;
  opacity: 1;
}

:-ms-input-placeholder {
  color: #999;
}

7.7.7 ::marker——自定义列表标记的样式,如 li::marker { color: red; }

::marker 自定义列表的标记符号(圆点、数字等)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 改变列表标记颜色 */
li::marker {
  color: #3498db;
}

/* 改变列表标记字体 */
li::marker {
  font-family: "Font Awesome";
  content: "✓ ";
}

/* 有序列表的数字样式 */
ol::marker {
  color: #e74c3c;
  font-weight: bold;
}

/* 导航列表的箭头标记 */
.nav li::marker {
  content: "→ ";
  color: #3498db;
}

7.7.8 ::cue——自定义媒体字幕的样式

::cue 自定义 <track> 元素显示的字幕样式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 字幕样式 */
video::cue {
  background: rgba(0, 0, 0, 0.8);
  color: white;
  font-family: "Microsoft YaHei", sans-serif;
  font-size: 18px;
  padding: 5px 10px;
  border-radius: 4px;
}

/* 字幕中的时间码样式 */
video::cue(timestamp) {
  color: #ffd700;
}

7.7.9 ::file-selector-button——文件选择按钮的样式

::file-selector-button 自定义文件选择按钮的样式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* 文件上传按钮样式 */
input[type="file"]::file-selector-button {
  background: #3498db;
  color: white;
  border: none;
  padding: 8px 16px;
  margin-right: 10px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.2s;
}

input[type="file"]::file-selector-button:hover {
  background: #2980b9;
}

7.7.10 ::backdrop——元素进入全屏时的背景样式,用于 video:fullscreen::backdrop

::backdrop 全屏元素背后的背景样式。

1
2
3
4
5
6
7
8
9
/* 视频全屏时的背景 */
video:fullscreen::backdrop {
  background: black;
}

/* 图片全屏时的背景 */
img:fullscreen::backdrop {
  background: rgba(0, 0, 0, 0.9);
}

伪元素汇总表:

伪元素说明典型用途
::before元素内容前插入加图标、加装饰
::after元素内容后插入加箭头、清除浮动
::first-line第一行首行特殊样式
::first-letter第一个字母首字下沉
::selection选中文字高亮颜色
::placeholder占位符文字输入框占位符样式
::marker列表标记列表符号样式
::backdrop全屏背景全屏时背景色
graph LR
    A["伪元素 vs 伪类"] --> B["伪类"]
    A --> C["伪元素"]

    B --> B1[":hover"]
    B --> B2[":first-child"]
    B --> B3[":checked"]
    B --> B4["选中已有元素的状态"]

    C --> C1["::before"]
    C --> C2["::after"]
    C --> C3["::first-line"]
    C --> C4["创建新的虚拟元素"]

本章小结

恭喜你完成了第七章的学习!让我们来回顾一下这章的精华:

核心知识点

小节重点内容
7.1 组合选择器后代(空格)、子(>)、相邻兄弟(+)、通用兄弟(~)
7.2 结构性伪类:root、:first-child、:nth-child()、:empty 等
7.3 链接与交互伪类:link、:visited、:hover、:focus、:focus-within
7.4 表单伪类:checked、:valid、:invalid、:disabled、:placeholder-shown
7.5 逻辑伪类:not()、:is()、:where()、:has()
7.6 语言与方向伪类:lang()、:dir()
7.7 伪元素::before、::after、::first-line、::first-letter 等

选择器分类图

mindmap
  root((CSS 选择器))
    基础选择器
      标签选择器
      类选择器
      ID 选择器
      通用选择器
      属性选择器
    组合选择器
      后代空格
      子>
      相邻兄弟+
      通用兄弟~
    伪类
      结构性:first-child
      交互:hover :focus
      表单:checked
      逻辑:not :has
    伪元素
      ::before
      ::after
      ::first-line

实战建议

  1. 优先使用类选择器:日常开发以类选择器为主
  2. 善用组合选择器:减少 HTML 结构依赖
  3. 掌握 :has():这是目前最强大的选择器
  4. 区分 :focus 和 :focus-visible:对无障碍访问很重要

下章预告

下一章我们将学习选择器优先级,这是 CSS 中最容易被忽略但又极其重要的知识点。理解了优先级,你才能真正掌握 CSS 的层叠规则!