0%

伪类

CSS 伪类详解:UI 伪类与结构化伪类的用法与实战

CSS 伪类(Pseudo-class)是一种特殊的选择符,用于选择处于特定状态或具有特定结构关系的元素—— 它们无需在 HTML 中添加实际的 classid,而是通过元素的 “动态状态”(如鼠标悬停)或 “结构位置”(如列表第 3 项)来定位元素。伪类极大地扩展了 CSS 的选择能力,是实现交互效果和结构化样式的核心工具。从 “核心概念→分类详解→实战场景→注意事项” 四个维度,全面讲解伪类的使用方法与进阶技巧。

伪类核心概念

1. 伪类的语法

伪类以冒号 : 开头,紧跟伪类名称,语法格式如下:

1
2
3
4
5
6
7
8
9
/* 基础语法:选择符 + 伪类 */
选择符:伪类名称 {
属性: 值;
}

/* 示例:为 hover 状态的 <a> 标签设置样式 */
a:hover {
color: red;
}

2. 伪类的核心特点

  • 无实际 class:伪类不依赖 HTML 中的 class 属性,而是基于元素的 “状态” 或 “结构” 动态生效;
  • 动态性:UI 伪类(如 :hover)会随用户操作(如鼠标移动)实时切换样式;
  • 结构性:结构化伪类(如 :nth-child)仅依赖 HTML 结构(如元素在父容器中的位置),与用户操作无关;
  • 可叠加使用:多个伪类可叠加(如 a:link:hover,表示 “未点击的链接被悬停时”)。

UI 伪类:基于元素状态的样式

UI 伪类(UI Pseudo-classes)用于选择处于特定交互状态的元素,核心场景是响应用户操作(如点击、悬停、聚焦),最典型的应用是链接样式控制。

1. 链接相关的 UI 伪类

链接(<a> 标签)是 UI 伪类最常用的场景,4 个伪类分别对应链接的不同生命周期状态,必须按固定顺序书写linkvisitedhoveractive),否则样式可能失效。

伪类名称 作用描述 适用元素 示例效果
:link 选择未被点击过的链接(默认状态) <a>(必须有 href 未点击的链接为蓝色(默认)
:visited 选择已被点击过的链接(浏览器记录过访问历史) <a>(必须有 href 已点击的链接为紫色(默认)
:hover 选择鼠标正悬停在上面的元素 所有元素(常用 <a>、按钮) 鼠标悬停时链接变红色
:active 选择正在被点击的元素(鼠标按下但未松开的瞬间) 所有元素(常用 <a>、按钮) 点击时链接变深红色
示例:链接样式的完整实现(按顺序书写)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 1. 未点击的链接 */
a:link {
color: #2c7be5; /* 蓝色 */
text-decoration: none; /* 清除下划线 */
}

/* 2. 已点击的链接 */
a:visited {
color: #868e96; /* 灰色(避免使用与 :link 差异过大的颜色) */
}

/* 3. 鼠标悬停的链接 */
a:hover {
color: #195fc6; /* 深一点的蓝色 */
text-decoration: underline; /* 显示下划线 */
}

/* 4. 正在被点击的链接 */
a:active {
color: #0f3fa8; /* 更深的蓝色 */
}
关键注意事项:
  • 顺序规则linkvisitedhoveractive(记忆口诀:LVHA),因为后定义的样式会覆盖先定义的,若顺序错误(如 hovervisited 前),visited 样式会覆盖 hover

  • :visited 的限制:出于隐私保护,浏览器限制 :visited 可设置的样式(仅支持 colorbackground-colorborder-color 等,无法设置 background-imagebox-shadow 等);

  • 非链接元素的应用:hover和active可用于按钮、卡片等元素,实现交互效果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /* 按钮 hover 和 active 状态 */
    .btn {
    padding: 8px 16px;
    background-color: #2c7be5;
    color: white;
    border: none;
    border-radius: 4px;
    }
    .btn:hover {
    background-color: #195fc6; /* 悬停时加深背景色 */
    }
    .btn:active {
    background-color: #0f3fa8; /* 点击时进一步加深 */
    }

2. 表单相关的 UI 伪类

表单元素(如输入框、复选框)的状态(聚焦、禁用、选中)也可通过 UI 伪类控制,是表单美化的核心工具。

伪类名称 作用描述 适用元素
:focus 选择获得焦点的元素(如输入框被点击、通过 Tab 键选中) <input><textarea><select>
:disabled 选择被禁用的元素(HTML 中设置 disabled 属性) 表单元素(<input>、按钮等)
:enabled 选择未被禁用的元素(与 :disabled 相反) 表单元素
:checked 选择被选中的元素(如复选框、单选按钮) <input type="checkbox/radio">
:placeholder-shown 选择占位符(placeholder)可见的输入框(即未输入内容时) <input><textarea>
示例:表单元素的伪类样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 1. 聚焦的输入框 */
input:focus {
outline: none; /* 清除默认聚焦边框 */
border-color: #2c7be5; /* 自定义聚焦边框颜色 */
box-shadow: 0 0 0 2px rgba(44, 123, 229, 0.2); /* 聚焦阴影 */
}

/* 2. 禁用的输入框 */
input:disabled {
background-color: #f8f9fa; /* 灰色背景 */
color: #adb5bd; /* 浅灰色文字 */
cursor: not-allowed; /* 禁用鼠标指针 */
}

/* 3. 被选中的复选框(配合邻接兄弟选择符,控制文字样式) */
input[type="checkbox"]:checked + span {
color: #2c7be5; /* 选中时文字变蓝 */
font-weight: bold; /* 文字加粗 */
}

/* 4. 占位符可见的输入框(未输入内容时) */
input:placeholder-shown {
border-color: #ced4da; /* 默认边框颜色 */
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 表单 HTML 示例 -->
<div>
<label>
<input type="checkbox">
<span>同意用户协议</span> <!-- 复选框选中时,此文字变蓝 -->
</label>
</div>
<div>
<input type="text" placeholder="请输入用户名" class="form-input">
</div>
<div>
<input type="text" value="禁用的输入框" disabled class="form-input">
</div>

结构化伪类:基于元素结构的样式

结构化伪类(Structural Pseudo-classes)用于选择具有特定结构关系的元素(如 “第 3 个列表项”“表格的偶数行”“父元素的第一个子元素”),无需手动为元素添加 class,即可实现结构化样式(如奇偶行交替色、首项特殊样式)。

1. 核心结构化伪类::nth-child()

:nth-child(n) 是最常用的结构化伪类,用于选择 “父元素的第 n 个子元素”,支持多种取值方式,灵活度极高。

语法与取值
1
2
3
4
/* 基础语法:父元素下的第 n 个符合选择符的子元素 */
父元素 选择符:nth-child(取值) {
属性: 值;
}
取值类型 示例 作用描述
具体数字 :nth-child(3) 选择第 3 个子元素(从 1 开始计数)
even/odd :nth-child(even) even:选择偶数位置的子元素(2、4、6…);odd:选择奇数位置的子元素(1、3、5…)
公式(an+b :nth-child(2n+1) n 从 0 开始递增,计算结果为元素位置(如 2n+1 等价于 odd2n 等价于 even
反向计数 :nth-child(-n+3) 选择前 3 个子元素(n=0 时为 3,n=1 时为 2,n=2 时为 1,n=3 时为 0 不生效)
示例 1:列表奇偶行交替色(表格 / 列表通用)
1
2
3
4
5
6
7
8
9
/* 无序列表的奇数行(1、3、5...) */
ul li:nth-child(odd) {
background-color: #f8f9fa; /* 浅灰色背景 */
}

/* 无序列表的偶数行(2、4、6...) */
ul li:nth-child(even) {
background-color: #ffffff; /* 白色背景 */
}
1
2
3
4
5
6
<ul style="list-style: none; padding: 0;">
<li style="padding: 8px 12px; border: 1px solid #eee;">列表项 1(奇数行,浅灰背景)</li>
<li style="padding: 8px 12px; border: 1px solid #eee;">列表项 2(偶数行,白色背景)</li>
<li style="padding: 8px 12px; border: 1px solid #eee;">列表项 3(奇数行,浅灰背景)</li>
<li style="padding: 8px 12px; border: 1px solid #eee;">列表项 4(偶数行,白色背景)</li>
</ul>
示例 2:选择前 3 个列表项
1
2
3
4
/* 选择前 3 个列表项,添加特殊边框 */
ul li:nth-child(-n+3) {
border-left: 3px solid #2c7be5; /* 蓝色左侧边框 */
}

2. 其他常用结构化伪类

除了 :nth-child(),还有多个结构化伪类用于定位 “首项、尾项、唯一项” 等特殊位置的元素,进一步简化结构化样式的实现。

伪类名称 作用描述 示例
:first-child 选择父元素的第一个子元素(无论类型) ul li:first-child:列表的第一个项
:last-child 选择父元素的最后一个子元素(无论类型) ul li:last-child:列表的最后一个项
:only-child 选择父元素中唯一的子元素(若有多个子元素,则不生效) div p:only-child:div 中唯一的 p 标签
:first-of-type 选择父元素中同类型的第一个子元素(仅按类型计数,忽略其他类型元素) div p:first-of-type:div 中第一个 p 标签(即使前面有 span)
:last-of-type 选择父元素中同类型的最后一个子元素 div p:last-of-type:div 中最后一个 p 标签
:nth-last-child(n) 父元素的最后一个子元素开始计数,选择第 n 个元素(反向版 :nth-child) ul li:nth-last-child(2):列表的倒数第二个项
示例::first-child:first-of-type 的区别
1
2
3
4
5
<div class="container">
<span>这是一个 span 标签</span>
<p>这是第一个 p 标签</p>
<p>这是第二个 p 标签</p>
</div>
1
2
3
4
5
6
7
8
9
/* 1. :first-child:选择 .container 的第一个子元素(span 标签) */
.container :first-child {
color: red; /* span 标签变红色 */
}

/* 2. :first-of-type:选择 .container 中第一个 p 标签(无论前面有什么元素) */
.container p:first-of-type {
color: blue; /* 第一个 p 标签变蓝色 */
}
效果:
  • span 标签因是 “第一个子元素” 变红色;
  • 第一个 p 标签因是 “同类型第一个” 变蓝色。

实战场景:伪类的综合应用

场景 1:表格奇偶行交替色(提升可读性)

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
/* 表格基础样式 */
table {
width: 100%;
border-collapse: collapse; /* 合并边框 */
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f8f9fa;
font-weight: bold;
}

/* 表格偶数行样式(:nth-child(even)) */
tbody tr:nth-child(even) {
background-color: #fafbfc;
}

/* 鼠标悬停行样式(:hover) */
tbody tr:hover {
background-color: #f1f3f5;
cursor: default;
}
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
<table>
<thead>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
</tr>
</thead>
<tbody>
<tr>
<td>张三</td>
<td>25</td>
<td></td>
</tr>
<tr>
<td>李四</td>
<td>30</td>
<td></td>
</tr>
<tr>
<td>王五</td>
<td>28</td>
<td></td>
</tr>
</tbody>
</table>

场景 2:导航菜单的交互效果(UI 伪类组合)

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
/* 导航菜单基础样式 */
.nav {
list-style: none;
padding: 0;
display: flex;
gap: 2px;
background-color: #333;
}
.nav li a {
display: block;
padding: 12px 20px;
color: white;
text-decoration: none;
}

/* 1. 未点击的链接(:link) */
.nav li a:link {
color: white;
}

/* 2. 已点击的链接(:visited) */
.nav li a:visited {
color: white; /* 导航链接不区分是否点击,统一白色 */
}

/* 3. 鼠标悬停(:hover)+ 当前页(.active 类) */
.nav li a:hover,
.nav li a.active {
background-color: #2c7be5; /* 悬停或当前页时背景变蓝 */
}

/* 4. 正在点击(:active) */
.nav li a:active {
background-color: #195fc6; /* 点击时背景更深 */
}
1
2
3
4
5
6
<ul class="nav">
<li><a href="#" class="active">首页</a></li> <!-- 当前页,应用 .active 样式 -->
<li><a href="#">产品</a></li>
<li><a href="#">新闻</a></li>
<li><a href="#">关于我们</a></li>
</ul>

场景 3:列表首项与尾项特殊样式(结构化伪类)

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
/* 列表基础样式 */
.feature-list {
list-style: none;
padding: 0;
max-width: 600px;
}
.feature-list li {
padding: 15px;
margin: 5px 0;
border: 1px solid #eee;
border-radius: 4px;
}

/* 1. 首项:添加顶部边框和特殊图标 */
.feature-list li:first-child {
border-top: 3px solid #2c7be5;
position: relative;
}
.feature-list li:first-child::before { /* 伪元素配合伪类,添加图标 */
content: "★";
color: #2c7be5;
position: absolute;
right: 15px;
top: 15px;
}

/* 2. 尾项:添加底部边框和按钮样式 */
.feature-list li:last-child {
border-bottom: 3px solid #2c7be5;
text-align: center;
}
.feature-list li:last-child button {
padding: 8px 16px;
background-color: #2c7be5;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
1
2
3
4
5
6
<ul class="feature-list">
<li>核心功能 1:支持多终端适配</li>
<li>核心功能 2:提供完善的 API 接口</li>
<li>核心功能 3:支持数据导出与导入</li>
<li><button>立即体验</button></li>
</ul>

常见问题与解决方案

1. :nth-child() 计数不符合预期

  • 问题原因:nth-child(n) 会计算父元素的 “所有子元素”(无论类型),若父元素中混合了不同类型的元素(如 spanli),计数会包含非目标类型元素;

  • 解决方案:使用:nth-of-type(n)替代,仅按 “目标元素类型” 计数:

    1
    2
    3
    4
    5
    <ul>
    <span>这是一个 span(会被 :nth-child 计入,但 :nth-of-type 忽略)</span>
    <li>列表项 1</li>
    <li>列表项 2</li>
    </ul>
1
2
3
4
5
/* 错误::nth-child(1) 会选中 span,而非第一个 li */
ul li:nth-child(1) { color: red; }

/* 正确::nth-of-type(1) 仅计数 li,选中第一个 li */
ul li:nth-of-type(1) { color: red; }

2. 链接伪类顺序错误导致样式失效

  • 问题现象hover 样式不生效,或 visited 样式覆盖 hover
  • 原因:未按 linkvisitedhoveractive 的顺序书写;
  • 解决方案:严格遵循 LVHA 顺序,确保后定义的伪类覆盖前定义的(如 hovervisited 后,activehover 后)。

3. :hover 样式在移动设备上无响应

  • 问题原因:移动设备(手机、平板)无 “鼠标悬停” 状态,hover 样式可能在点击后残留,影响体验;

  • 解决方案:

    1. 为移动设备添加触摸事件样式(@media (hover: none));
    2. 避免在移动设备依赖 hover 实现关键功能(如菜单展开),改用 click 事件;
    1
    2
    3
    4
    5
    6
    7
    /* 移动设备上禁用 hover 样式 */
    @media (hover: none) {
    a:hover {
    color: inherit; /* 继承默认颜色,不改变 */
    text-decoration: none; /* 不显示下划线 */
    }
    }

总结与最佳实践

1. 核心总结

  • UI 伪类:基于元素的动态状态(如 hoverfocus),用于实现交互效果,核心是响应用户操作;
  • 结构化伪类:基于元素的结构位置(如 :nth-child:first-child),用于实现结构化样式,无需手动添加 class
  • 伪类与伪元素的区别:伪类(:)选择元素的状态或位置,伪元素(::,如 ::before)创建虚拟元素(如添加图标),二者常配合使用(如场景 3 中的 :first-child::before)。

2. 最佳实践

  1. 链接样式:严格遵循 LVHA 顺序,统一 visitedlink 的样式差异(避免用户混淆);
  2. 结构化样式:优先使用 :nth-child()/:nth-of-type() 替代手动添加 class(如 class="odd"/class="even"),减少 HTML 冗余;
  3. 性能优化:避免在复杂页面(如大量列表)使用过于复杂的结构化伪类(如 :nth-child(3n+2)),可能影响渲染性能;
  4. 兼容性:现代浏览器均支持大部分伪类(IE 11 支持核心伪类如 :nth-child:hover),无需额外兼容处理

欢迎关注我的其它发布渠道