只用一个 div 元素,我们已经通过四个篇章写了很多形状。
首先,我们通过对这个 div 的宽度与高度的直接控制,轻松写出矩形和正方形,并结合 transform 的 skew 方法写出了平行四边形与菱形。
其次,我们通过对边框的灵活运用,与宽度和高度相结合,成功写出了各种各样的三角形和梯形。
再次,我们结合圆角边框的灵活使用,写出了圆形与椭圆形等由弧形组成的形状,也可以和直线配合写出扇形或者吃豆人等个性形状。
最后,我们通过对阴影属性的灵活控制,我们可以使用类似于控制每一个像素点的方式写出更加复杂的形状。
同时,阴影属性的引入,我们在理论上已经可以写出一切形状,甚至只用一个 div 元素就可以通过控制每一个像素点画出一张复杂图片。
然而,我们在上一个篇章,一共使用了 30 个阴影才写出了一个相对简单的太空入侵者的图案。并且每一个阴影都是和元素本身的形状是一样的,做不到每一个元素在形状方面个性化。
那么今天,我们就使用伪元素,用更加灵活轻松的方式来写出各种各样的形状吧!
一、伪元素
伪元素是一个附加至选择器末的关键词,允许我们对被选择元素的特定部分修改样式。
截止到今天,一共有 15 个伪元素:
::before:创建一个伪元素,作为所选元素的第一个子元素。
::after:创建一个伪元素,作为所选元素的最后一个子元素。
::first-letter:将样式应用于区块容器第一行的第一个字母,但仅当其前面没有其他内容(例如图像或行内表格)时才有效。
::first-line:在某块级元素的第一行应用样式。
::placeholder:表示 <input> 或 <textarea> 元素中的占位文本。
::selection:应用于文档中被用户高亮的部分(比如使用鼠标或其他选择设备选中的部分)。
::cue:匹配所选元素中的 WebVTT 提示。
::file-selector-button:代表 type="file" 的 <input> 的按钮。
::marker:匹配列表的标记框(通常为一个符号或数字)。
::part():表示在阴影树中任何匹配 part 属性的元素。
::slotted():用于选定那些被放在 HTML 模板中的元素。
::backdrop:在任何处于全屏模式的元素下的即刻渲染的盒子。
::grammar-error:应用于浏览器标识为语法错误的文本段。
::spelling-error:表示浏览器标记为不正确拼写的文本段。
::target-text:代表了浏览器在支持文本片段技术时所滚动到的文字。
说明:
::backdrop、::grammar-error、::spelling-error、::target-text 四个为实验性技术,在 W3C 确认定稿之前,我们都继续做观望,而不做过多展开;
::cue、::part()、::slotted() 三个伪元素比较冷门,实际开发中几乎用不上,这里也不做展开了;
::file-selector-button 和 ::marker 两个伪元素在实际开发中用的比较少,就只做简单介绍了。
1. before 和 after
::before 和 ::after 两个伪元素,分别作为所选元素的第一个子元素和最后一个子元素,通常用于为具有 content 属性的元素添加修饰内容。默认情况下,它是行向布局的。
div::before {
content: '';
}
div::after {
content: '';
}
可以看到,伪元素的使用方法是附加在选择器的末端,这部分的样式仅仅控制被该伪元素指定的部分内容的样式。
代码中,我们写了 ::before 和 ::after 两个伪元素,这两个伪元素需要在 CSS 中使用 content 属性激活。
通过浏览器的控制台查看渲染后的 DOM 结构,看到咱们的 div 元素中多了 ::before 和 ::after 两个伪元素。
为了看的足够清晰,我们给这个 div 元素添加一些文字信息:
<div>这里是 div 标签中的内容</div>
再次检查浏览器的控制台可以看到渲染后的 DOM 结构如下:
接下来,我们再在两个伪元素的 content 属性中都添加一些文字信息:
div::before {
content: '这里是 before 中的内容';
}
div::after {
content: '这里是 after 中的内容';
}
现在我们再次检查浏览器的控制台,我们惊讶的看到渲染后的 DOM 结构依然如下:
不同的是,在浏览器主窗口中呈现出来的显示效果是按照顺序显示出来的:
当我们在浏览器的控制台中选中其中一个伪元素,在浏览器主窗口中也会标识出对应的元素信息,同时我们可以看到该伪元素的 CSS 控制信息:
现在我们尝试把这两个伪元素都控制一下样式:
div::before {
display: block;
color: red;
content: '这里是 before 中的内容';
}
div::after {
display: block;
color: blue;
content: '这里是 after 中的内容';
}
我们大胆的控制这两个伪元素,将其设置为块级元素(最新叫法为块盒),并且分别设置文字颜色为红色与蓝色。得到效果如下:
还是一样的,我们在浏览器的控制台中选中其中一个伪元素,然后观察在浏览器主窗口中标识出的对应元素信息,以及该伪元素的 CSS 控制信息:
很明显,伪元素完全受到 css 控制,彷佛就是一个直接写在 html 中的元素。
这里,我们可以得到一如下结论:
::before 伪元素永远是元素中的第一个子元素,::after 伪元素永远是元素中的最后一个子元素,两个伪元素均需要 content 属性将其激活,伪元素的内容放在 content 属性中;
::before 和 ::after 两个伪元素默认为行内元素(最新叫法为行盒),可以通过 CSS 进行任意控制,就相当于写在元素中的两个 HTML 子标签。
::before 和 ::after 两个伪元素是我们画各种形状的篇章中,使用最多的两个伪元素,也是实际开发中使用最多的伪元素。在后面画形状的文中,我们还会不断使用。
这里,我们先对别的伪元素做一个简单的介绍。
2. first-letter
::first-letter 伪元素将样式应用于区块容器第一行的第一个字母,但仅当其前面没有其他内容(例如图像或行内表格)时才有效。
咱们在 div 元素中放入一段文字:
<div>将梦想悬挂于枝头,在夏季的丰盛中饱满,绽放;为梦想披星戴月,刷新生命的温度。那些因梦想蜕变了的灵魂,历经时光坎坷,痛苦挣扎,依然在枝头放歌,温暖了生命如花的影子。前世,储蓄梦想;今生,演绎铿锵。</div>
为了显示效果,控制 div 的宽度为 500px,并给一个淡蓝色的背景。
width: 500px;
background: lightblue;
现在显示一段正常的文字:
现在我们给这个 div 添加上 ::first-letter 伪元素:
div::first-letter {
font-size: 72px;
color: red;
}
这里,我们设置 ::first-letter 伪元素的样式为字体大小为 72px,文字颜色为红色。得到如下效果:
明显的看到,这段文字的第一个字,其显示样式发生了变化。并且,在浏览器的控制台中是无法找到的,而且伪元素的样式控制中,也不需要 content 属性来进行激活。
3. first-line
::first-line 伪元素将样式应用于某块级元素的第一行应用样式。
和 ::first-letter 伪元素中的样例一样,我们仅仅换成 ::first-line 伪元素:
div::first-line {
font-size: 72px;
color: red;
}
得到的效果如下:
明显的看到,这段文字的第一行,其显示样式发生了变化。
4. selection
::selection 伪元素应用于文档中被用户高亮的部分(比如使用鼠标或其他选择设备选中的部分)。
和 ::first-letter 伪元素中的样例一样,我们仅仅换成 ::selection 伪元素:
div::selection {
font-size: 72px;
color: red;
}
得到的效果如下:
我们可以看到,选中的文字呈现了红色,但是文字大小不变。这是因为,在 ::selection 伪元素中只能使用 color、background-color、cursor、caret-color、outline、text-decoration、text-emphasis-color、text-shadow 这八个控制文字的 CSS 属性,其它 CSS 属性会被自动忽略。
注意:background-image 会如同其他属性一样被忽略。
5. placeholder
::placeholder 伪元素表示 <input> 或 <textarea> 元素中的占位文本。
我们在 HTML 中写入一个 input 元素:
<input type="text" placeholder="这里是我们的占位符!" />
我们通过 placeholder 属性设置了占位符,默认显示效果如下:
然后,我们使用 ::placeholder 伪元素改变占位符中的文字颜色为红色:
input::placeholder {
color: red;
}
这样就可以控制占位符的文字样式了。
6. file-selector-button
::file-selector-button 伪元素代表 type="file" 的 <input> 的按钮。
我们在 HTML 中写入一个 type="file" 的 input 元素:
<input type="file" />
我们给这个 input 设置文字颜色为蓝色:
input {
color: blue;
}
显示效果如下:
可以看到,我们控制的文字颜色是文件名部分的文字颜色,按钮中的文字颜色还是默认的黑色。
然后,我们使用 ::file-selector-button 伪元素改变按钮中的文字颜色为红色:
input::file-selector-button {
color: red;
}
这样就可以控制按钮的文字样式了。
7. marker
::marker 伪元素匹配列表的标记框(通常为一个符号或数字)。
我们在 HTML 中写入一个无序列表:
<ul>
<li>造纸术</li>
<li>印刷术</li>
<li>指南针</li>
<li>火药</li>
<li>杂交水稻</li>
</ul>
默认效果如下:
可以看到,无序列表中每一项使用的默认标记框是一个黑点,现在我们想要让其变成自定义的标记框,直接使用 ::marker 伪元素:
li::marker {
content: '→';
}
在 ::marker 伪元素中,我们也是使用 content 属性对自定义标记框进行声明,效果如下:
无论是无序列表还是有序列表,列表项使用的都是 li 标签。熟悉推广的宝子们都知道,li 标签对 SEO 非常不友好。所以我们写列表的时候,更多使用的是 dl 列表。
于是,我们把 HTML 中的列表换成 dl 列表:
<dl>
<dt>我国五大发明</dt>
<dd>造纸术</dd>
<dd>印刷术</dd>
<dd>指南针</dd>
<dd>火药</dd>
<dd>杂交水稻</dd>
<dt>我国五大淡水湖</dt>
<dd>鄱阳湖</dd>
<dd>洞庭湖</dd>
<dd>太湖</dd>
<dd>洪泽湖</dd>
<dd>巢湖</dd>
<dt>五岳</dt>
<dd>泰山</dd>
<dd>衡山</dd>
<dd>华山</dd>
<dd>恒山</dd>
<dd>嵩山</dd>
</dl>
dl 列表中,可以使用 dt 标签定义列表标题,然后使用 dd 标签定义列表项,效果如下:
然后,我们直接对 dd 标签使用 ::marker 伪元素:
dd::marker {
content: '→';
}
结果我们惊讶的发现,没有起效果:
这是因为,::marker 伪元素作用在任何设置了 display: list-item 的元素或伪元素上,例如 li 和 summary 元素。而 dd 元素默认情况下是没有设置 display: list-item 属性的,于是我们手动给 dd 元素设置 display: list-item 属性:
dd {
display: list-item;
}
这样一来,我们的 ::marker 伪元素就生效了:
最后要说的是,::marker 伪元素不一定非要使用 content 属性,对于有序列表和无序列表来说,由于默认自带列表的标记框,于是我们可以直接控制其显示样式:
li::marker {
font-size: 72px;
color: red;
}
如此这般,我们直接控制标记框的样式,效果如下:
在将 ::marker 作为选择器的规则中,只能使用某些 CSS 属性:
所有的字体属性
white-space 属性;
color 属性;
text-combine-upright、unicode-bidi 和 direction 属性;
content 属性;
所有的 animation 和 transition 属性。
说明:规范指出,将来可能会支持其他 CSS 属性,让我们拭目以待吧!
二、正五边形
还记得我在欧洲杯的时候写过一篇用纯前端写一个足球的文章。
文章中,我们已经明确分析过,要写一个正五边形,只需要写一个等腰梯形和一个等腰三角形即可:
通过计算,得到如下数值:
先写一个上底长下底短的等腰梯形:
width: 200px;
height: 0;
border-top: 190.35px solid red;
border-right: 61.8px solid transparent;
border-left: 61.8px solid transparent;
然后使用 ::before 伪元素写一个等腰三角形,并通过定位控制到对应的位置:
div::before {
position: absolute;
top: -285px;
left: -61.8px;
display: block;
width: 0;
height: 0;
border-right: 161.8px solid transparent;
border-bottom: 95.1px solid red;
border-left: 161.8px solid transparent;
content: '';
}
于是,一个正五边形就写出来了:
之所以在这里重申了正五边形的写法,主要目的是要阐述几个观点:
在伪元素中,::before 和 ::after 两个伪元素,相当于两个子标签,使用 content 属性激活之后,可以完全当作两个 span 标签使用;
结合我们前面的四个篇章,相当于是拥有了三个标签,每一个标签都可以写出前面四个篇章中的任何一个形状,三个形状相结合之后,又可以得到更多的形状;
::before 和 ::after 两个伪元素是原元素的子标签,所以只需要通过对个元素设置一个非 static 的定位,就可以直接对这两个伪元素进行非常容易的定位。
关于定位的知识,我曾经在用纯前端写一个足球中做过讲解。
position 属性是控制元素定位的,默认值为 static。
若一个元素的 position 属性值为绝对定位(absolute),即可通过 top、bottom 两个属性控制该元素的竖直偏移,也可以通过 right、left 两个属性控制该元素的水平偏移。
若没有设置 top、bottom 两个属性,则竖直方向的偏移和 position 的值为 static 显示的一样;若没有设置 right、left 两个属性,则水平方向的偏移和 position 的值为 static 显示的一样。
无论水平方向还是竖直方向,只要设置了偏移属性,那么就需要看该元素的父元素的 position 是什么值。若父元素的 position 不是 static,那么就会以该父元素的容器范围为基准开始偏移;若父元素的 position 是 static,那么就会无视该父元素的管理范围,继续向上寻找“爷爷”元素,直到找到 position 不是 static 的元素,或者直接以 body 元素为基准。
如此,通过再次画正五边形,顺便带领大家复习了定位的知识,也阐述了使用伪元素画出更多形状的方法。
那么下一篇章,我们就引入一些例子来实战一下吧!让我们敬请期待!
关注“临界程序员”,为您送上更多精彩内容!