定义
伪元素(如 ::before、::after)是在 CSS 中定义的,在浏览器渲染页面时“临时生成”的虚拟内容,它们不会出现在真实的 HTML DOM 结构中。
需要伪元素最主要的原因是:在不修改 HTML 结构的前提下,仅通过 CSS 实现装饰性或功能性内容的添加。
这样可以:
- 保持 HTML 语义纯净:HTML 应该只描述内容结构和语义(比如标题、段落、按钮),而 图标、装饰线、小红点、背景弧形等属于视觉表现。如果为了一个红点就加
<span class="badge"></span>,HTML 会变得臃肿、语义不清 - 减少 DOM 节点数量:每个真实 DOM 元素都会占用内存、增加渲染负担。用伪元素实现装饰,零额外 DOM 节点,对性能敏感场景(如列表、轮播)尤其重要
- 提升开发效率与可维护性:修改样式只需改 CSS,不用回去找 HTML 加/删标签。团队协作中,设计师调整 UI 不会影响结构代码。
- 实现某些“纯 CSS 技巧”:有些效果必须依赖伪元素才能实现。
- 清除浮动(旧方法)
- Tooltip 小三角(用 border 模拟)
- 双层边框、叠加蒙版
- 动画遮罩、悬停特效
核心特点:
| 特性 | 说明 |
|---|---|
| 不是真实 DOM 节点 | 无法通过 JavaScript 直接操作(如 document.querySelector 找不到) |
必须用双冒号 :: |
如 ::before, ::after(CSS3 标准) |
需要 content 属性 |
否则不渲染(即使为空也要写 content: "") |
| 可设置完整样式 | 宽高、颜色、定位、背景、动画等都支持 |
常见的伪元素(共 7 个,常用 4 个)
| 伪元素 | 作用 | 是否常用 |
|---|---|---|
::before |
在元素内容最前面插入内容 | ✅ 极常用 |
::after |
在元素内容最后面插入内容 | ✅ 极常用 |
::first-line |
选中块级元素的第一行文本 | ⚠️ 较少用 |
::first-letter |
选中块级元素的首字母 | ⚠️ 排版用 |
::selection |
设置用户选中文本的样式 | ✅ 实用 |
::placeholder |
设置 <input> / <textarea> 的占位符样式 |
✅ 常用(但注意:它是伪类 :placeholder,不是伪元素) |
::backdrop |
全屏模式或 <dialog> 背景遮罩 |
⚠️ 特殊场景 |
案例
::before —— 在内容前插入
用途:
- 添加图标
- 创建装饰背景
- 实现小红点、角标
<template>
<view class="container">
<view class="item before-icon">前端开发</view>
<view class="item before-badge">新消息</view>
</view>
</template>
<style lang="scss">
.container {
padding: 60rpx;
}
.item {
position: relative;
margin-bottom: 50rpx;
padding: 20rpx 30rpx;
background: #f0f9ff;
border-radius: 16rpx;
}
/* 插入图标 */
.before-icon::before {
content: "🚀";
margin-right: 12rpx;
}
/* 右上角红点 */
.before-badge::before {
content: "";
position: absolute;
top: -8rpx;
right: -8rpx;
width: 24rpx;
height: 24rpx;
background: #ff4d4f;
border: 4rpx solid white;
border-radius: 50%;
}
</style>
理解什么叫做内容
元素和内容
“内容”指的是:前端开发(即元素的文本内容或子节点),而不是整个 <view> 标签。
<view class="item before-icon">前端开发</view>
│ ↑↑↑↑↑↑↑↑↑
│ 这才是“内容”
└───────────────────────────── 这是“元素标签”
| 术语 | 指代 |
|---|---|
| 元素(element) | 整个 <view class="item before-icon">前端开发</view> |
| 内容(content) | 仅 "前端开发"(包括文本、子元素等) |
::before是插入到“内容”的最前面,而不是“标签”的前面。
渲染逻辑
文本
<view>前端开发</view>
应用 ::before 后(视觉效果,非真实 DOM):
<view>🚀 前端开发</view>
↑
└── 插在这里!—— 内容的开头
不是:
<!-- ❌ 错误理解 -->
🚀
<view>前端开发</view>
子元素
<view class="box">
<text>子元素1</text>
<text>子元素2</text>
</view>
.box::before {
content: "★";
}
渲染效果相当于:
<view class="box">
★ <!-- ::before 插在这里 -->
<text>子元素1</text>
<text>子元素2</text>
</view>
插在所有子内容之前,但仍在 <view> 内部!
::before 的父级是谁
是它所依附的那个元素本身:
.before-icon::before的“父级” =<view class="item before-icon">.before-badge::before的“父级” =<view class="item before-badge">
[ .container ]
│
├─┬ [ .item + .before-icon ] ← 同一个 view,两个 class
│ ├─ ::before ("🚀")
│ └─ Text("前端开发")
│
└─┬ [ .item + .before-badge ] ← 同一个 view,两个 class
├─ ::before (红点,absolute)
└─ Text("新消息")
<view class="container">—— 最外层容器<view class="item before-icon">—— 第一个子项(同时有item和before-icon两个 class)<view class="item before-badge">—— 第二个子项(同时有item和before-badge两个 class)- 注意:
.before-icon和.before-badge不是独立元素,只是 class 名!它们和.item是同一个<view>元素的多个类名。
解释红点为什么在右上方
逻辑上,::before 内容是插入到目标元素的内容最前面。在这个例子中,尽管 content: "" 是空的,但由于设置了宽高、背景色等属性,它创建了一个小圆点。
使用 position: absolute; 允许开发者精确地控制该伪元素相对于其最近的已定位(即 position 值不是 static 的)祖先元素的位置。如果没有这样的祖先,则相对于初始包含块(通常是视口)。
在这里,top: -8rpx; right: -8rpx; 将这个伪元素从默认位置向上和向右移动了一定距离,使其看起来像是附着在目标元素的右上角外侧。
为什么absolute能相对于.item 定位?
.before-badge::before是.item的生成内容- 它的包含块(containing block) 就是
.item元素 - 而
.item设置了position: relative - 所以
::before的position: absolute就相对于.item定位
改写后的代码(纯 HTML 实现)
<template>
<view class="container">
<!-- 改写 1:用真实图标 -->
<view class="item icon-item">
<text class="icon">🚀</text>
<text class="label">前端开发</text>
</view>
<!-- 改写 2:用真实红点 -->
<view class="item badge-item">
<text class="label">新消息</text>
<!-- 虽然 ::before 在逻辑上“插入在内容前”,
但红点是通过 position: absolute 定位到右上角的 —— 它的视觉位置和 DOM 顺序无关。
所以用真实 HTML 实现时,<view class="red-dot"> 放在 <text> 前后都可以,只要它能被正确定位。 -->
<view class="red-dot"></view>
</view>
</view>
</template>
<script>
export default {}
</script>
<style lang="scss">
.container {
padding: 60rpx;
}
.item {
position: relative;
margin-bottom: 50rpx;
padding: 20rpx 30rpx;
background: #f0f9ff;
border-radius: 16rpx;
// 注意:因为用了子元素,可能需要调整布局
}
/* === 改写 1:图标用真实 text === */
.icon-item {
display: flex;
align-items: center;
}
.icon {
margin-right: 12rpx;
font-size: inherit;
}
.label {
// 可选:确保文字样式一致
}
/* === 改写 2:红点用真实 view === */
.badge-item {
// 保持 position: relative,供红点定位
}
.red-dot {
position: absolute;
top: -8rpx;
right: -8rpx;
width: 24rpx;
height: 24rpx;
background: #ff4d4f;
border: 4rpx solid white;
border-radius: 50%;
}
</style>
::after —— 在内容后插入
用途:
- 添加箭头(如 tooltip)
- 清除浮动(旧方法)
- 追加单位(如 “元”、“%”)
<template>
<view class="container">
<view class="price">99</view>
<view class="tooltip">鼠标悬停</view>
</view>
</template>
<style lang="scss">
.container {
padding: 60rpx;
}
.price {
font-size: 48rpx;
font-weight: bold;
color: #e6a23c;
}
/* 追加“元”字 */
.price::after {
content: "元";
font-size: 32rpx;
margin-left: 8rpx;
}
.tooltip {
position: relative;
display: inline-block;
padding: 50rpx 100rpx;
background: #67c23a;
color: white;
border-radius: 8rpx;
}
/* 下方小三角 */
/*
把 .tooltip::after 改成 .tooltip::before 是完全可以的,而且视觉效果几乎一模一样
因为使用了 position: absolute,伪元素脱离了文档流,
它的视觉位置由 top/left 等定位属性决定,和它是 ::before 还是 ::after 无关。
开发者因为语义习惯 + 社区惯例,常用 ::after 而不是 ::before
*/
.tooltip::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -8rpx;
border: 30rpx solid transparent;
// 应该和上面的background,但是为了区别开,这里用黑色
border-top-color: #000000;
}
</style>
::selection —— 设置选中文本样式
**用途:**自定义高亮颜色(提升品牌感)
需加 selectable 属性才能选中文本
<template>
<view class="container">
<text selectable>请长按这段文字进行选择,看看高亮颜色是不是紫色?</text>
<br/>
<text >没有selectable修饰,是无法选择文本的</text>
</view>
</template>
<style lang="scss">
/* 全局设置选中样式 */
::selection {
background: #8e44ad;
color: white;
}
.container {
padding: 60rpx;
font-size: 32rpx;
line-height: 1.6;
}
</style>
::first-letter —— 首字母大写(杂志风)
<template>
<view class="article">
这是一段模拟的文章开头。首字母会被放大并变色,营造杂志排版效果。
</view>
</template>
<style lang="scss">
.article {
padding: 60rpx;
font-size: 32rpx;
line-height: 1.8;
/* 必须是 block 元素 */
display: block;
}
.article::first-letter {
font-size: 80rpx;
font-weight: bold;
color: #409eff;
float: left;
margin-right: 16rpx;
line-height: 1;
}
</style>
::first-line —— 第一行特殊样式
<template>
<view class="poem">
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
</view>
</template>
<style lang="scss">
.poem {
padding: 60rpx;
font-size: 64rpx;
line-height: 2;
/* 第一行加粗+红色 */
}
.poem::first-line {
font-weight: bold;
color: #e74c3c;
}
</style>
重要注意事项
content是必须的!.example::before { /* ❌ 不写 content → 伪元素不显示 */ content: ""; /* ✅ 即使为空也要写 */ }默认是行内元素
想设宽高?加
display: block或inline-block想绝对定位?父元素必须
position: relative
不能被 JavaScript 直接操作
// ❌ 无效!找不到 ::before document.querySelector('.item::before')✅ 替代方案:通过切换 class 控制
.active::before { background: red; }伪元素属于“子内容”
::before插在 第一个子节点前::after插在 最后一个子节点后