伪元素::before
和::after
是令人难以置信的通用的CSS工具。了解它们可以帮助您编写实用的 CSS 来解决各种情况。或使用它们来创造令人印象深刻的 CSS 效果。
本文从底层开始,介绍了这些伪元素以及如何使用它们。随着本文的进行,我们将深入探讨更有趣的主题。我们还将大量讨论content
的 CSS 属性,因为它至关重要。
什么是伪元素?
在 CSS 中,您可以将关键字::before
和::after
关键字添加到选择器中以创建伪元素。伪元素被插入到与选择器匹配的元素中,该元素位于其中的任何内容之前或之后。
举个例子可能最简单:
<p class="my-paragraph">
I'm a paragraph!
</p>
.my-paragraph {
padding: 20px;
border: 4px dashed lightgray;
}
.my-paragraph::before {
content: 'This is before!';
color: hotpink;
}
.my-paragraph::after {
content: 'This is after!';
color: steelblue;
}
使用以上 HTML 和 CSS 后,产生以下效果。
如果您最擅长使用开发工具,那么您会发现<p>
内部的::before
和::after
元素。您可以单击它们以查看或编辑其样式,就像其他任何元素一样。
我们只需要使用 CSS 来创建 HTML 中不存在的元素和内容,使其样式无限可能!
装饰线标题
::before
和::after
非常适合添加装饰,下面是带有装饰线条的标题。
HTML只有一个<h1>
标签。
两侧的双线都完全在 CSS 中处理。
h1 {
display: grid;
grid-template-columns: minmax(50px, 1fr) auto minmax(50px, 1fr);
align-items: center;
text-align: center;
gap: 40px;
}
h1::before, h1::after {
content: '';
border-top: 6px double;
}
这将在标题文本之前和之后插入伪元素。它们的content
设置为''
,因为我们实际上不需要任何内容,我们只希望在那里存在元素,因此我们可以对它们应用双边框来创建线条。
现在创建伪元素所需的content
属性,但是正如您在此处所看到的,可以将其设置为''
(空字符串)。
通过将<h1>
元素转换为 3 列来实现布局。左列和右列是双线,宽度均为minmax(50px, 1fr)
,表示它们的匹配宽度始终不小于 50px。标题文本整齐地居中。
装饰丝带标题
现在,让我们进一步介绍功能区标题。
这次,我们仍然只是需要 HTML 中的<h1>
单个标签。
从下面获取 CSS 效果
h1 {
position: relative;
margin: 0 auto 20px;
padding: 10px 40px;
text-align: center;
background-color: #875e46;
}
h1::before, h1::after {
content: '';
width: 80px;
height: 100%;
background-color: #724b34;
/* position ribbon ends behind and slightly lower */
position: absolute;
z-index: -1;
top: 20px;
/* clip ribbon end shape */
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 25% 50%);
/* draw and position the folded ribbon bit */
background-image: linear-gradient(45deg, transparent 50%, #5d3922 50%);
background-size: 20px 20px;
background-repeat: no-repeat;
background-position: bottom right;
}
h1::before {
left: -60px;
}
h1::after {
right: -60px;
transform: scaleX(-1); /* flip horizontally */
}
这次,我们使用绝对定位来放置伪元素。左侧的碳带末端用::before
创建,右侧的碳带用::after
创建。CSS 共享样式一起绘制两者,面向相同的方向,然后翻转h1::after
用transform: scaleX(-1)
。
碳带末端的形状通过clip-path
来实现。折叠的带状钻头是在底部内角上background-image
绘制linear-gradient()
并小心放置的小三角形。
引用块装饰
让我们回到设置content
上,::before::after
不是使用空字符串,因为您可以在其中做一些有趣的事情。
content
可以设置为特殊值open-quote
和close-quote
。这样做会插入适合当前语言的开/闭引号。样式<blockquote>
是一个很好的用例。
<blockquote lang="en">
Hello! I am a block quote in English. Check out how the quotation marks are automatically added around me!
</blockquote>
<blockquote lang="fr">
Salut ! Je suis une citation en français. Je ne connais pas le français, alors j’ai demandé à quelqu’un sur Twitter de le traduire !
</blockquote>
blockquote::before {
content: open-quote;
}
blockquote::after {
content: close-quote;
}
blockquote::before, blockquote::after {
opacity: 0.25;
padding: 0 10px;
font-size: 3em;
}
blockquote {
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px;
padding: 20px 10px;
border-radius: 10px;
background-color: #e5ddcb;
}
现在,我们有了自动时尚的<blockquote>
口号。
不同于<blockquote>
,<q>
标记(内联引号)默认情况下显示引号。这不是魔术,它是浏览器的默认用户代理样式表,它与我们刚才展示的一样!
q::before {
content: open-quote;
}
q::after {
content: close-quote;
}
关于另一个很酷的事情open-quote
和close-quote
,自带筑巢感知。例如,对于英语,如果您有嵌套<q>
标签,则外部标签将使用“ ”
,而内部标签将使用‘和’
。
CSS quotes
属性使您可以直接控制open-quote
和close-quote
的用途。
q {
quotes: '<' '>'; /* wrap all quotes with < and > */
}
q {
quotes:
'<' '>' /* wrap outermost quotes with < and > */
'<<' '>>' /* then quotes nested one level deep with << and >> */
'<<<' '>>>'; /* then quotes nested deeper with <<< and >>> */
}
让我们用实际演示来结束本节quotes
。
每个引用口号都有相同的标记。
<q>If you're fine with <q>unexplained phenomena</q> then go ahead.</q>
但是具有不同的quotes
值。
.customized {
quotes: '→' '←';
}
.customized-nested {
quotes: '⟪' '⟫' '⟨' '⟩';
}
.emoji {
quotes: '😀💬"' '"';
}
q::before, q::after {
color: #ff003c;
}
显示元素属性
使用::before
和::after
可以在伪元素中显示元素属性attr()
。例如,href
是<a>
下面元素的属性。
<a href="https://tacobell.com">Taco Bell</a>
我们可以href
以此显示链接中的值。
a::after {
content: ' → ' attr(href); /* show an arrow before the href */
}
下面是它的样子。
我在上面的 CSS 中添加了一些额外的内容。您可以将多个事物放在一起content
(通常用空格隔开)以将它们串联起来。在这种情况下,' → '
可与串联attr(href)
以实现您在演示中看到的内容。
显示计数
CSS 计数器使您可以对事物进行计数。可以使用::before
和::after
显示计数值。该演示显示选中了多少个复选框,没有涉及JavaScript
。
这是相关的 CSS。
input:checked {
counter-increment: total;
}
output::before {
content: counter(total);
}
每个选中的复选框都会增加total
计数器的数量,然后将其显示在<output>
元素的::before
伪元素中。
如果您想了解有关 CSS 计数器的更多信息,请参阅本文。
影像嵌入
您可以将图像放入::before
或::after
设置content
为url()
。
div::before {
content: url(image.png);
}
还记得content
以前的串联吗?它也适用于图像!这是连接图像,字符串和属性的示例。
<div data-lives="3"></div>
div::before {
content: url(mario.gif) ' × ' attr(data-lives);
}
并不是必须使用content
显示图像。另一种选择是background-image
与适当大小的伪元素一起使用。
div::before {
content: '';
background-image: url(image.png);
display: block;
width: 100px;
height: 100px;
}
值得一提的是,::before
和::afte
r默认display: inline
。如要设置width/ height
,则需要更改它,如上面的CSS所示。
内容替换
正如您现在所注意到的,::before
和::after
依赖content
。除非content
设置,否则它们甚至不会渲染。但是,依赖关系不是对等的,在某些情况下,content
可以不使用::before
或::after
。
如果content
仅将其设置为单个图像,则可以直接在元素上使用它来替换该元素的 HTML 内容。
.replace {
content: url(replace.png);
}
让我们在以下 HTML 上尝试一下。
<span class="replace">This is text!</span>
<span class="replace"><img src="chicken-nugget.png" alt="chicken nugget"></span>
<img class="replace" src="chicken-nugget.png" alt="chicken nugget">
HTML 正在测试 3 种情况:
- 具有简单文本的元素,它将被替换。
<img>
内部有元素,它也将被替换。
<img>
包含元素。Firefox 不会替代它,但是其他浏览器会替代它。
你自己看!替换了元素内的所有内容。最初默认鸡块(chicken-nugget.png)。
标记内容
::marker
是可以使用的另一个伪元素content
,类似::before
和::after
。但它专用设置列表项标记的样式。
<ul>
<li>Cupcakes are essentially cakes, but cuppier.</li>
<li>The first cupcake was invented by Harley McCupcakken in 1796.</li>
<li>Alright fine, I don't actually know anything about cupcakes.</li>
</ul>
li::marker {
content: '🧁';
}
不幸的是,Safari 不会让你设置content
的::marker
,并且浏览器有其他限制。
另一个选择是完全隐藏标记list-style-type: none
,然后使用::before
来做自己的事情。
设计图形
有时人们只想通过 CSS 发挥创造力。很有趣!一个特别受欢迎的游戏是查看单个元素可以做多少事情。::before
和::after
经常在这里发挥作用,因为在您的一次性设备上拥有这些额外的伪元素会带来很多可能性。
Lynn Fisher 的 A Single Div 画廊提供了一些令人印象深刻的例子。检查 CSS,您将看到很多::before
和::after
伪元素。
Dhanish Gajjar 的 Pure CSS Flags,Single Divs 是 113 个标志的集合,其中许多标志是用::before
和::after
伪元素绘制的。
然后是 Astrit Malsija 的 700 多个 CSS 图标。仔细研究一下图标,您就会明白。
这些事情并不总是可行的,但并不是毫无意义。通常可以获得一些创意乐趣以及在此过程中学习一些新技巧。
控制布局
回到实际问题,::after
可以与实用程序类一起使用以控制布局。一个著名的例子是“clearfix”,它确保元素环绕其中的所有浮动元素。这是一个示例实现,尽管有很多变体。
.clearfix::after {
content: '';
display: block;
clear: both;
}
多年来,CSS 缺一不可,但是现在有了一个更现代的解决方案。
.clearfix {
display: flow-root;
}
这是一个演示该问题和修复方法的演示。请注意,第一个鸡块延伸到其父元素之外,而第二个和第三个则没有。
该::after
伪元素还可以通过布局插入换行符协助。
.inline-element::after {
content: '\A';
white-space: pre;
}
<form>
<label for="email">Email address</label>
<input autocapitalize="none" type="email" id="email" placeholder="like test@test.com" />
</form>
有时您想在插入元素之后创建换行符,同时保留该插入元素的插入状态。幸运的是,它所需要的只是一点伪元素魔术。
会动画吗?
可以将 CSS 动画应用于::before
和::after
吗?是的!它们的工作就像处理普通元素一样容易。
话虽如此,为content
属性设置动画更加有趣。在 Safari 以外的浏览器中,它可以是独立动画的,这意味着content
可以在值之间切换而无需任何逐渐过渡。
div::before {
content: '';
animation: flip 6s linear infinite;
}
@keyframes flip {
from { content: 'Hello!'; }
to { content: 'Goodbye!'; }
}
如果您想为中的数字设置动画content
,可以执行一个技巧,但是它仅适用于 Chrome,Edge 和 Opera(这些浏览器当前支持 Properties and Values API,而该技巧依赖于该浏览器)。
这是它的工作方式:
- 声明一个为整数的自定义属性。
- 设置一个计数器,以通过获取该整数的值
counter-reset
。
- 在
content
伪元素的中显示该计数器。
- 对整数值进行动画处理。
@property --num {
syntax: '<integer>';
inherits: true;
initial-value: 0;
}
div::before {
counter-reset: my-counter var(--num);
content: counter(my-counter);
animation: count 10s ease-in-out infinite alternate;
}
@keyframes count {
to { --num: 100; }
}
如果您想更深入地研究该技术,请参阅本文,这将使您走得更远。
伪元素与伪类
除了::before::after
,还有其他伪元素用于对现有元素的各个部分进行样式设置。我们之前讨论了::marker
样式列表项标记。另一个示例是::selection
,它用于为选定的文本设置样式。
还有伪类,它们与伪元素不同。伪类基于状态应用样式。例如,我们:checked
在较早的演示中使用了仅在选中复选框时才应用样式。
伪元素以双冒号(::)开头,而伪类以单冒号(:)开头。W3C 规范的旧版本使用单冒号的伪元素,所以你可能偶尔会看到:before
和:after
。它们仍然可以工作,但是建议改用::
。
辅助功能
content
是屏幕阅读器如何传达信息的重要标记。根据 W3C 规范,content
是语音倾述。
“content”属性适用于语音,生成的内容必须呈现为语音输出。
好吧,听起来不错。但是,让我们仔细看看在各种情况下会发生什么。
如果您使用::before
或::after
出于装饰目的,并且将其content
设置为''
(空字符串),那么屏幕阅读器将不会宣布它们。
div::after {
/* screen reader says nothing */
content: '';
}
屏幕阅读器将阅读放入的文本content
。并非总是如此,但是现在……除非您仍在支持 IE11(对不起)。
已经有人分享了他们测试各种浏览器 + 屏幕阅读器组合的结果。比较老旧的浏览器版本看起来很糟糕,直到您意识到所有故障都来自 IE11 和 Firefox 29(2014年发布)。
div::after {
/* screen reader says "hello" */
content: 'hello';
}
请记住,表情符号和符号算作文本。屏幕阅读器会宣布它们,这可能很好,但是如果您将它们用作装饰,也可能会很尴尬。
div::after {
/* screen reader says "yellow five pointed star" */
content: '⭐';
}
div::after {
/* screen reader says "clockwise open circle arrow" */
content: '↻';
}
屏幕阅读器可以识别中的图像content
。在我的测试中,VoiceOver 将在 Firefox 和 Safari 中宣布“图像”,并在 Chrome 中宣布“未标记的图像”。
div::after {
/* screen reader says something like "image" or "unlabelled image" */
content: url(image.png);
}
如果图像不是装饰性的,则说明我们处境不好。我们有一个有意义的图像,仅作为“图像”传达。
如果图像是装饰性的,则使用屏幕阅读器的人可能不会丢失任何重要信息,但他们不知道!他们会听到“图像”,并想知道错过了什么。
好消息是,您可以为指定替代文本conten
t。这提供了一种方法来告知屏幕阅读器要宣布的内容,类似于将alt
属性设置为<img>
。
div::after {
/* screen reader says "chicken nugget" */
content: url(chicken-nugget.png) / 'chicken nugget';
}
div::after {
/* screen reader says nothing */
content: url(decorative-image.png) / '';
}
它也可以与文本一起使用,因此我们可以告诉屏幕阅读器如何声明装饰性表情符号和符号。
div::after {
/* screen reader says nothing */
content: '⭐' / '';
}
div::after {
/* screen reader says "refresh" */
content: '↻' / 'refresh';
}
坏消息是 Firefox 和 Safari 不支持的替代文本content
,实际上content
完全在这些浏览器中失效。您可以通过content
两次声明来解决此问题。
div::after {
content: '⭐'; /* Firefox and Safari will use this */
content: '⭐' / ''; /* other browsers will use this */
}
您还应该知道,输入中的文本content
是不可选择且不可搜索的,并且content
无法右键单击其中的图像以获取图像上下文菜单。对于琐碎或装饰性的东西来说没什么大不了的,但是在放入冗长的文本或重要图像之前,我会重新考虑content
。
结束语!
::before
和::after
伪元素和content
属性可以做很多事情。我认为您可以拥有最少的标记(通常只是一个HTML标记),然后让 CSS 随它一起运行并完成所有这些令人惊奇的事情,真是太好了。
希望我们在本文中介绍了足够的基础知识,使您对各种可能性(和局限性!)有所了解,并激发了一些创造性的兴趣。谢谢阅读!