伪类
CSS 伪类详解:UI 伪类与结构化伪类的用法与实战
CSS 伪类(Pseudo-class)是一种特殊的选择符,用于选择处于特定状态或具有特定结构关系的元素—— 它们无需在 HTML 中添加实际的 class 或 id,而是通过元素的 “动态状态”(如鼠标悬停)或 “结构位置”(如列表第 3 项)来定位元素。伪类极大地扩展了 CSS 的选择能力,是实现交互效果和结构化样式的核心工具。从 “核心概念→分类详解→实战场景→注意事项” 四个维度,全面讲解伪类的使用方法与进阶技巧。
伪类核心概念
1. 伪类的语法
伪类以冒号 : 开头,紧跟伪类名称,语法格式如下:
/* 基础语法:选择符 + 伪类 */
选择符:伪类名称 {
属性: 值;
}
/* 示例:为 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 个伪类分别对应链接的不同生命周期状态,必须按固定顺序书写(link → visited → hover → active),否则样式可能失效。
| 伪类名称 | 作用描述 | 适用元素 | 示例效果 |
|---|---|---|---|
:link |
选择未被点击过的链接(默认状态) | <a>(必须有 href) |
未点击的链接为蓝色(默认) |
:visited |
选择已被点击过的链接(浏览器记录过访问历史) | <a>(必须有 href) |
已点击的链接为紫色(默认) |
:hover |
选择鼠标正悬停在上面的元素 | 所有元素(常用 <a>、按钮) |
鼠标悬停时链接变红色 |
:active |
选择正在被点击的元素(鼠标按下但未松开的瞬间) | 所有元素(常用 <a>、按钮) |
点击时链接变深红色 |
示例:链接样式的完整实现(按顺序书写)
/* 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; /* 更深的蓝色 */
}
关键注意事项:
顺序规则:
link→visited→hover→active(记忆口诀:LVHA),因为后定义的样式会覆盖先定义的,若顺序错误(如hover在visited前),visited样式会覆盖hover;:visited的限制:出于隐私保护,浏览器限制:visited可设置的样式(仅支持color、background-color、border-color等,无法设置background-image、box-shadow等);非链接元素的应用:hover和active可用于按钮、卡片等元素,实现交互效果:
/* 按钮 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. 聚焦的输入框 */
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; /* 默认边框颜色 */
}
<!-- 表单 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 个子元素”,支持多种取值方式,灵活度极高。
语法与取值
/* 基础语法:父元素下的第 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 等价于 odd,2n 等价于 even) |
| 反向计数 | :nth-child(-n+3) |
选择前 3 个子元素(n=0 时为 3,n=1 时为 2,n=2 时为 1,n=3 时为 0 不生效) |
示例 1:列表奇偶行交替色(表格 / 列表通用)
/* 无序列表的奇数行(1、3、5...) */
ul li:nth-child(odd) {
background-color: #f8f9fa; /* 浅灰色背景 */
}
/* 无序列表的偶数行(2、4、6...) */
ul li:nth-child(even) {
background-color: #ffffff; /* 白色背景 */
}
<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 个列表项
/* 选择前 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 的区别
<div class="container">
<span>这是一个 span 标签</span>
<p>这是第一个 p 标签</p>
<p>这是第二个 p 标签</p>
</div>
/* 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:表格奇偶行交替色(提升可读性)
/* 表格基础样式 */
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;
}
<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 伪类组合)
/* 导航菜单基础样式 */
.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; /* 点击时背景更深 */
}
<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:列表首项与尾项特殊样式(结构化伪类)
/* 列表基础样式 */
.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;
}
<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)会计算父元素的 “所有子元素”(无论类型),若父元素中混合了不同类型的元素(如span和li),计数会包含非目标类型元素;解决方案:使用
:nth-of-type(n)替代,仅按 “目标元素类型” 计数:<ul> <span>这是一个 span(会被 :nth-child 计入,但 :nth-of-type 忽略)</span> <li>列表项 1</li> <li>列表项 2</li> </ul>
/* 错误::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; - 原因:未按
link→visited→hover→active的顺序书写; - 解决方案:严格遵循 LVHA 顺序,确保后定义的伪类覆盖前定义的(如
hover在visited后,active在hover后)。
3. :hover 样式在移动设备上无响应
问题原因:移动设备(手机、平板)无 “鼠标悬停” 状态,
hover样式可能在点击后残留,影响体验;解决方案:
- 为移动设备添加触摸事件样式(
@media (hover: none)); - 避免在移动设备依赖
hover实现关键功能(如菜单展开),改用click事件;
/* 移动设备上禁用 hover 样式 */ @media (hover: none) { a:hover { color: inherit; /* 继承默认颜色,不改变 */ text-decoration: none; /* 不显示下划线 */ } }- 为移动设备添加触摸事件样式(
总结与最佳实践
1. 核心总结
- UI 伪类:基于元素的动态状态(如
hover、focus),用于实现交互效果,核心是响应用户操作; - 结构化伪类:基于元素的结构位置(如
:nth-child、:first-child),用于实现结构化样式,无需手动添加class; - 伪类与伪元素的区别:伪类(
:)选择元素的状态或位置,伪元素(::,如::before)创建虚拟元素(如添加图标),二者常配合使用(如场景 3 中的:first-child::before)。
2. 最佳实践
- 链接样式:严格遵循 LVHA 顺序,统一
visited与link的样式差异(避免用户混淆); - 结构化样式:优先使用
:nth-child()/:nth-of-type()替代手动添加class(如class="odd"/class="even"),减少 HTML 冗余; - 性能优化:避免在复杂页面(如大量列表)使用过于复杂的结构化伪类(如
:nth-child(3n+2)),可能影响渲染性能; - 兼容性:现代浏览器均支持大部分伪类(IE 11 支持核心伪类如
:nth-child、:hover),无需额外兼容处理