CSS3 新增伪类详解

小飞兽 HTML/CSS 183 次阅读 2026-04-25

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 没有 requiredpatterntype="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. 相关资源链接

  • Can I Use - CSS3 Pseudo-classes:https://caniuse.com/css-sel3

CSS3 新增伪类极大丰富了选择器的表达能力,从简单的结构选择到复杂的表单验证状态,从静态样式到动态交互反馈,伪类已经成为现代 CSS 不可或缺的核心语法。掌握这些伪类的用法,配合 Selectors Level 4 的新特性,可以显著减少对 JavaScript 的依赖,写出更简洁、更高效的样式代码。