CSS3 新增伪类详解
CSS3 新增伪类详解
一、Introduction(简介)
CSS3 在 CSS2.1 的基础上新增了大量伪类(Pseudo-classes),这些选择器以冒号(:)开头,用于选择元素的特定状态或结构位置,而无需添加额外的 HTML 类名。CSS3 伪类的引入极大地提升了选择器的表达能力和灵活性,让开发者能够以更简洁、更声明式的方式实现复杂的样式效果。从结构伪类到表单伪类,从状态伪类到否定伪类,CSS3 伪类已经成为现代前端开发中不可或缺的工具,是构建响应式、交互友好界面的重要基石。
二、基础语法
1. 伪类的基本语法
伪类的语法形式为:
selector:pseudo-class {
property: value;
}
伪类可以链式使用,例如:
a:hover:focus {
/* 鼠标悬停且获得焦点时的样式 */
}
2. CSS3 伪类分类
CSS3 伪类大致可分为以下几类:
- 结构伪类(Structural Pseudo-classes):基于文档树中的位置来选择元素,如
:first-child、:last-child、:nth-child()等。 - 目标伪类(Target Pseudo-class):选择当前活动的目标元素,如
:target。 - 语言伪类(Language Pseudo-class):根据元素语言选择,如
:lang()。 - UI 元素状态伪类(UI Element State Pseudo-classes):基于表单元素状态选择,如
:checked、:enabled、:disabled、:valid、:invalid等。 - 否定伪类(Negation Pseudo-class):
:not()排除特定元素。 - 结构性伪类变体:如
:first-of-type、:last-of-type、:nth-of-type()等。 - 空内容伪类:
:empty选择没有子元素的元素。 - 根伪类:
:root选择文档根元素。
三、代码示例
1. 结构伪类示例
/* 选择父元素下的第一个子元素 */
li:first-child {
border-top: none;
}
/* 选择父元素下的最后一个子元素 */
li:last-child {
border-bottom: none;
}
/* 选择父元素下唯一的子元素 */
p:only-child {
background: #f0f0f0;
}
/* 奇偶行斑马纹表格 */
tr:nth-child(odd) {
background: #f9f9f9;
}
tr:nth-child(even) {
background: #ffffff;
}
/* 每隔3个元素选择第3n个(3, 6, 9...) */
.item:nth-child(3n) {
color: red;
}
/* 从第2个开始选择(2, 3, 4...) */
.item:nth-child(n+2) {
font-weight: bold;
}
/* 前5个(1, 2, 3, 4, 5) */
.item:nth-child(-n+5) {
margin-top: 0;
}
/* 选择倒数第3个 */
li:nth-last-child(3) {
opacity: 0.6;
}
2. :first-of-type / :last-of-type 系列
/* 同级中第一种类型的首个元素 */
p:first-of-type {
font-size: 1.5em;
}
/* 同级中最后一种类型的末个元素 */
span:last-of-type {
color: blue;
}
/* 选择同类型中的唯一一个 */
img:only-of-type {
display: block;
margin: 0 auto;
}
/* 同类型倒数第一个 */
div:nth-last-of-type(1) {
border-bottom: none;
}
3. 表单状态伪类示例
/* 未被选中的单选/复选框 */
input[type="radio"]:not(:checked) + label {
color: #999;
}
/* 启用状态的输入框 */
input:enabled {
border-color: green;
}
/* 禁用状态的输入框 */
input:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 聚焦的输入框 */
input:focus {
outline: 2px solid blue;
box-shadow: 0 0 5px rgba(0,0,255,0.3);
}
/* 通过验证的输入框 */
input:valid {
border-color: #2ecc71;
}
/* 未通过验证的输入框 */
input:invalid {
border-color: #e74c3c;
}
/* 占位符显示时 */
input:placeholder-shown {
background: #f0f0f0;
}
4. 完整表单验证样式示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<style>
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, textarea, select {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
transition: all 0.3s;
}
/* 必填项聚焦时的样式 */
input:required:focus {
border-color: #3498db;
}
/* 验证通过的样式 */
input:valid {
border-color: #2ecc71;
background: rgba(46, 204, 113, 0.05);
}
/* 验证失败的样式 */
input:invalid:not(:placeholder-shown):not(:focus) {
border-color: #e74c3c;
background: rgba(231, 76, 60, 0.05);
}
/* 只读/禁用 */
input:read-only {
background: #f5f5f5;
}
input:read-write {
background: white;
}
/* 选中单选按钮 */
input[type="radio"]:checked + span {
font-weight: bold;
color: #3498db;
}
/* 复选框被选中时改变标签颜色 */
input[type="checkbox"]:checked + label {
color: #2ecc71;
text-decoration: line-through;
}
/* 选中选项的颜色 */
option:checked {
background: #3498db;
color: white;
}
</style>
</head>
<body>
<form>
<div class="form-group">
<input type="text" required placeholder="请输入姓名">
</div>
<div class="form-group">
<input type="email" required placeholder="请输入邮箱">
</div>
<div class="form-group">
<input type="number" min="18" max="100" placeholder="年龄">
</div>
<div class="form-group">
<input type="text" readonly value="不可修改的内容">
</div>
<div class="form-group">
<input type="text" disabled value="禁用字段">
</div>
</form>
</body>
</html>
5. :not() 否定伪类示例
/* 除最后一个外的所有列表项 */
li:not(:last-child) {
border-bottom: 1px solid #eee;
}
/* 除特定类名外的所有元素 */
div:not(.exclude):not(.another-exclude) {
padding: 20px;
}
/* 非空段落 */
p:not(:empty) {
min-height: 1em;
}
/* 排除第一和最后两个 */
li:not(:first-child):not(:nth-last-child(-n+2)) {
margin: 5px 0;
}
/* 排除禁用状态的按钮 */
button:not(:disabled) {
cursor: pointer;
opacity: 1;
}
button:disabled {
cursor: not-allowed;
opacity: 0.5;
}
6. :empty 和 :root 伪类示例
/* 选择空元素(无子节点和文本) */
p:empty::before {
content: "暂无内容";
color: #999;
}
/* 等同于 html,但优先级更高 */
:root {
--primary-color: #3498db;
--bg-color: #f5f5f5;
}
/* :target 示例 - URL 带 #id 时触发样式 */
:target {
background: #fffde7;
border-left: 4px solid #ffc107;
}
7. 实用斑马纹 + 悬停表格
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<style>
table {
width: 100%;
border-collapse: collapse;
}
/* 斑马纹 - 奇数行 */
tbody tr:nth-child(odd) {
background: #f8f9fa;
}
/* 斑马纹 - 偶数行 */
tbody tr:nth-child(even) {
background: #ffffff;
}
/* 悬停高亮 */
tbody tr:hover {
background: #e3f2fd;
}
/* 每行最后一列右对齐 */
td:nth-last-child(1) {
text-align: right;
}
/* 第一个单元格加粗 */
td:first-child {
font-weight: bold;
}
/* 最后一列特殊颜色 */
td:last-child {
color: #2ecc71;
}
/* 首行样式 */
thead th:first-child { border-radius: 8px 0 0 0; }
thead th:last-child { border-radius: 0 8px 0 0; }
thead th {
background: #3498db;
color: white;
padding: 12px;
}
/* 唯一子行 */
tr:only-child {
background: #fffde7 !important;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>商品</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr>
<td>Python 入门</td>
<td>2</td>
<td>¥49.00</td>
<td>¥98.00</td>
</tr>
<tr>
<td>JavaScript 高级</td>
<td>1</td>
<td>¥79.00</td>
<td>¥79.00</td>
</tr>
<tr>
<td>CSS3 权威指南</td>
<td>3</td>
<td>¥89.00</td>
<td>¥267.00</td>
</tr>
</tbody>
</table>
</body>
</html>
四、运行效果
1. 结构伪类效果
:first-child/:last-child精准选择首个或末个元素,无需额外类名。
:nth-child(odd/even)自动生成斑马纹表格,交替背景色提升可读性。:nth-child(3n)每3个元素应用特殊样式,常用于网格布局中控制行列。
:only-child仅有一个子元素时触发样式,适用于单卡片场景。
2. 表单伪类效果
:valid/:invalid在用户输入时实时反馈验证状态,无需 JavaScript。
:checked选中单选/复选框后立即改变关联标签样式,提升交互反馈感。:disabled/:enabled区分可用/禁用状态,引导用户操作。
:focus聚焦时出现高亮边框或阴影,提升无障碍访问体验。
3. :not() 否定伪类效果
- 配合
:last-child实现"除最后一个外都加底边"的分隔线效果。
- 链式排除多个类,替代过去需要多个规则或 JavaScript 的复杂逻辑。
五、常见问题(FAQ)
Q1::first-child 和 :first-of-type 有什么区别?
区别: :first-child 要求元素是其父元素的第一个子元素,且该元素本身必须是指定类型。:first-of-type 则是在父元素的所有子元素中,找到第一个指定类型的元素(忽略非该类型的子元素)。举例:div p:first-child 要求 p 必须是父 div 的第一个孩子;而 div p:first-of-type 则是在 div 的所有子 p 中取第一个。
Q2::nth-child() 的参数有哪些合法写法?
合法参数:
- 关键字:
odd(奇数)、even(偶数)
- 整数:
3(第3个) - 代数式:
3n(每3个)、2n+1(奇数)、3n+2(2,5,8...)、-n+5(前5个)、n+2(从第2个开始)
- 关键词:
of S配合其他选择器(CSS3 Selector Level 4 支持)。
Q3::valid 和 :invalid 为什么不工作?
原因:这两个伪类需要浏览器内置的 HTML5 表单验证机制支持。如果 input 没有 required、pattern、type="email" 等验证属性,则不会触发验证状态。
解决方案:确保 input 元素有适当的验证属性(如 type="email"、type="url"、pattern="\d+" 等)。
Q4::not(:empty) 为什么有时不生效?
原因:即使元素内有空格或换行,浏览器也将其视为有子节点,:empty 不会匹配。通常意义上的"空"(无内容)包括空白字符,但 :empty 只匹配真正什么都没有的元素(包括没有空白文本节点)。
Q5::focus-within 是什么?
说明::focus-within 是 CSS3 新增的焦点伪类,当元素本身或其任意后代获得焦点时触发。这个伪类非常适合实现"表单聚焦时整体高亮"的效果(例如父容器加阴影、边框变色的效果),这是 :focus 无法单独完成的。
Q6::is() 和 :where() 与伪类有什么关系?
说明::is() 和 :where() 是 CSS3 后续版本(Selectors Level 4)引入的伪类函数,它们接受选择器列表作为参数。:is(h1, h2, h3):hover 等同于分别写这三个选择器的悬停样式,大幅简化代码。:where() 与 :is() 功能相同,但 specificity 始终为 0,便于覆盖样式。
六、延伸阅读
1. CSS Selectors Level 4 新特性
Selectors Level 4 规范在 CSS3 基础上引入了更多强大的伪类,包括 :has()(父选择器,接受子选择器参数,如 div:has(p) 选择包含 p 的 div)、:is() 和 :where() 伪类函数(接受选择器列表)、:focus-visible(仅在键盘焦点时触发,与鼠标点击区分)、:placeholder-shown 等。主流现代浏览器已广泛支持这些新特性。
2. :has() 父选择器详解
:has() 被称为"父选择器",是 CSS 选择器历史上最重大的突破之一。它允许选择基于子元素或后代元素状态来选择父元素。例如:form:has(input:invalid) 选择包含无效输入框的表单;li:has(> .active) 选择包含带 active 类直接子元素的 li。:has() 的出现使得许多过去必须依赖 JavaScript 的 DOM 操作可以用纯 CSS 实现。
3. :focus-visible 的无障碍意义
:focus-visible 只在用户通过键盘(Tab 键)导航时才显示焦点样式,鼠标点击时不显示。这解决了长期以来"焦点环在鼠标操作时显得多余"的问题,同时保留了键盘用户的可访问性。实现方式为::focus { outline: none; } 清除默认焦点环,然后 :focus-visible { outline: 2px solid blue; } 仅在键盘导航时显示。
4. 相关资源链接
- MDN CSS Pseudo-classes:https://developer.mozilla.org/zh-CN/docs/Web/CSS/Pseudo-classes
- CSS Selectors Level 4 规范:https://www.w3.org/TR/selectors-4/
- CSS-Tricks :nth Tester:https://css-tricks.com/examples/nth-child-tester/
CSS3 新增伪类极大丰富了选择器的表达能力,从简单的结构选择到复杂的表单验证状态,从静态样式到动态交互反馈,伪类已经成为现代 CSS 不可或缺的核心语法。掌握这些伪类的用法,配合 Selectors Level 4 的新特性,可以显著减少对 JavaScript 的依赖,写出更简洁、更高效的样式代码。