CSS:Pseudo-element伪元素-实现临时的装饰功能

定义

伪元素(如 ::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("新消息")
  1. <view class="container"> —— 最外层容器
  2. <view class="item before-icon"> —— 第一个子项(同时有 itembefore-icon 两个 class)
  3. <view class="item before-badge"> —— 第二个子项(同时有 itembefore-badge 两个 class)
  4. 注意:.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
  • 所以 ::beforeposition: 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>

重要注意事项

  1. content 是必须的!

    .example::before {
      /* ❌ 不写 content → 伪元素不显示 */
      content: ""; /* ✅ 即使为空也要写 */
    }
    
  2. 默认是行内元素

    • 想设宽高?加 display: blockinline-block

    • 想绝对定位?父元素必须 position: relative

  3. 不能被 JavaScript 直接操作

    // ❌ 无效!找不到 ::before
    document.querySelector('.item::before')
    

    ✅ 替代方案:通过切换 class 控制

    .active::before { background: red; }
    
  4. 伪元素属于“子内容”

    • ::before 插在 第一个子节点前

    • ::after 插在 最后一个子节点后

×

喜欢就点赞,疼爱就打赏