MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

CSS 伪元素选择器::before与::after的实际案例

2022-07-167.3k 阅读

CSS 伪元素选择器::before 与::after 的基础概念

::before 伪元素

::before 伪元素在选定元素的内容之前插入一个虚拟的元素。这个虚拟元素实际上并不存在于 HTML 文档的 DOM 结构中,但是可以通过 CSS 对其进行样式设置。它常常用于为元素添加一些装饰性的内容,比如图标、符号等。

语法上,使用 ::before 时需要结合选择器来指定作用的元素。例如:

p::before {
  content: "★";
  color: red;
  margin-right: 5px;
}

在上述代码中,所有 p 标签内的内容前面都会插入一个红色的 “★” 符号,并且该符号右侧有 5 像素的空白间距。这里的 content 属性是 ::before 伪元素必不可少的,它用来定义插入的内容,如果省略 content::before 伪元素不会显示。

::after 伪元素

::after 伪元素与 ::before 伪元素类似,不过它是在选定元素的内容之后插入一个虚拟元素。同样,这个虚拟元素也不存在于 DOM 结构中,通过 CSS 来赋予样式。

例如:

a::after {
  content: " (链接)";
  color: gray;
  font-size: 12px;
}

上述代码会在所有 a 标签的内容之后添加 “ (链接)” 的文字,文字颜色为灰色,字号为 12 像素。

常见应用场景

装饰性元素添加

  1. 项目符号替换 在无序列表中,默认的项目符号可能不符合设计需求,我们可以利用 ::before 伪元素来替换。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    ul {
      list-style-type: none;
      padding: 0;
    }
    li::before {
      content: "✓";
      color: green;
      margin-right: 5px;
    }
  </style>
</head>
<body>
  <ul>
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
  </ul>
</body>
</html>

在这个例子中,我们首先将 ul 的默认列表样式 list-style-type 设置为 none,然后使用 li::before 伪元素在每个列表项前面添加一个绿色的 “✓” 符号作为项目符号。

  1. 图标添加 有时候需要在按钮、链接等元素旁边添加小图标。假设我们要在一个 “下载” 按钮旁边添加一个向下箭头图标,可以这样实现:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .download-button::before {
      content: "";
      display: inline-block;
      width: 0;
      height: 0;
      border-left: 5px solid transparent;
      border-right: 5px solid transparent;
      border-top: 5px solid black;
      margin-right: 5px;
    }
  </style>
</head>
<body>
  <button class="download-button">下载</button>
</body>
</html>

这里通过 ::before 伪元素创建了一个三角形作为向下箭头图标。通过 display: inline - block 将其设置为内联块元素,以便和按钮文字在同一行显示。widthheight 设置为 0,通过边框来绘制三角形。

清除浮动

在 CSS 布局中,浮动元素可能会导致父元素高度塌陷的问题。::after 伪元素常被用于清除浮动。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .parent {
      border: 1px solid black;
    }
    .child {
      float: left;
      width: 100px;
      height: 100px;
      background-color: lightblue;
    }
    .parent::after {
      content: "";
      display: block;
      clear: both;
    }
  </style>
</head>
<body>
  <div class="parent">
    <div class="child"></div>
    <div class="child"></div>
  </div>
</body>
</html>

在上述代码中,.child 元素设置了向左浮动。如果不处理,.parent 元素会因为子元素的浮动而高度塌陷。通过 ::after 伪元素,我们在 .parent 元素内部的最后添加一个虚拟元素,将其 display 设置为 block 并使用 clear: both,这样就可以清除浮动,使 .parent 元素能够正确包裹住浮动的子元素。

创建分隔线

  1. 水平分隔线 可以利用 ::before::after 伪元素在两个元素之间创建水平分隔线。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .section::after {
      content: "";
      display: block;
      height: 1px;
      background-color: gray;
      margin: 10px 0;
    }
  </style>
</head>
<body>
  <div class="section">
    <p>这是第一段内容。</p>
  </div>
  <div class="section">
    <p>这是第二段内容。</p>
  </div>
</body>
</html>

在这个例子中,.section::after 伪元素创建了一个高度为 1 像素、背景色为灰色的水平分隔线,并且在其上下各有 10 像素的外边距。

  1. 垂直分隔线 同样,也可以创建垂直分隔线。假设我们有两个并排的导航项,要在它们之间添加垂直分隔线:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .nav-item + .nav-item::before {
      content: "";
      display: inline-block;
      height: 100%;
      width: 1px;
      background-color: gray;
      margin: 0 10px;
    }
  </style>
</head>
<body>
  <a href="#" class="nav-item">首页</a>
  <a href="#" class="nav-item">关于我们</a>
  <a href="#" class="nav-item">联系我们</a>
</body>
</html>

这里使用了相邻兄弟选择器 +,使每个 .nav-item 元素(除了第一个)的前面添加一个垂直的灰色分隔线,分隔线高度为 100%(即与导航项高度相同),宽度为 1 像素,左右各有 10 像素的外边距。

实现特殊形状和效果

制作三角形

  1. 向上三角形
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .triangle-up::before {
      content: "";
      display: block;
      width: 0;
      height: 0;
      border-left: 50px solid transparent;
      border-right: 50px solid transparent;
      border-bottom: 100px solid blue;
    }
  </style>
</head>
<body>
  <div class="triangle-up"></div>
</body>
</html>

通过设置 ::before 伪元素的边框属性,我们创建了一个向上的三角形。将 widthheight 设置为 0,通过不同方向的边框宽度和颜色来绘制三角形。这里左右边框设置为透明,底部边框设置为蓝色,形成向上的三角形形状。

  1. 向下三角形
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .triangle-down::before {
      content: "";
      display: block;
      width: 0;
      height: 0;
      border-left: 50px solid transparent;
      border-right: 50px solid transparent;
      border-top: 100px solid green;
    }
  </style>
</head>
<body>
  <div class="triangle-down"></div>
</body>
</html>

与向上三角形类似,只是将顶部边框设置为绿色,左右边框保持透明,从而形成向下的三角形。

制作箭头按钮效果

  1. 向左箭头按钮
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .arrow-left-button::before {
      content: "";
      display: inline-block;
      width: 0;
      height: 0;
      border-top: 10px solid transparent;
      border-bottom: 10px solid transparent;
      border-right: 10px solid black;
      margin-right: 5px;
    }
  </style>
</head>
<body>
  <button class="arrow-left-button">返回</button>
</body>
</html>

这里通过 ::before 伪元素创建了一个向左的箭头,添加在按钮文字的左侧,与按钮文字组合形成向左箭头按钮的效果。

  1. 向右箭头按钮
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .arrow-right-button::after {
      content: "";
      display: inline-block;
      width: 0;
      height: 0;
      border-top: 10px solid transparent;
      border-bottom: 10px solid transparent;
      border-left: 10px solid black;
      margin-left: 5px;
    }
  </style>
</head>
<body>
  <button class="arrow-right-button">前进</button>
</body>
</html>

通过 ::after 伪元素在按钮文字右侧添加一个向右的箭头,实现向右箭头按钮的效果。

实现提示框效果

  1. 简单提示框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .tooltip {
      position: relative;
      display: inline-block;
    }
    .tooltip::after {
      content: attr(data-tooltip);
      position: absolute;
      bottom: 125%;
      left: 50%;
      transform: translateX(-50%);
      background-color: black;
      color: white;
      padding: 5px 10px;
      border-radius: 3px;
      font-size: 12px;
      opacity: 0;
      transition: opacity 0.3s ease;
    }
    .tooltip:hover::after {
      opacity: 1;
    }
  </style>
</head>
<body>
  <span class="tooltip" data-tooltip="这是一个提示信息">悬停我</span>
</body>
</html>

在这个例子中,我们使用 ::after 伪元素来创建一个提示框。attr(data - tooltip) 用于获取 data - tooltip 属性的值并显示为提示框内容。通过 position: absolute 将提示框定位在触发元素的上方,left: 50%transform: translateX(-50%) 使其水平居中。初始时提示框的 opacity 为 0,即不可见,当鼠标悬停在 .tooltip 元素上时,opacity 变为 1,提示框显示出来。

  1. 带箭头的提示框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .tooltip-with-arrow {
      position: relative;
      display: inline-block;
    }
    .tooltip-with-arrow::after {
      content: attr(data-tooltip);
      position: absolute;
      bottom: 125%;
      left: 50%;
      transform: translateX(-50%);
      background-color: black;
      color: white;
      padding: 5px 10px;
      border-radius: 3px;
      font-size: 12px;
      opacity: 0;
      transition: opacity 0.3s ease;
    }
    .tooltip-with-arrow::before {
      content: "";
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translateX(-50%);
      width: 0;
      height: 0;
      border-left: 10px solid transparent;
      border-right: 10px solid transparent;
      border-top: 10px solid black;
      opacity: 0;
      transition: opacity 0.3s ease;
    }
    .tooltip-with-arrow:hover::after,
    .tooltip-with-arrow:hover::before {
      opacity: 1;
    }
  </style>
</head>
<body>
  <span class="tooltip-with-arrow" data-tooltip="这是一个带箭头的提示信息">悬停我</span>
</body>
</html>

在简单提示框的基础上,我们使用 ::before 伪元素添加了一个向下的箭头。同样,箭头初始时 opacity 为 0,当鼠标悬停时,箭头和提示框同时显示。

结合其他 CSS 特性使用

与 CSS 动画结合

  1. 动画装饰元素
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .animated-icon::before {
      content: "★";
      color: red;
      margin-right: 5px;
      animation: star - blink 2s infinite alternate;
    }
    @keyframes star - blink {
      from {
        opacity: 1;
      }
      to {
        opacity: 0.3;
      }
    }
  </style>
</head>
<body>
  <p class="animated-icon">闪烁的星星装饰文本</p>
</body>
</html>

这里我们通过 ::before 伪元素在段落前面添加了一个红色的星星,然后使用 CSS 动画 star - blink 让星星在不透明度 1 到 0.3 之间交替变化,实现闪烁效果,动画时长为 2 秒且无限循环。

  1. 动画提示框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .animated-tooltip {
      position: relative;
      display: inline-block;
    }
    .animated-tooltip::after {
      content: attr(data - tooltip);
      position: absolute;
      bottom: 125%;
      left: 50%;
      transform: translateX(-50%);
      background-color: black;
      color: white;
      padding: 5px 10px;
      border-radius: 3px;
      font-size: 12px;
      opacity: 0;
      animation: fade - in - out 3s ease - in - out infinite;
    }
    @keyframes fade - in - out {
      0% {
        opacity: 0;
      }
      50% {
        opacity: 1;
      }
      100% {
        opacity: 0;
      }
    }
  </style>
</head>
<body>
  <span class="animated-tooltip" data - tooltip="闪烁的提示信息">悬停我</span>
</body>
</html>

这个例子中,提示框使用 ::after 伪元素创建,通过 fade - in - out 动画让提示框在 3 秒内以缓入缓出的方式在不透明度 0 和 1 之间循环变化,实现闪烁的提示框效果。

与媒体查询结合

  1. 响应式装饰元素
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .responsive - icon::before {
      content: "●";
      color: blue;
      margin-right: 5px;
    }
    @media (max - width: 600px) {
      .responsive - icon::before {
        content: "★";
        color: red;
      }
    }
  </style>
</head>
<body>
  <p class="responsive - icon">响应式装饰文本</p>
</body>
</html>

在这个例子中,在屏幕宽度大于 600 像素时,::before 伪元素在段落前添加一个蓝色的实心圆 “●”。当屏幕宽度小于等于 600 像素时,通过媒体查询,::before 伪元素的内容变为红色的星星 “★”,实现响应式的装饰元素效果。

  1. 响应式提示框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .responsive - tooltip {
      position: relative;
      display: inline-block;
    }
    .responsive - tooltip::after {
      content: attr(data - tooltip);
      position: absolute;
      bottom: 125%;
      left: 50%;
      transform: translateX(-50%);
      background-color: black;
      color: white;
      padding: 5px 10px;
      border-radius: 3px;
      font-size: 12px;
    }
    @media (max - width: 480px) {
      .responsive - tooltip::after {
        bottom: 100%;
        left: 0;
        transform: translateX(0);
        background-color: gray;
        color: black;
      }
    }
  </style>
</head>
<body>
  <span class="responsive - tooltip" data - tooltip="响应式提示信息">悬停我</span>
</body>
</html>

在屏幕宽度大于 480 像素时,提示框在触发元素上方居中显示,背景色为黑色,文字颜色为白色。当屏幕宽度小于等于 480 像素时,通过媒体查询,提示框位置变为在触发元素下方左侧显示,背景色变为灰色,文字颜色变为黑色,实现响应式的提示框效果。

注意事项

兼容性问题

虽然 ::before::after 伪元素在现代浏览器中得到了广泛支持,但在一些较老的浏览器(如 Internet Explorer 8 及以下)可能不支持。在需要兼容旧浏览器的项目中,可能需要考虑使用其他替代方案,比如使用额外的 HTML 元素来模拟相同的效果。

性能影响

尽管 ::before::after 伪元素很强大,但过度使用可能会对性能产生一定影响。尤其是在创建复杂的形状和大量使用动画时,可能会导致浏览器渲染性能下降。因此,在使用时需要权衡,尽量保持样式的简洁性,避免不必要的复杂效果。

与 JavaScript 的交互

由于 ::before::after 伪元素并不存在于 DOM 结构中,通过 JavaScript 直接操作它们相对困难。如果需要在 JavaScript 中动态改变伪元素的样式,通常需要通过修改父元素的类名,从而间接影响伪元素的样式。例如:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    .button::before {
      content: "点击前";
      color: gray;
    }
    .clicked::before {
      content: "已点击";
      color: green;
    }
  </style>
</head>
<body>
  <button class="button" onclick="this.classList.toggle('clicked')">点击我</button>
</body>
</html>

在这个例子中,通过点击按钮,使用 JavaScript 的 classList.toggle 方法切换按钮的类名,从而改变 ::before 伪元素的内容和颜色。

嵌套与层级关系

当元素存在嵌套关系时,要注意 ::before::after 伪元素的作用范围。例如,在一个嵌套的列表中:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <style>
    ul li::before {
      content: "→";
      color: blue;
      margin-right: 5px;
    }
  </style>
</head>
<body>
  <ul>
    <li>一级列表项
      <ul>
        <li>二级列表项</li>
      </ul>
    </li>
  </ul>
</body>
</html>

上述代码中,ul li::before 选择器会作用于所有层级的列表项,给它们都添加一个蓝色的 “→” 符号。如果只想给一级列表项添加,可以使用更具体的选择器,如 ul > li::before,这里的 > 表示直接子元素关系,这样就只会给一级列表项添加符号。

打印样式

在设置打印样式时,需要注意 ::before::after 伪元素的显示情况。有些浏览器在打印时可能不会显示伪元素,或者显示效果与屏幕显示不同。可以通过在打印样式表中重新定义伪元素的样式来确保打印效果符合预期。例如:

@media print {
  p::before {
    content: "";
    display: none;
  }
}

上述代码在打印时会隐藏所有 p 元素前的 ::before 伪元素内容。如果希望在打印时显示特定的内容,可以重新设置 content 等属性。

通过深入了解 ::before::after 伪元素的特性、应用场景、结合其他 CSS 特性的使用方法以及注意事项,开发者能够更加灵活和高效地利用它们来创建出丰富多样且美观的前端页面效果。无论是简单的装饰,还是复杂的交互和布局,这两个伪元素都为前端开发提供了强大的工具。在实际项目中,要根据具体需求合理运用,充分发挥它们的优势,同时注意避免可能出现的问题,以提升用户体验和页面性能。