深入理解 CSS 伪类和伪元素的本质区别

在写前端CSS样式的时候,经常用到这种:hover或者::before有冒号的写法。我很疑惑,为什么有些是单冒号,有些又是双冒号呢?

后来我才知道这种写法也区分为伪类伪元素

伪类和伪元素是什么?

伪类(Pseudo-classes):​用于选择处于特定状态的元素。也可以理解为,当元素处于某种状态的时候,给它加上一个"类"来定义样式。

  • 语法:单个冒号:,例如:hover

伪元素(Pseudo-elements):用于选择元素的特定部分。可以理解为,它在文档中创建了一个虚拟的"元素"来设置样式。

  • 语法:双冒号::,例如::before

只要是单个冒号的就一定是伪元素吗?

在现代CSS3点规范中,所有使用双冒号::语法的选择器都被定义为伪元素。这是W3C为了明确区分伪类和伪元素而引入的约定。

但在早期的CSS中,伪元素也使用单冒号,因为当时没有区分语法。所以为了向后兼容,大多数的浏览器还是会支持:before:after等单冒号的写法。

但新的伪元素只支持双冒号,比如:::selection


伪类(Pseudo-classes)

伪类用于选择处于特定状态的元素,比如用户交互状态、结构位置等。

常见伪类示例:

  • :hover:鼠标悬停时
  • :focus:元素获得焦点时(如输入框)
  • :active:元素被激活时(如点击按钮)
  • :visited:链接已被访问过
  • :first-child / :last-child:第一个/最后一个子元素
  • :nth-child(n):第 n 个子元素
  • :not(selector):排除匹配 selector 的元素

示例:

css 复制代码
a:hover {
  color: red;
}

input:focus {
  border: 2px solid blue;
}

li:first-child {
  font-weight: bold;
}

伪类使用单冒号


伪元素(Pseudo-elements)

伪元素用于创建并样式化文档中不存在的虚拟元素,比如段落首字母、选中文本、元素前后插入内容等。

常见伪元素:

  • ::before:在元素内容前插入内容
  • ::after:在元素内容后插入内容
  • ::first-letter:段落的第一个字母
  • ::first-line:段落的第一行
  • ::selection:用户选中的文本(部分浏览器需加前缀)

示例:

css 复制代码
p::first-letter {
  font-size: 2em;
  color: gold;
}

.quote::before {
  content: """;
}

.quote::after {
  content: """;
}

::selection {
  background: yellow;
  color: black;
}

伪元素使用双冒号::before),这是CSS3的规范写法。出于兼容性考虑,旧代码可能仍用单冒号(如:before),现代项目建议用双冒号。


主要区别

特性 伪类(Pseudo-class) 伪元素(Pseudo-element)
作用对象 已存在的元素的状态或位置 创建不存在的虚拟内容或部分
语法 单冒号(如 :hover 双冒号(如 ::before
是否生成内容 是(常配合 content 属性)
示例 a:hover, :nth-child(2) ::first-letter, ::after

示例效果

一个简单的待办事项列表

完整代码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>伪类vs伪元素示例</title>
    <style>
        .container {
            max-width: 500px;
            margin: 0 auto;
            background: white;
            padding: 30px;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
        }

        h1 {
            text-align: center;
            color: #2c3e50;
            margin-bottom: 30px;
        }

        .explanation {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
            border-left: 4px solid #3498db;
        }

        .todo-list {
            list-style: none;
            padding: 0;
        }

        .todo-list li {
            padding: 15px;
            margin: 8px 0;
            background: white;
            border: 2px solid #e9ecef;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
        }

        /* === 伪类样式 === */
        /* 第一个子元素 */
        .todo-list li:first-child {
            border-left: 4px solid #e74c3c;
        }

        /* 偶数项 */
        .todo-list li:nth-child(even) {
            background-color: #f8f9fa;
        }

        /* 悬停效果 */
        .todo-list li:hover {
            background-color: #aab49b;
            color: white;
            transform: translateX(10px);
            border-color: #aab49b;
        }

        /* 点击效果 */
        .todo-list li:active {
            background-color: #2ecc71;
            transform: scale(0.98);
        }

        /* === 伪元素样式 === */
        /* 前面的图标 */
        .todo-list li::before {
            content: "📌";
            margin-right: 10px;
            transition: all 0.3s ease;
        }

        /* 悬停时图标变化 */
        .todo-list li:hover::before {
            content: "🔥";
            transform: scale(1.2);
        }

        /* 后面的装饰线 */
        .todo-list li::after {
            content: "";
            position: absolute;
            left: 0;
            bottom: 0;
            width: 0;
            height: 3px;
            background: linear-gradient(90deg, #e74c3c, #669521);
            transition: width 0.3s ease;
        }

        .todo-list li:hover::after {
            width: 100%;
        }

        /* 首字母样式 */
        .todo-list li::first-letter {
            font-size: 1.3em;
            color: #2c3e50;
            font-weight: bold;
        }

        .todo-list li:hover::first-letter {
            color: white;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>伪类 vs 伪元素 演示</h1>
        
        <div class="explanation">
            <p><strong>伪类(Pseudo-class)</strong>:选择元素的特定<strong>状态</strong></p>
            <p><strong>伪元素(Pseudo-element)</strong>:选择元素的特定<strong>部分</strong></p>
        </div>

        <ul class="todo-list">
            <li>学习 CSS 伪类</li>
            <li>理解伪元素</li>
            <li>完成项目练习</li>
            <li>复习知识点</li>
        </ul>
    </div>

    <script>
        // 添加点击切换完成状态的功能
        document.querySelectorAll('.todo-list li').forEach(item => {
            item.addEventListener('click', function() {
                this.classList.toggle('completed');
            });
        });
    </script>
</body>
</html>

在这个示例中,可以清晰地看到:

伪类(操作的是整个元素)

  • :first-child:操作第一个<li>元素的整体样式
  • :nth-child(even):操作偶数位置<li>的整体背景
  • :hover:操作鼠标悬停时<li>的整体状态变化

伪元素(操作的是元素的一部分)

  • ::before:在<li>内容之前插入新内容(图标)
  • ::after:在<li>内容之后插入装饰线条
  • ::first-letter:只样式化<li>文本的第一个字母

简单记忆:伪类是状态选择器,伪元素是内容生成器。


总结

伪类

  • 选择元素的特定状态(如:hover、:focus)
  • 语法使用单冒号 (如:hover
  • 不生成新内容,只针对元素本身的状态变化
  • 常见用途:用户交互反馈、结构位置选择

伪元素

  • 创建并样式化元素的特定部分虚拟内容
  • 语法使用双冒号 (如::before
  • 常配合content属性生成新内容
  • 常见用途:插入装饰元素、样式化文本部分

通过上面的内容,我也终于搞懂了伪类和伪元素的区别。

本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《async/await 到底要不要加 try-catch?异步错误处理最佳实践》

《Vue3 和 Vue2 的核心区别?很多开发者都没完全搞懂的 10 个细节》

《Java 开发必看:什么时候用 for,什么时候用 Stream?》

《这 10 个 MySQL 高级用法,让你的代码又快又好看》

相关推荐
HIT_Weston4 小时前
64、【Ubuntu】【Gitlab】拉出内网 Web 服务:Gitlab 配置审视(八)
前端·ubuntu·gitlab
余生H4 小时前
前端科技新闻(WTN-3)React v19 引发 Cloudflare 异常事件复盘 - 一次序列化升级,如何影响全球边缘网络?
前端·科技·react.js
HIT_Weston4 小时前
62、【Ubuntu】【Gitlab】拉出内网 Web 服务:Gitlab 配置审视(六)
前端·ubuntu·gitlab
ID_180079054735 小时前
淘宝关键词搜索 API 系列 数据返回参考(附解析与实战)
java·服务器·前端
Hao_Harrision5 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨| BackgroundSlider(背景滑块)
前端·typescript·react·vite7·tailwildcss
weixin_307779135 小时前
Jenkins Font Awesome API插件:现代化插件界面的图标引擎
开发语言·前端·自动化·jenkins
June bug5 小时前
【Vue】从0开始使用Vue构建界面
前端·vue.js·前端框架
lcc1875 小时前
CSS 三大特性
css
IT_陈寒5 小时前
SpringBoot3.0性能优化:这5个冷门配置让我节省了40%内存占用
前端·人工智能·后端