伪元素的各种骚操作

前言

很早之前阅读大佬优秀的CSS源码的时候,总会看到一些::before,::after这样的代码,但是自己日常中却很少用到,最近在实际工作中,发现这两种伪元素能够带来很大的便利,于是找了个时间来深入理解一下这两种伪元素。

定义

CSS中有一类元素称作“伪元素”,例如::first-letter,::first-line,::before,::after,::selection,但是常用像::before,::afte能给我们在书写样式的时候带来很多便利,那么为什么他们会被称为伪元素呢?因为这些伪元素并不会出现在dom中,也就是说它们只是在CSS渲染层面上的东西,相比于普通元素,伪元素是无法用js获取到的,它们不会改变文档内容,不可复制,只是用于在css渲染中向元素逻辑上的头部或尾部添加内容。今天我们开始探索其中两种伪元素::before,::after的用法,因为它们在一些场景能带来极大的便利。

例子

我们以两个简单的例子来说描述这俩个伪元素

给书名加个书名号

这是一个很实用的例子,假如后端返回的数据没有给你加上书名号,需要在前端来加,用js来操作显然对性能不友好,不过用伪元素就能很完美地解决这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
.direc::before{
content: "《"
}
.direc::after{
content: "》"
}
</style>

<body>
<p class="direc">西游记</p>
<p class="direc">基督山伯爵</p>
<p class="direc">红与黑</p>
<p class="direc">你不知道的JavaScript</p>
</body>

如图:

image

给手机号码加个电话符号

这个在实际当中也很常见,我们经常在一些网页地footer下面看到联系方式会带上这个符号:

1
2
3
4
5
6
7
8
9
10
<style>
.tel::before {
content: '\260E';
color: skyblue;
}
</style>

<body>
<span class="tel">13082801882</span>
</body>

如图:

image

探究伪元素的display属性

那么我们问题来了,我们上面用到的伪元素既然是元素,那么是属于什么元素呢?我们来看看chorme调试工具下它的display属性是什么:

image

我们看到,伪元素的默认display属性是inline,那么,我们可以自定义伪元素的display吗?我们看下面代码:

1
2
3
4
5
6
7
8
9
10
11
<style>
.tel::before {
content: '\260E';
color: skyblue;
margin: 30px;
display: block;
}
</style>
<body>
<span class="tel">13082801882</span>
</body>

我们将伪元素设置为block,并且设置他的margin(因为inline元素的margin-top与margin-bottom是不生效的),我们看看是什么效果:

image

如图所示,可以表明伪元素虽然不是dom中真正的元素,但是和普通元素有一样的CSS样式效果,那么我们就能用它来做很多事~

伪元素是否脱离文档流

那么问题来了,之前我们说过伪元素和普通呀孙元素有很大的区别,最明显的就是伪元素不会出现在DOM中,不会改变文档内容,不可复制,仅仅是在css渲染层加入,那么我们来探究它是否会脱离文档流,是否对其他元素的排布会有影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style>
.box1{
display: inline-block;
height: 100px;
width: 50px;
border: 1px solid red;
}
.box1::before {
content: '';
display: block;
height: 30px;
width: 100px;
background: pink;
}
.box2{
display: inline-block;
height: 100px;
width: 50px;
border: 1px solid green;
}
</style>
<body>
<div class="box1"></div>
<div class="box2"></div>
</body>

如图:

image

我们试图将伪元素来挡住box2的布局,但是效果确实box2的布局丝毫不影响,并且box2的z-index要大于伪元素,因此伪元素是脱离文档流的,并且z-index的值也非常低。

弄清楚上面几个属性后,我们开始真正地去探究伪元素能给我们带来什么~

content属性

这是一个非常非常容易被忽视的属性,很多小伙伴在使用伪元素的时候都容易犯错:“为啥我的伪元素没有显示出来?”,最后一查才发现,是自己的content属性没有设置,导致伪元素不显示,那么content元素究竟可以填哪些东西呢?

string

最简单的不过如此,上面例子1中给书名添加书名号的就是string类型

attr()

嗯哼,这是什么值,难不成还能设置元素的attr?我们来一串代码解释一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.test1::after {
content: attr(class)
}
.test2::after {
content: attr(href)
}
</style>

<body>
<span class="test1">this el's class is :</span>
<br>
<a class="test2" href="baidu.com">百度:</a>
</body>

如图:

image

非常好理解,就是在元素后面加上该元素的属性值。

url()

这个属性是用来索引媒体文件,比如一个放一个图片,经过测试,对mp4、pdf、mp3格式的文件并不能显示出来:

1
2
3
4
5
6
7
8
9
<style>
.test1::before {
content: url("https://sfault-avatar.b0.upaiyun.com/124/827/1248273836-57c7f5f15344a_big64");
}
</style>

<body>
<span class="test1">there is a pic before the el</span>
</body>

如图:image

counter()

CSS中有个计数器属性,就能能够像一个树状图一样列出1.1 1.2这样的目录结构,对于这个属性,后续我也会专门出一篇博文来总结它的使用方式。对于伪元素来说,能定制计数器的分隔符号,说的有点抽象,我们来个demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<style>
body{
counter-reset: section;
}
h1{
counter-reset: subsection;
}
h1:before{
counter-increment:section;
content:counter(section) "、";
}
h2:before{
counter-increment:subsection;
content: counter(section) "-" counter(subsection) "、";
}
</style>

<body>
<h1>CSS的世界</h1>
<h2>color</h2>
<h2>fonst-size</h2>
<h2>backgroung</h2>

<h1>HTML的世界</h1>
<h2>div</h2>
<h2>ul</h2>

<h1>Javascript的世界</h1>
<h2>原型</h2>
<h2>闭包</h2>
</body>

如图:

image

也就是说能够定制每个分隔的符号,计数器属性非常好用,就像之前之前给书名加上书名号的那个例子一样,以防后端不给你序列号~

常见用法

清除浮动

包括我,对于伪元素的认知在之前还是处在清除浮动这个作用上,anyway,这也是一个很不错的用法,尽管当初CSS设置伪元素并不是为了清除浮动,设置浮动也并不是用来css实现拼方块一样布局,但是CSS就是这么神奇,多个属性结合在一起,就能做出很多有趣的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<style>
.cf:before,
.cf:after {
content: " ";
display: table;
}

.cf:after {
clear: both;
}

.cf {
*zoom: 1;
}
.box {
border: 1px solid red;
}
.smbox {
float: left;
width: 30px;
height: 30px;
background: pink;
}
</style>

<body>
<div class="box cf">
<div class="smbox"></div>
</div>
</body>

如图:
image

实现各种有趣的图形

我们来实现一个六边形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<style>
.star{
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
border-top: 50px solid transparent;
position: relative;
}
.star::after {
content: '';
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 100px solid red;
border-bottom: 50px solid transparent;
position: absolute;
top: 30px;
left: -50px;
}
</style>

<body>
<div class="star"></div>
</body>

如图:

image

其实就是用伪元素制造一个三角形,然后与原元素合成为一个六角星。

实现一些有趣的小icon
利用beofre和after两种伪元素,合成一些简单的icon,这样我们就不需要再去获取图片的icon,一下的icon实现来自其他小伙伴的分享:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<style>
#wraper{padding:10px;width:380px;height:380px;border:3px solid #ccc;margin:auto;}
#power{width: 30px;height: 30px;margin:20px;border: 6px solid #EEB422;border-radius: 50%;position: relative;display: inline-block;}
#power:before{width:7px;height:22px;background:#EEB422;position: absolute;left:8px;top:-13px;content: "";border: 3px solid #fff;}
#play{width: 30px;height: 30px;margin:20px;border: 6px solid #EEB422;border-radius: 50%;position:relative;display: inline-block;background: #EEB422;}
#play:before{border:11px solid transparent;border-left:17px solid #fff;content: "";position: absolute;left:9px;top: 3px;}
#pause{width: 30px;height: 30px;margin:20px;border: 6px solid #EEB422;border-radius: 50%;position:relative;display: inline-block;background: #EEB422;}
#pause:before{height:20px;width:5px;border:0px solid transparent;border-left:8px solid #fff;border-right:8px solid #fff;content: "";position: absolute;left:4px;top: 5px;}
#stop{width: 30px;height: 30px;margin:20px;border: 6px solid #EEB422;border-radius: 50%;position:relative;display: inline-block;background: #EEB422;}
#stop:before{height:17px;width:17px;background:#fff;content: "";position: absolute;left:6px;top: 6px;}
#comment{width: 50px;height: 25px;margin:20px;border: 6px solid #EEB422;border-radius: 20%;position:relative;display: inline-block;background: #EEB422;}
#comment:before{border:10px solid transparent;border-top:10px solid #EEB422;content: "";position: absolute;left:28px;top: 28px;}
#comment:after{content: "....";position: absolute;color: #fff;font-size: 26px;top: -10px;left: 2px;}
#like{width: 50px;height: 30px;margin:20px;border-radius: 55%;transform:rotate(55deg);-webkit-transform:rotate(55deg);position:relative;display: inline-block;background: #EEB422;}
#like:before{width:50px;height:30px;border-radius: 55%;transform:rotate(-110deg);-webkit-transform:rotate(-110deg);background:#EEB422;content: "";position: absolute;left:8px;top: -12px;}
#search{width: 20px;height: 20px;border:4px solid #EEB422;border-radius:50%;margin:20px;position:relative;display: inline-block;top: -5px;left: -5px;}
#search:before{width:20px;height:5px;background:#EEB422;transform:rotate(45deg);-webkit-transform:rotate(45deg);content: "";position: absolute;left:15px;top: 22px;}
#home{width: 30px;height: 30px;background:#EEB422;margin:20px;position:relative;display: inline-block;top: 5px;}
#home:before{width:6px;height:12px;background:#fff;content: "";position: absolute;left:12px;top: 20px;}
#home:after{border:25px solid transparent;border-bottom:20px solid #EEB422;content: "";position: absolute;top: -38px;left:-10px;}
#photo{width:40px;height:30px;background:#EEB422;margin:20px;position:relative;display: inline-block;top: 5px;}
#photo:before{width:13px;height:13px;border:4px solid #fff;border-radius:50%;background:#EEB422;content: "";position: absolute;left:10px;top: 5px;}
#photo:after{width:15px;height:10px;background:#EEB422;content: "";position: absolute;top: -7px;left:13px;}
#photo{width:40px;height:30px;background:#EEB422;margin:20px;position:relative;display: inline-block;top: 5px;}
#email{width:50px;height:35px;background:#EEB422;margin:20px;position:relative;display: inline-block;top: 5px;}
#email:before{border:25px solid transparent;border-top:20px solid #fff;content: "";position: absolute;left:0px;top: 2px;}
#email:after{border:25px solid transparent;border-top:20px solid #EEB422;content: "";position: absolute;top:0px;}
#file{width:30px;height:45px;border:4px solid #EEB422;margin:20px;position:relative;display: inline-block;top: 5px;}
#file:before{border:10px solid #fff;border-right:10px solid #EEB422;border-bottom:10px solid #EEB422;content: "";position: absolute;left:-4px;top: -4px;}
#file:after{width:20px;height:5px;border-top:3px solid #EEB422;border-bottom:3px solid #EEB422;content: "";position: absolute;left: 5px;top: 25px;}
#history{width:35px;height:35px;border:4px solid #EEB422;border-radius: 50%;margin:20px;position:relative;display: inline-block;top: 5px;}
#history:before{width:14px;height:14px;border-bottom:4px solid #EEB422;border-left:4px solid #EEB422;content: "";position: absolute;left:14px;top: 3px;}
#video{width:50px;height:35px;background:#EEB422;border-radius: 20%;margin:20px;position:relative;display: inline-block;top: -5px;}
#video:before{width:6px;height:6px;border:11px solid transparent;border-right:11px solid #EEB422;content: "";position: absolute;left:35px;top: 4px;}
#video:after{width:10px;height:10px;border:6px solid transparent;border-top:6px solid #EEB422;border-left:6px solid #EEB422;transform:rotate(45deg);-webkit-transform:rotate(45deg);content: "";position: absolute;left:13px;top: 35px;}
#tags{width:50px;height:25px;background:#EEB422;border-radius: 35% 0% 0% 35%;transform:rotate(45deg);-webkit-transform:rotate(45deg);margin:20px;margin-left:35px;position:relative;display: inline-block;top: -5px;}
#tags:before{width:10px;height:10px;border-radius:50%;background:#fff;content: "";position: absolute;left:7px;top: 7px;}
#phone{width:50px;height:50px;border-left:6px solid #EEB422;border-radius:20%;transform:rotate(-30deg);-webkit-transform:rotate(-30deg);margin:20px;margin-right:0px;position:relative;display: inline-block;top: -5px;}
#phone:before{width:15px;height:15px;background:#EEB422;border-radius: 20%;content: "";position: absolute;left:-2px;top: 1px;}
#phone:after{width:15px;height:15px;background:#EEB422;border-radius: 20%;content: "";position: absolute;left:-3px;top: 34px;}
#profile{width: 40px;height:15px;background:#EEB422;border-radius: 45% 45% 0 0;margin:20px;position:relative;display: inline-block;top: 0px;}
#profile:before{width: 20px;height:22px;background:#EEB422;border-radius:40%;content: "";position: absolute;left: 10px;top: -22px;}
</style>
<body>
<div id="wraper">
<div id="power"></div>
<div id="play"></div>
<div id="pause"></div>
<div id="stop"></div>
<div id="comment"></div>
<div id="like"></div>
<div id="search"></div>
<div id="home"></div>
<div id="photo"></div>
<div id="email"></div>
<div id="file"></div>
<div id="history"></div>
<div id="video"></div>
<div id="tags"></div>
<div id="phone"></div>
<div id="profile"></div>
</div>
</body>

如图:

image

总结

以上就是::after与::before的一些属性说明以及常见用法,我们只要知道伪元素并不是dom中的真实元素,而是一种CSS修饰,伪元素的display可以随意变换,因此可以做出很多好玩的样式,对于一些修饰类的元素,我们可以直接使用伪元素来实现,这样dom结构也会很紧凑和干净~