聊聊 CSS 的 ::marker
::marker
是一个 CSS 的另一个伪元素,有点类似于 CSS 的 ::before
和 ::after
伪元素。只不过,它常用于给列表标记框定制样式。简而言之,使用::marker
伪元素,可以对列表做一些有趣的事情,在本文中,我们将深入的聊聊该伪元素。
初识 CSS 的 ::marker
::marker
是 CSS 的伪元素,现在被纳入到 CSS Lists Module Level 3 规范中。在该规范中涵盖了列表和计算数器相关的属性,比如我们熟悉的list-style-type
、list-style-position
、list-style
、list-item
、counter-increment、counter-reset、counter()和counters()
等属性。
在 CSS 中 display 设置 list-item
值之后就会生成一个 Markers 标记以及控制标记位置和样式的几个属性,而且还定义了计数器(计数器是一种特殊的数值对象),而且该计数器通常用于生成标记(Markers)的默认内容。
一时之间,估计大家对于Markers标记并不熟悉,但对于一个列表所涉及到的相关属性应该较为熟悉,对于一个CSS List,它可以涵盖了下图所涉及到的相关属性:
如果你对CSS List中所涉及到的属性不是很了解的话,可以暂时忽略,随着后续的知识,你会越来越清楚的。
解构一个列表
虽然我们在 Web 的制作中经常会用到列表,但大家可能不会过多的考虑列表相关的属性或使用。就 HTML语义化出发,如果遇到无序列表的时候会使用
- ,遇到有序列表的时候会使用
,但在有些场景(或不追求语义化的同学)会采用其他的标签元素,比如说
。针对这个场景,会采用 display
设置为list-item
。如此一来会创建一个块级别的框,以及一个附加的标记框。同时也会自动增加一个隐含列表项计算数器。ul
和 ol
元素默认情况之下会带有list-style-type
、list-style-image
和list-style-position
属性,可以用来设置列表项的标记样式。同样的,带有display:list-item
的元素生成的标记框,也可以使用这几个属性来设置标记项样式。
list-style-type
的属性有很多个值:
取值不同时,列表符号(也就是Marker标识符)会有不同的效果,比如下面这个示例所示:
Demo 地址:codepen.io/airen/full/…
在 CSS 中给列表项设置类型的样式风格可以通过 list-style-type
和 list-style-image
来实现,但这两个属性设置列表项的样式风格会有所限制。比如要实现类似下图的列表项样式风格:
值得庆幸的是,CSS 的 ::marker
给予我们更大的灵活性,可以让我们实现更多的列表样式风格,而且灵活性也更大。
创建 marker 标记框
HTML 中的 ul
和 ol
元素会自动创建 marker
标记框。如果通过浏览器调试器来查看的话,你会发现,不管是 ul
还是 ol
的子元素 li
会自带 display:list-item
属性设计(客户端默认属性),另外会带有一个默认的list-style-type
样式设置:
这样一来,它自身就默认创建了一个 marker
标记框,同时我们可以通过 ::marker
伪元素来设置列表项的样式风格,比如下面这个示例:
ul ::marker,
ol ::marker {
font-size: 200%;
color: #00b7a8;
font-family: "Comic Sans MS", cursive, sans-serif;
}
你会看到效果如下所示:
Demo 地址:codepen.io/airen/full/…
对于非列表元素,可以通过display: list-item
来创建 Marker 标记,这样就可以在元素上使用 ::marker
伪元素来设置项目符号的样式。虽然通过display:list-item
在形式上看上去像列表项,但在语义化上并没有起到任何的作用。
在深入探讨 ::marker
使用之前,大家要知道,元素必须要具备一个Marker标记框,对于非列表项的元素需要显式的使用 display:list-item
来创建Marker标记框。
CSS的display属性是一个非常重要的属性,现在被纳入在CSS Display Module Level 3中。CSS的display
属性可以改变任何一个元素的框模型。而且在Level 3规范中给display
引用了两个值的语法,比如使用display: inline list-item
可以创建一个内联列表项。
::marker
的基本使用
前面的小示例中,其实我们已经领略到了::marker
的魅力。在列表项li
中,其实已经带有Marker标记框,可以借助::marker
伪元素来设置列表标记的样式。
我们先来回忆一下,CSS的::marker
还未出现(或者说不支持的浏览器)时,要对列表项设置不同的样式,都是通过li
上来控制(看上去继承了li
上的样式)。虽然能设置列表样式,但还是具有一定的局限性,灵活度不够大 —— 特别是当列表项标记样式和内容要区分时。
CSS的::marker
会让我们变得容易的多。从前面的示例中我们可以了解到, ::marker
伪元素和列表项内容是分开的,正因此,我们可以独立为两个部分设计不同的样式。这在以前的CSS版本中是不可能的(除非借助::before
伪元素来模拟,稍后也会介绍这一部分)。比如说,我们更改ul
或li
的color
或font-size
时也会更改标记的color
和font-size
。为了达到两者的区分,往往需要在HTML中做一些结构上的调整,比如列表项用一个子元素来包裹(比如span
元素或::before
伪元素)。
更了大家更易于理解::marker
的作用,我们在上面的示例基础上做一些调整:
.box:nth-child(odd) li {
font-size: 200%;
color: #00b7a8;
font-family: "Comic Sans MS", cursive, sans-serif;
}
.box:nth-child(even) ::marker {
font-size: 200%;
color: #00b7a8;
font-family: "Comic Sans MS", cursive, sans-serif;
}
代码中的具体作用不做介绍,很简单的代码,但效果却有很大的差异性,如下图所示:
很神奇吧!在浏览器中查看源码,你会发现使用::marker
和未使用::marker
的差异性:
虽然::marker
易于帮助我们控制标记的样式风格,但有一点需要特别注意,如果显式的设置了list-style-type: none
时,::marker
标记内容就会丢失不可见。在这个时候,不管是否显式的设置了::marker
的样式都将会看不到。比如:
大家是否还记得,在::marker
还没有出现之前,要对列表项设置别的标记符,比如Emoji。我们就需要通过别的方式来完成,最为常见的是修改HTML的结构,或者借助CSS伪元素::before
和CSS的content
属性,例如下面这个示例:
Demo 地址:codepen.io/airen/full/…
事实上,CSS的::marker
和伪元素::before
类似,也可以通过content
和attr()
一起来控制Marker标记的效果。需要记住,生成个性化Marker标记内容需要做到几下几点:
- 非列表项
li
元素需要显式的设置display:list-item
(内联列表项需要使用display: inline list-item
) - 需要显式设置
list-style-type
为none
- 使用
content
添加内容(也可以通过attr()
配合data-*
来添加内容)
来看一个小示例:
li::marker {
content: attr(data-emoji);
}
::marker
伪元素自从可以使用content
来添加内容之后,让我们可操作的空间更大了。对于列表标记(即,带有Marker标记)的元素再也不需要额外的通过::before
伪元素和content
来生成标记内容。而且,我们还可以结合计算数器相关的特性,让列表标记可造性空间更大。如果你感兴趣的话,请继续往下阅读。
::marker
与计数器的结合
对于无序列表,或者说统一使用同样的标记符,那么::marker
和content
结合就可以解决。但是如果面对的是一个有顺列表,那么我们就需要用到CSS计数器的相关特性。
先来回忆一下CSS的计数器相关的特性。在CSS中计数器有三个属性:
counter-reset
:设置一个计数器,定义计数器名称,用来标识计数器作用域counter-set
:将计数器设置为给定的值。它操作现有计数器的值,并且只在元素上还没有给定名称的计数器时才创建新的计数器counter-increment
:用来标识计数器与实际关联元素范围,可接受两个值,第一个值必须是counter-reset
定义的标识符,第二个值是可选值,是一个整数值(正负值都可以),用来预设递增的值
以及两个相关的函数:
counter()
:主要配合content
一起使用,用来调用定义好的计数器标识符counters()
:支持嵌套计数器,如果有指定计数器的当前值,则返回一个表示这些计数器的当前值的串联字符串。counters()
有两种形式counters(name, string)
和counters(name, string, style)
。通常和伪元素一起使用,但理论上可以支持值的任何地方使用
一般情况之下,
counter-reset
、counter-increment
和counter()
即可满足一个计数器的需求。
CSS的计数器使用非常的简单。在元素的父元素上显式设置:
body {
counter-reset: section
}
使用counter-reset
声明了一个计数器标识符叫section
。然后再需要使用计算器的元素上(一般配合伪元素::before
)使用counter-increment
来调用counter-reset
已声明的计数器标识符,然后使用counter(section)
来计数:
h3::before {
counter-increment: section
content: "Section " counter(section) ": "
}
下图会更详尽一些,把计数器可能会用的值都罗列出来了,可供参考:
回到我们的列表设置中来。::marker
还没有得到浏览器支持之前,一般都是使用CSS的计数器来实现一些带有个性化的有顺序列表,比如下面这样的效果:
也可以借助计数器做一些其他的效果比如:
Demo 地址:codepen.io/snookca/ful…
更为厉害的是,CSS的计数器配合复选框或单选按钮还可以做一些小游戏,比如 @una教程中向我们展示的一个效果:
Demo 地址:codepen.io/jak_e/full/…
@kizmarh使用同样的原理,做了一个黑白棋的小游戏:
是不是很有意思,有关于CSS计数器相关的特性暂且搁置。我们回到::marker
的世界中来。
::marker
配合content
可以定制个性化Marker标记风格。借助CSS计数器,可以更轻易的构建带有顺序的Marker标记。同样可以让Marker标记和内容分离。更易于实现可定制化的样式风格。
接下来,我们来看一个简单的示例,看看::marker
生成的标记符和以往生成的标记符效果上有何差异没。
结果很简单,这里使用的是一个无序列表:
<ul>
<li>
Item1
<ul>
<li>Item 1-1li>
<li>Item 1-2li>
<li>Item 1-3li>
ul>
li>
<li>Item2li>
<li>Item3li>
<li>Item4li>
<li>Item5li>
ul>
你可以根据自己的爱好来选择标签元素。先来看::before
和content
配合counter()
和counters()
的一个效果:
/* counter() */
.box:nth-child(1) {
ul {
counter-reset: item;
}
li {
counter-increment: item;
&::before{
content: counter(item);
/* ... */
}
}
}
/* counters() */
.box:nth-child(2) {
ul {
counter-reset: item;
}
li {
counter-increment: item;
&::before{
content: counters(item, '.');
/* ... */
}
}
}
对于上面的效果,大家可能也猜到了。我们再来看一下::marker
的使用:
/* counter() */
.box:nth-child(3) {
ul {
counter-reset: item;
}
li {
counter-increment: item;
}
::marker {
content: counter(item);
/* ... */
}
}
/* counters() */
.box:nth-child(4) {
ul {
counter-reset: item;
}
li {
counter-increment: item;
}
::marker {
content: counters(item, '.');
/* ... */
}
}
可以看到::marker
和前面::before
效果是一样的:
另外使用::marker
还有一特殊之处。不管是列表元素还是设置了display:list-item
的非列表元素,不需要显式的使用counter-reset
声明计数器标识符,也无需使用counter-increment
调用已声明的计数器标识符。它可以直接在 ::marker
伪元素的 content
中使用 counter(list-item)
或 counters(list-item, '.')
。
但是非列表元素,哪怕是设置了display:list-item
,直接在::marker
的content
中使用counters(list-item, '.')
所起的效果和我们预期的有所不同。如果在非列表元素的::marker
的content
中使用counters()
达到我们想要的效果,需要使counter-reset
先声明计数器标识符,然后counter-increment
调用已声明的计数器标识符(回归到以前::before
的使用)。具本的可以看下面的示例代码:
::marker {
content: counter(list-item);
padding: 5px 30px 5px 12px;
background: linear-gradient(to right, #f36, #f09);
font-size: 2rem;
clip-path: polygon(0% 0%, 75% 0, 75% 51%, 100% 52%, 75% 65%, 75% 100%, 0 100%);
border-radius: 5px;
color: #fff;
text-shadow: 1px 1px 1px rgba(#09f, .5);
}
.box:nth-child(2n) ::marker {
content: counters(list-item, '.');
}
.box:nth-child(3) {
section {
counter-reset: item;
}
article {
counter-increment: item;
}
::marker {
content: counters(item, '.');
}
}
具体效果如下:
是不是觉得::marker
非常有意思,特别是给元素添加Marker标记的时候。换句话说,就是在定制个性化列表符号时,使用::marker
伪元素要比::before
之类的较为方便。而且::marker
是元素原生的列表标记符(::marker
)。
一旦::marker
伪元素得到所有浏览器支持之后,我们要让列表标记符和内容分离就会多了一种方案:
- 调整HTML结构
- 伪元素
::before
和content
- 伪元素
::marker
和content
前面也向大家展示了,::marker
也可以像::before
一样,借助CSS计数器属性,可以更好的实现有序列表,甚至是嵌套的列表。
写在最后
虽然 ::marker
的出现允许我们为列表标记定制样式,但它也有一定的限制性,至少到目前为止是这样。比如,我们在 ::marker
伪元素上可控样式还是有限,要实现下面这样的个性化效果是不可能的:
庆幸的是,CSS 中除了 ::marker
伪元素之外,还可以使用 ::before
或 ::after
来生成内容,然后通过 CSS 来实现更具个性化的列表标记样式。
来源:juejin.cn/post/7358348786843959336