伪类与伪元素深度解析:before/after 实用案例

伪类与伪元素深度解析:before/after 实用案例


一、先分清两个东西

很多人把伪类和伪元素混为一谈,其实它们是 CSS 的两套完全不同的系统。

伪类(Pseudo-class) 伪元素(Pseudo-element)
选择器写法 一个冒号 :hover 两个冒号 ::before
本质 描述元素的状态 创建一个不存在的虚拟元素
能否加样式 ✅ 能 ✅ 能(因为它真的生成了节点)
DOM 中是否存在 否,只是选择器匹配规则 否,但渲染树中真实存在

⚠️ 旧标准写一个冒号(:before)也能用,现代写法用两个冒号(::before)以区分伪类。实际项目中两者都兼容,但建议统一用双冒号。

一句话区分:伪类是"它现在什么状态",伪元素是"凭空造出一块东西"。


二、伪类:状态选择器

高频伪类速查

伪类 含义 典型场景
:hover 鼠标悬停 按钮变色
:active 按下瞬间 点击反馈
:focus 获得焦点 输入框高亮
:focus-within 自身或子元素获得焦点 表单容器高亮
:first-child 第一个子元素 去掉首项 margin
:last-child 最后一个子元素 去掉末项 padding
:nth-child(n) 第 n 个子元素 斑马条纹
:not(.xxx) 排除某类 排除禁用状态
:is(.a, .b) 匹配任一 简化重复选择器
:has(.xxx) 包含某子元素 父级联动(CSS4)

:has() --- 伪类里的杀手级特性

css 复制代码
css
/* 父元素包含 checked 的 input 时,改变自身样式 */
.card:has(input:checked) {
  border-color: #4f46e5;
  background: #eef2ff;
}

以前这事只能用 JS 干,现在纯 CSS 一行搞定。


三、伪元素:凭空造节点

::before / ::after 核心规则

css 复制代码
css
.element::before {
  content: "";        /* ❗ 必须有,否则不渲染 */
  display: block;     /* 或 inline-block,决定它的盒子模型 */
  width: 100px;
  height: 100px;
  /* ...其他样式 */
}

三个铁律

  1. content 必须写,空字符串 "" 也行,不写等于没这个元素
  2. 默认 display: inline,不占宽高,必须手动改 blockinline-block
  3. 它是元素的第一个/最后一个子节点,不是兄弟节点

四、before/after 实用案例(10 个)

案例 1:清除浮动(经典中的经典)

css 复制代码
css
.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

现在有了 Flex/Grid,这个用法在减少,但维护旧项目时天天见。


案例 2:自定义列表序号

css 复制代码
css
li::before {
  content: "✓ ";
  color: #22c55e;
  font-weight: bold;
  margin-right: 8px;
}

效果:不用 <span> 标签,纯 CSS 造出绿色对勾。


案例 3:装饰性引号

css 复制代码
css
blockquote::before {
  content: "「";
  font-size: 48px;
  color: #94a3b8;
  line-height: 0;
  vertical-align: -0.5em;
  margin-right: 4px;
}

blockquote::after {
  content: "」";
  font-size: 48px;
  color: #94a3b8;
  line-height: 0;
  vertical-align: -0.5em;
  margin-left: 4px;
}

效果:引用文字自动被大号引号包裹,HTML 里一个字符都不用加。


案例 4:输入框聚焦动画(下划线展开)

css 复制代码
css
.input-wrapper {
  position: relative;
}

.input-wrapper::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  width: 0;
  height: 2px;
  background: #6366f1;
  transition: all 0.3s;
  transform: translateX(-50%);
}

.input-wrapper:focus-within::after {
  width: 100%;
}

效果:输入框聚焦时,底部出现一条从中间展开的彩色下划线。


案例 5:图片叠加渐变遮罩

css 复制代码
css
.card-img::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
}

效果:图片底部渐变变暗,文字直接叠加在图上清晰可读,不用加任何 <div> 遮罩层。


案例 6:气泡对话框小三角

css 复制代码
css
.tooltip::after {
  content: "";
  position: absolute;
  top: -8px;
  left: 50%;
  transform: translateX(-50%);
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-bottom: 8px solid #1e293b;
}

效果:纯 CSS 画出一个向上的小三角,气泡框指向下方元素。


案例 7:徽章/角标(未读消息红点)

css 复制代码
css
.badge::after {
  content: "3";
  position: absolute;
  top: -6px;
  right: -6px;
  width: 18px;
  height: 18px;
  background: #ef4444;
  color: white;
  font-size: 11px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

效果:右上角红色圆形角标,数字居中,零 JS 零图片。


案例 8:加载动画(纯 CSS spinner)

css 复制代码
css
.spinner::after {
  content: "";
  display: block;
  width: 20px;
  height: 20px;
  border: 3px solid #e2e8f0;
  border-top-color: #6366f1;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

效果:元素后面自动出现旋转加载圈,比 <img> 轻量一百倍。


案例 9:分栏装饰线(伪元素做分隔符)

css 复制代码
css
.price::before {
  content: "¥";
  font-size: 14px;
  vertical-align: super;
  color: #94a3b8;
}

.old-price::after {
  content: attr(data-original);  /* 读取 HTML 属性值 */
  text-decoration: line-through;
  color: #94a3b8;
  font-size: 12px;
  margin-left: 8px;
}

HTML:

ini 复制代码
html
<span class="price" data-original="199">99</span>

效果:¥99 ~~199~~,原价从 data 属性读取,不用写死在 HTML 里。


案例 10:多行文本省略 + "展开"按钮

css 复制代码
css
.text-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.text-clamp::after {
  content: " 展开";
  color: #6366f1;
  cursor: pointer;
  font-weight: 500;
}

效果:文本超过 3 行自动省略,末尾自动跟一个"展开"链接,纯 CSS 实现。


五、content 能放什么?

说明
"" 空字符串(最常用,造盒子用)
"文字" 直接显示文本
attr(href) 读取元素属性值
counter(num) 读取计数器值
url(...) 插入背景图(注意是背景,不是 img 标签)
none 不生成伪元素

六、避坑清单

正确做法
::before 没写 content 必写 content: "",否则整个伪元素不存在
伪元素用了 float 伪元素默认 inline,浮动会导致 display 变为 block,行为不可控
想给伪元素加点击事件 ❌ 不可能,伪元素不在 DOM 树中,无法绑定事件
::before::after 嵌套 ❌ 不支持,伪元素不能再生成伪元素
屏幕阅读器读不到伪元素内容 content 里的文字会被读到,但装饰性内容建议用 ""

七、一张图总结

ruby 复制代码
伪类 :    选择"状态"   → :hover :focus :nth-child :has()
              ↓
         不创造节点,只改变匹配规则

伪元素 ::   创造"节点"   → ::before ::after ::placeholder ::selection
              ↓
         真的在渲染树中生成了一个虚拟盒子
         能加宽高、背景、动画、内容
         但不在 DOM 中,JS 抓不到

核心结论:伪类让你精准选中"某种状态的元素",伪元素让你在不改 HTML 的前提下凭空造出一块可样式化的区域。before/after 是 CSS 里最被低估的瑞士军刀------它不是装饰,是真正的布局工具。

相关推荐
码事漫谈2 小时前
时序数据库2026盘点:国产数据库如何以“融合多模”走出差异化之路?
前端·后端
浮游本尊2 小时前
Java学习第42天 - Spring 事务传播、隔离级别、锁机制与并发一致性
后端
道友可好2 小时前
让 AI 自己验收,等于让学生自己批卷
前端·人工智能·后端
鱼人2 小时前
响应式三巨头:rem / vw / em 深度对比,移动端到底该选谁?
后端
小强19882 小时前
Grid 网格布局实战:快速实现复杂网页排版
后端
胡志辉2 小时前
深入浅出 call、apply、bind
前端·javascript·后端
长大19882 小时前
Flex 布局完整教程:告别浮动,拥抱万能弹性布局
后端
iccb10133 小时前
5年,一个程序员是如何把私有化在线客服系统做到第一名的
前端·后端·github
Rust研习社3 小时前
这 8 个 Rust 学习资源值得每个新手收藏起来
后端·rust·编程语言