伪元素能帮助我们做些什么

伪元素已经不是什么新东东了,大家在实际生产中肯定有使用过伪元素。但伪元素能帮助我们做些什么呢?针对该问题,有很多同学能很好的回答,但也有很多场景并不是所有开发人员都完全了解的。今天再次花时间来整理一下伪元素能帮助我们做些什么?我想接下来的内容和实例肯定会有不少同学感兴趣的。如果你是其中的一位,那么请继续往下阅读。

伪元素是什么

元素事实上是HTML中的概念,常常把HTML中的标签称作为元素 。那么伪元素是什么呢?从其名称上来说, 即为假,在实际的DOM中是不存在的,而事实上呢?我们可以借助一些CSS的特性让其模拟成一个元素,对于这样的元素我们称之为伪元素

在W3C的标准规范中也有独立的规范文档,时到今日,伪元素的最新规范是 CSS Pseudo-Elements Module Level 4 。在该规范中有我们熟悉的伪元素,比如::first-line::first-letter::selection::placholder::after::before,也有我们不熟悉的::marker::inactive-selection::spelling-error::grammar-error

不过我们今天要聊的仅仅是其中的::before::after两个伪元素。这两个伪元素配合W3C的另外一个规范 CSS Generated Content Module Level 3 中的content可以创建出两个伪元素。这样一来,一个HTML元素就具备多个盒模型,即有多个背景和边框等,正如下图所示:

伪元素如何生成内容

刚才提到过,伪元素::before::after需要和CSS的content结合在一起才能有效的生成内容或盒子。比如:

HTML 复制代码
<!-- HTML -->
<div>我是一个div</div>
CSS 复制代码
div{
    &::before, 
    &::after {
        content: ''
    }
}

在浏览器中查看元素时,可以看到::before插入到div内容前面,::after插入到div内容后面,如下图所示:

再次强调,::before::after能生效是因为我们在伪元素中显式的声明了content,哪怕是个空字符串。虽content是一个空字符串值,但这个时候其实已经在页面中就生成了一个盒模型,而且也具备了"Computed"的样式,如下图所示:

这个时候适用于元素的CSS属性也就适用于伪元素上了。比如:

CSS 复制代码
div {
    display: flex;
    align-items: center;
    
    &::before,
    &::after {
        content:"";
        display: block;
        width: 32px;
        height: 32px;
        border-radius: 100%;
        background-color: #f36;
        margin: 0 5px;
    }
}

效果如下:

如果在::before::after中未显式设置content的话,那么就无法将伪元素插入到DOM结构中,如下所示:

上面我们看到的是给content一个空字符串,事实上除了空字符串还可以是任意你想要的字符串,比如文本,HTML实体符,Emoji等。比如:

除此之外,还可以配合content的其他特性来生成内容,比如attr()函数将HTML标签的属性值当作伪元素的内容,还可以通过url()函数将图片当作伪元素内容:

HTML 复制代码
<div data-content="Let's Go!">我是一个div</div>
CSS 复制代码
div::before {
    content: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/161359/quotes.png)
}

div::after {
    content:attr(data-content)
}

更为厉害的是,还可以将attr()url()与字符串内容结合起来使用:

CSS 复制代码
div::before {
    content:"dodododod" url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/161359/quotes.png)
}

div::after {
    content:attr(data-content) "➜";
}

还可以更复杂一些,将他们都结合在一起:

CSS 复制代码
div::before {
    content:"dodododod" url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/161359/quotes.png) attr(data-content)
}

div::after {
    content:attr(data-content) "➜" url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/161359/quotes.png);
}

content除了上面所提到的特性之外,还可以配合counterscounter-incrementcounter-reset实现自动计数器和列表编号等,但该效果暂时先不阐述,我们将放到后面来聊这个特性。

接下来我们来看看在实际上能帮助我们做些什么?

伪元素的常见用例

我们先来看看伪元素相关的常见用例。

清除浮动

CSS的float属性虽然其本质不是用来布局的,但很长一段时间中它都被用于Web布局中。熟悉float的同学都知道,浮动会带来一些其他的麻烦事情,比如说容器高度的坍塌。也正因为这些原因,在使用float时,最好记得清除浮动。其中清除浮动有一种经典的用法clearfix,它就是借助伪元素来完成:

CSS 复制代码
.clearfix:before,
.clearfix:after {
    content:"";
    display:table;
}
.clearfix:after {
    clear:both;
    overflow:hidden;
}

Icon图标

随着CSS的@font-face的出,伪元素常被用于一些Icon Font中,用来制作Icon图标。在业内很多Icon Font库都采用这种方式来实现的,比如著名的 Font Awesome

CSS 复制代码
.fa-flag:before {
    content: "\f024";
}

另外在一些纯CSS绘制的Icons上也有伪元素的身影,比如@wentinCSS Icons库中的很多标有借助了伪元素:

HTML 复制代码
<div class="audio-solid icon"></div>
CSS 复制代码
.audio-solid.icon {
    color: #000;
    position: absolute;
    margin-left: 5px;
    margin-top: 8px;
    width: 9px;
    height: 7px;
    border-left: solid 1px currentColor;
    border-right: solid 1px currentColor;
    border-bottom: solid 1px currentColor;
    border-radius: 0 0 50% 50%;
}

.audio-solid.icon:before {
    content: '';
    position: absolute;
    left: 1px;
    top: -6px;
    width: 5px;
    height: 10px;
    border: solid 1px currentColor;
    border-radius: 4px;
    background-color: currentColor;
}

.audio-solid.icon:after {
    content: '';
    position: absolute;
    left: 4px;
    bottom: -4px;
    width: 1px;
    height: 4px;
    background-color: currentColor;
}

伪元素和Sprites的结合

在Web开发中,时常会用到CSS Sprites或者是SVG Sprites来节约请求。那么在使用Sprites时为了更好的控制图标的容器,常常是通过增加一个额外的标签或者伪元素。比如:

Demo 地址:codepen.io/airen/full/...

上面的示例每个button的文本前面有一个Icon图标,示例中的每个图标的尺寸大致是32px x 32px,使用伪元素::before来起到一个空标签作用,从而更好的更控制图标容器大小,位置。

制作Ribbon和Bubbles

红丝带(Ribbon)和气泡(Bubbles)时常都是通过纯CSS绘制而成,比如气泡效果。早在2010年@necolas就用纯CSS实现了不同效果的气泡,而这些气泡效果都有使用到伪元素:

和气泡类似,结合伪元素可以实现不同效果的丝带,比如下面这个效果:

Demo 地址:codepen.io/nikhil/full...

如果你足够仔细的话,你会发现,这个和前面绘制图标的案例有点类似。的确是这样的,特别是在一个div绘制不同图形的案例,更显伪元素的强大:

是不是觉得不可思议,要是你也想尝试着使用一个div绘制出一个图形(你喜欢的图形),其思路和具体操作步骤可以参阅读@Lynn Fisher和Robert Nyman一起写的一篇教程《Single Div Drawings with CSS》。

而且在Codepen上你搜索"Single Div",你会发现有很多优秀的案例,足可以让你脑洞大开:

告诉你一个更有意思的网站,那就是**CSSBattle**,这是一个况技网站,用最少的代码实现同一个效果,谁的代码量少谁就获胜。这里面的效果基本上是基于一个div完成的。可以说是一个练习CSS绝佳场所。

CSS Divider(分隔线)

在实际生产中会碰到区块之间的分隔线,常常我们把其称为CSS Divider,如下图所示:

面对这样的效果,在内容前后插入伪元素是非常有效的,比如下面这样的一个示例:

CSS 复制代码
div {
    display: flex;
    justify-content: center;
    align-items: center;
    &::before,
    &::after {
        content: '';
        display: block;
        height: 0.09em;
        min-width: 30vw;
    }
    &::before {
        background: linear-gradient(to right, rgba(240,240,240,0), #fff);
        margin-right: 4vh;
    }
    &::after {
        background: linear-gradient(to left, rgba(240,240,240,0), #fff);
        margin-left: 4vh;
    } 
}

你看到的效果如下:

Demo 地址:codepen.io/airen/full/...

上面的效果是最简单的,你可以根据自己所需要的效果,让其更个性化:

Demo 地址:codepen.io/airen/full/...

@Samia Rai在《25 Creative CSS Divider Examples》一文中收集了共25种有关于CSS制作的分隔线案例,当然有些是没有使用到伪元素的。如果你感兴趣的话,你可针对同样的效果,做一些改造。

CSS Tooltips

提示框(Tooltips)对于大家而言应该不会感到陌生。在实际制作提示框效果时,使用纯CSS也可以很好的实现。特别是配合HTM标签自定义属性会显得更为有意思。在介绍伪元素如何生成内容的时候,我们提到过content配合HTML标签自定义属性的时候,可以让自定义属性的值很好的放到伪元素中。

也就是说,基于该特性,我们可以很好的实现提示框的效果。比如:

HTML 复制代码
<span class="tool" data-tip="By adding this class you can provide almost any element with a tool tip." tabindex="1">tool</span>
CSS 复制代码
.tool {
    cursor: help;
    position: relative;
}

.tool::before,
.tool::after {
    left: 50%;
    opacity: 0;
    position: absolute;
    z-index: -100;
}

.tool:hover::before,
.tool:focus::before,
.tool:hover::after,
.tool:focus::after {
    opacity: 1;
    transform: scale(1) translateY(0);
    z-index: 100; 
}

.tool::before {
    border-style: solid;
    border-width: 1em 0.75em 0 0.75em;
    border-color: #3E474F transparent transparent transparent;
    bottom: 100%;
    content: "";
    margin-left: -0.5em;
    transition: all .65s cubic-bezier(.84,-0.18,.31,1.26), opacity .65s .5s;
    transform:  scale(.6) translateY(-90%);
} 

.tool:hover::before,
.tool:focus::before {
    transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s;
}

.tool::after {
    background: #3E474F;
    border-radius: .25em;
    bottom: 178%;
    color: #EDEFF0;
    content: attr(data-tip);
    line-height: 1.2;
    margin-left: -8.75em;
    padding: 1em;
    transition: all .65s cubic-bezier(.84,-0.18,.31,1.26) .2s;
    transform:  scale(.6) translateY(50%);    width: 17.5em;
}

.tool:hover::after,
.tool:focus::after  {
    transition: all .65s cubic-bezier(.84,-0.18,.31,1.26);
}

效果如下:

Demo 地址:codepen.io/airen/full/...

自动生成计数器

如果给一个列表添加列表项目 ,如果是一个默认效果的话,可能会直接考虑ol这样的有序列表,但很多时候我们是需要一些个性化的列表项目符:

不考虑别的,原生CSS就具备这方面的特性。不知道大家是否还记得,我们在前面给大家预留了一个话题:伪元素结合content中的counters、counter-increment和counter-reset实现自动计数器和列表编号

其原理很简单:

配合不同的CSS我们可以实现很多个性化的效果:

这里要提一下我的偶像 @Ana Tudor,她给大家提供的案例更是令我们感到CSS强大的魅力。比如她的博文《Restricting a (pseudo) element to its parent's border-box》中的案例:

Demo 地址:codepen.io/thebabydino...

虽然它们的结合能让我实现很多个性化的列表项的效果,但这里我要告诉大家的是,在未来CSS的::marker能赋予我们更强的能力,如果对该特性感兴趣的话,可以阅读《聊聊CSS的::marker》一文。

CSS Loading Animation

CSS的伪元素事实上就是一种免费的DOM元素,DOM元素具备的很多特性它们也同样具备。CSS的样式也是如此。比如我们常常在加载页面时用到的Loading动效,就有很多是借助CSS的伪元素一起完成的,比如@Camden Foucht写的LoadLab就是一个很好的示例

Demo 地址:codepen.io/camdenfouch...

如果你对Web动效方面感兴趣的话,可以点击这里查看有关于这方面的教程

优化链接

在Web页面中,你的链接会有站内链接也会有站外链接,可以通过伪元素很好的告诉你的用户它们之间的区别。比如下面这个示例:

CSS 复制代码
a[href^="http"]:hover::after{
    content:"(" attr(href) "➜)";
    padding: 0 5px;
    margin-left: 5px;
    background: linear-gradient(to right,var(--mainColor) 0%,var(--mainColor) 5px,transparent);
}

效果如下:

Demo 地址:codepen.io/airen/full/...

类似的思路也可以用于其他的一些链接或导航菜单上(当然也可以用于你自己喜欢的任何地方):

Demo 地址:codepen.io/sincamons/f...

如果你的链接是链接了一些文件(提供给用户下载或在线阅读),那么可以配合CSS属性选择器,根据文件扩展名提供不同的Icon图标向用户示意:

Demo 地址:codepen.io/airen/full/...

Switch Toggle Button (切换按钮)

自定义的checkboxradio(也常称作Switch Toggle ),通过label标签和其伪元素,可以轻易实现个性化定制效果,比如下面这个示例:

Demo 地址:codepen.io/fabriceleje...

上面列举了十种我们使用伪元素常见的场景,当然还有很多场景没有可能被我遗漏了,如果你有这方面的经验和案例,欢迎在下面的评论是与我们一起分享。

伪元素的不常见用例

接下来,我们再来看一些伪元素不见常的用例和使用场景。

盒阴影

在CSS中虽然有box-shadowdrop-shadow()可以让我们给一个元素添加阴影效果。但有些场景,他们是心有余而力不足的。比如:

针对于这样的场景我们使用伪元素可以让事情变得简单地多:

Demo 地址:codepen.io/airen/full/...

上面这个模拟3D按钮的算是简单了。采用同样的技术,也可以很好的实现上图所示的侧边,中间等不位置的阴影效果:

Demo 地址:codepen.io/jcorpus/ful...

这样做是有一定原因的。将box-shadow转换为伪元素实现阴影效果,对于性能是较大帮助的。特别是有动效的地方,可以让阴影效果更为平滑:

如果你使用浏览器开发者工具查看的话,两个效果之间的渲染的差异有多大:

链接和图片的连动

这个效果的思路很奇特,在伪元素上使用图片,而且伪元素和链接能相互连动 。这个效果是来自于@Ahmad Shadeed的《Uncommon Use Cases For Pseudo Elements》一文。先上Demo吧:

Demo 地址:codepen.io/shadeed/ful...

代码并不复杂,主要是思路新奇:

CSS 复制代码
.link-1 {
    color: #854FBB;
}

@media (min-width: 700px) {
    .link-1:after {
        content: "";
        position: absolute;
        right: 0;
        top: 20px;
        width: 150px;
        height: 100px;
        background: currentColor;
        opacity: 0.85;
        transition: 0.3s ease-out;
    }

    .link-1:hover {
        text-decoration: underline;
    }

    .link-1:hover:after {
        transform: scale(1.2);
        opacity: 1;
    }
}

尝试着在上面的示例中,将鼠标悬浮到链接上或者右侧颜色的区域上,你都可以看到两者有连动效果:

想象一下,如果将示例中的颜色区域换成产品图片,是不是很有创意:

扩展可点击区域

可点击区域是不合理直接影响了用户和你的产品的交互,特别是在移动端。大家可能有碰到过,有些产品在按钮、链接、复选框或单选框等操作上就是失效,要点击很多次才能有效果。造成这种行为就是因为点击区域过小。

特别是在一些带可点击操作的图标上,Icon图标的实际尺寸并不适合一些系统的设计规范,在iOS上就提供Icon图标可点击区域应该是48px x 48px,如果你使用的图标小于该区域的话,我们就应该通过别的方式来进行扩展。那么伪元素是一个较好的方式。比如下面这个示例:

Demo 地址:codepen.io/shadeed/ful...

@Ahmad Shadeed 和 @hankchizljaw 有过一个共同的观点。比如在一个卡片上,可以让整个卡片都具有可点击效应(click事件绑定在button或一个<a>)元素之上。如下图所示:

也可以查看下面的Demo源码:

Demo 地址:codepen.io/airen/full/...

蒙层效果

大家是否还记得在《CSS 的 Clipping 和 Masking》一文中介绍Masking和Clipping技术时,先用常规则的CSS技术实现了缕空的效果:

Demo 地址:codepen.io/airen/full/...

正如你所看到的,不管是使用box-shadowborder还是radial-gradient()都没有离开伪元素的身影。在某些场景之下,如果要给元素上面添加一层,借用伪元素的确是一个较好的选择。特别是想对图片做一些特殊效果的时候:

Demo 地址:codepen.io/airen/full/...

除了这种简易效果之外,还可以实现交叉布局效果

Demo 地址: codepen.io/airen/full/...

以及一些淡入淡出的效果:

Demo 地址:codepen.io/airen/full/...

Slider 和 Output

@Ana Tudor在《Using Conic Gradients and CSS Variables to Create a Doughnut Chart Output for a Range Input》一文介绍了怎么使用input[type="range"]实现一个圆形进度条。这也是个很有意思的案例,而且涉及到知识点较多,效果中运用到伪元素只是其中小小的一部分,感兴趣的同学可以阅读教程和示例源码:

Demo 地址:codepen.io/airen/full/...

不一样的计数器

伪元素和content结合是可以做很多事情。正如前面所示,结合content中的counterscounter-incrementcounter-reset实现自动计数器和列表编号等。除此之外,配合CSS自定义属性,还可以实现另样的效果,比如下面这个递增(递减)的案例:

Demo 地址:codepen.io/airen/full/...

有关于伪元素不常见的用例就整到这个为止。@Ahmad Shadeed在他的博文《Uncommon Use Cases For Pseudo Elements》还提供了一些其他的案例,感兴趣的可以去看看。如果你有其新奇或有意思的案例,欢迎与我们一起共享。

小结

CSS伪元素并不是什么新知识点或者说新技能,在这篇文章中主要是搜集和整理了自己在以往项目中用到的案例(或将来可用)。说实话并没有太多的隐藏技能,只不过有些案例可以开拓我们的思路,打开我们的眼界。最后希望这篇文章对你有所帮助。


如果你觉得该教程对你有所帮助,请给我点个赞。要是你喜欢 CSS ,或者想进一步了解和掌握 CSS 相关的知识,请关注我的专栏,或者移步阅读下面这些系列教程:

相关推荐
我要洋人死13 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人25 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人26 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR31 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香33 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969336 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai41 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#