先记住这个答案
::before 和 ::after 是伪元素,分别在元素内容的最前面和最后面插入一个虚拟的子节点。这个子节点的内容由 content 属性决定。如果不写 content,伪元素根本不会渲染——这是最常犯的错误。content 可以是字符串、attr() 读取属性值、url() 插入图片、CSS 计数器,甚至可以组合多个值。
- 没有
content属性就不渲染 content: ""常用于装饰性用途<img>和<input>上无效- 生成内容会影响元素尺寸
content 的各种值
content 最简单的用法是写一个字符串,比如在必填项标签后面加红色星号。但它还有很多其他能力。
/* 必填项标签后面加星号 */
.required::after {
content: " *";
color: red;
}
/* 读取 data 属性作为内容 */
a[href]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: gray;
}
/* 空字符串,用于纯装饰 */
.icon::before {
content: "";
display: inline-block;
width: 16px;
height: 16px;
background: url(icon.svg);
}content: "" 看起来什么都没插入,但伪元素确实渲染了。这时可以给它设置宽高和背景图,当作一个装饰性的图标用。这是用 ::before 做图标最常见的写法。
attr() 函数读取元素上的 HTML 属性。打印页面时经常用 a::after { content: " (" attr(href) ")"; } 把链接地址显示出来,因为纸上看不到超链接。
对元素尺寸的影响
::before 和 ::after 生成的内容是元素的子节点,会参与布局计算。如果 content 是一段文字,它会撑大容器;如果设置了固定宽高,它会占据空间。
比如经典的 clearfix 写法就是利用 ::after 插入一个空的块级元素来清除浮动。
/* 清除浮动 */
.clearfix::after {
content: "";
display: block;
clear: both;
}这里 content: "" 生成了一个空的内容节点,display: block 让它独占一行,clear: both 让它排在所有浮动元素下面。父容器的高度就被这个伪元素撑开了。
现在用 Flexbox 和 Grid 布局已经不太需要 clearfix 了,但面试经常考到它的原理。
用不了伪元素的情况
::before 和 ::after 不是对所有元素都有效。置换元素(replaced elements)不支持,因为它们的内容由外部资源决定,不是 CSS 能控制的。
<img>、<input>、<textarea>、<select>、<br>、<hr> 这些元素上写 ::before 或 ::after 不会有任何效果。想在输入框前面加图标,应该加在它的父容器上,而不是 <input> 本身。
另一个限制是生成的内容不在 DOM 里。document.querySelector('::before') 不会返回任何东西,用 JavaScript 也不能给伪元素添加事件监听。不过可以通过 getComputedStyle(element, '::before') 读取伪元素的样式。
容易答错的地方
- 忘记写 content 属性
- 写了
::before { color: red; }但没写content,伪元素不会渲染。即使只是想做装饰,也必须写content: ""。这是因为content的默认值是normal,对::before和::after来说等于none。 - 用伪元素放重要的文字信息
::before和::after的内容不在 DOM 里,屏幕阅读器对它们的处理不一致,有些会读有些不读。复制文字时伪元素内容通常也不会被复制。所以重要的信息应该放在 HTML 里,伪元素只用于装饰性内容。
面试官还会怎么问?
content 能不能用 url() 插入图片?
可以。content: url(icon.png) 会把图片作为置换内容插入。但这样插入的图片不能用 width 和 height 控制大小,它会按原始尺寸显示。想控制大小,更好的做法是用 content: "" 加 background-image,这样可以自由设置宽高。
CSS 计数器在 content 里怎么用?
content: counter(section) ". " 可以在内容前面插入自动编号。配合 counter-reset 和 counter-increment 使用,就能实现自动编号的列表或章节标题,不需要在 HTML 里手动写数字。
before 和 after 能不能同时有多个?
不能。每个元素只能有一个 ::before 和一个 ::after。但 content 属性可以拼接多个值,比如 content: "(" counter(n) ") " attr(title),一次性输出多段内容。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。