注册
web

聊聊 CSS 的 ::marker


::marker 是一个 CSS 的另一个伪元素,有点类似于 CSS 的 ::before::after 伪元素。只不过,它常用于给列表标记框定制样式。简而言之,使用::marker伪元素,可以对列表做一些有趣的事情,在本文中,我们将深入的聊聊该伪元素。


初识 CSS 的 ::marker


::marker 是 CSS 的伪元素,现在被纳入到 CSS Lists Module Level 3 规范中。在该规范中涵盖了列表和计算数器相关的属性,比如我们熟悉的list-style-typelist-style-positionlist-stylelist-itemcounter-increment、counter-reset、counter()和counters()等属性。


在 CSS 中 display 设置 list-item 值之后就会生成一个 Markers 标记以及控制标记位置和样式的几个属性,而且还定义了计数器(计数器是一种特殊的数值对象),而且该计数器通常用于生成标记(Markers)的默认内容。


一时之间,估计大家对于Markers标记并不熟悉,但对于一个列表所涉及到的相关属性应该较为熟悉,对于一个CSS List,它可以涵盖了下图所涉及到的相关属性:




如果你对CSS List中所涉及到的属性不是很了解的话,可以暂时忽略,随着后续的知识,你会越来越清楚的。



解构一个列表


虽然我们在 Web 的制作中经常会用到列表,但大家可能不会过多的考虑列表相关的属性或使用。就 HTML语义化出发,如果遇到无序列表的时候会使用

  • ,遇到有序列表的时候会使用
,但在有些场景(或不追求语义化的同学)会采用其他的标签元素,比如说
。针对这个场景,会采用 display 设置为list-item。如此一来会创建一个块级别的框,以及一个附加的标记框。同时也会自动增加一个隐含列表项计算数器。


ulol 元素默认情况之下会带有list-style-typelist-style-imagelist-style-position属性,可以用来设置列表项的标记样式。同样的,带有display:list-item的元素生成的标记框,也可以使用这几个属性来设置标记项样式。


list-style-type的属性有很多个值:



取值不同时,列表符号(也就是Marker标识符)会有不同的效果,比如下面这个示例所示:




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



在 CSS 中给列表项设置类型的样式风格可以通过 list-style-typelist-style-image 来实现,但这两个属性设置列表项的样式风格会有所限制。比如要实现类似下图的列表项样式风格:



值得庆幸的是,CSS 的 ::marker 给予我们更大的灵活性,可以让我们实现更多的列表样式风格,而且灵活性也更大。


创建 marker 标记框


HTML 中的 ulol 元素会自动创建 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伪元素来模拟,稍后也会介绍这一部分)。比如说,我们更改ullicolorfont-size时也会更改标记的colorfont-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类似,也可以通过contentattr()一起来控制Marker标记的效果。需要记住,生成个性化Marker标记内容需要做到几下几点:



  • 非列表项li元素需要显式的设置display:list-item (内联列表项需要使用display: inline list-item
  • 需要显式设置list-style-typenone
  • 使用content添加内容(也可以通过attr()配合data-*来添加内容)

来看一个小示例:


li::marker {
content: attr(data-emoji);
}


::marker伪元素自从可以使用content来添加内容之后,让我们可操作的空间更大了。对于列表标记(即,带有Marker标记)的元素再也不需要额外的通过::before伪元素和content来生成标记内容。而且,我们还可以结合计算数器相关的特性,让列表标记可造性空间更大。如果你感兴趣的话,请继续往下阅读。


::marker 与计数器的结合


对于无序列表,或者说统一使用同样的标记符,那么::markercontent结合就可以解决。但是如果面对的是一个有顺列表,那么我们就需要用到CSS计数器的相关特性。


先来回忆一下CSS的计数器相关的特性。在CSS中计数器有三个属性:



  • counter-reset:设置一个计数器,定义计数器名称,用来标识计数器作用域
  • counter-set:将计数器设置为给定的值。它操作现有计数器的值,并且只在元素上还没有给定名称的计数器时才创建新的计数器
  • counter-increment:用来标识计数器与实际关联元素范围,可接受两个值,第一个值必须是counter-reset定义的标识符,第二个值是可选值,是一个整数值(正负值都可以),用来预设递增的值

以及两个相关的函数:



  • counter() :主要配合content一起使用,用来调用定义好的计数器标识符
  • counters() :支持嵌套计数器,如果有指定计数器的当前值,则返回一个表示这些计数器的当前值的串联字符串。counters()有两种形式counters(name, string)counters(name, string, style)。通常和伪元素一起使用,但理论上可以支持值的任何地方使用


一般情况之下,counter-resetcounter-incrementcounter()即可满足一个计数器的需求。



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>

你可以根据自己的爱好来选择标签元素。先来看::beforecontent配合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,直接在::markercontent中使用counters(list-item, '.')所起的效果和我们预期的有所不同。如果在非列表元素的::markercontent中使用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结构
  • 伪元素::beforecontent
  • 伪元素 ::marker content

前面也向大家展示了,::marker也可以像::before一样,借助CSS计数器属性,可以更好的实现有序列表,甚至是嵌套的列表。


写在最后


虽然 ::marker 的出现允许我们为列表标记定制样式,但它也有一定的限制性,至少到目前为止是这样。比如,我们在 ::marker 伪元素上可控样式还是有限,要实现下面这样的个性化效果是不可能的:



庆幸的是,CSS 中除了 ::marker 伪元素之外,还可以使用 ::before::after 来生成内容,然后通过 CSS 来实现更具个性化的列表标记样式。


作者:大漠_w3cpluscom
来源:juejin.cn/post/7358348786843959336

0 个评论

要回复文章请先登录注册