转载

试探列表中的::marker

CSS中的列表包括有序、无序和定义列表,不过今天要扯的是是无序列表和有序列表。回忆一下,不管是有序还是无序列表,对于样式上都包括了 list-style-imagelist-style-typelist-stype-position 三个样式属性,而他们又能简写成 list-stype 。至于怎么用?这里就做过多阐述,如果不清楚的可以查看官网:

  • CSS2.1 Lists
  • CSS3 Lists

血案的起源

前几天有位同学问过一个问题, <li> 元素中的子元素浮动( float:left )之后,列表的项目符号会跑到浮动元素的后面。这个问题似乎在三年前碰到过,但又有那么点不一样。先来上张图吧,一图解胜过千言万语:

试探列表中的::marker

简单看看代码:

<ul>     <li>       <span class="title">我是标题</span>       <span class="content">我是描述内容,我就是描述内容,我不是别的</span>     </li>     ...     <li>       <span class="title">我是标题</span>       <span class="content">我是描述内容,我就是描述内容,我不是别的</span>     </li>   </ul> 

对应的CSS:

*{   margin: 0;   padding: 0; } ul {   width: 350px;   margin: 50px auto;   border: 1px solid green;   list-style-position: inside; } li {   border-bottom: 1px solid #ccc;   padding: 20px 0;   font-size: 16px;   color: #fff; } li:after {   content:"";   display: table;   clear:both; } .title {   float: left;   background-color: orange; } .content {   background-color: #f63;   overflow: hidden;   display: block; } 

这只是其中一种情形,再这个问题上,再扩展一下: li 中的子元素 .title 向右浮动,而 .content 不动:

.title {   float: right;   background-color: orange; } 

效果如下:

试探列表中的::marker

接下来再看一个情形, li 中的子元素 .title 向左浮动( float:left ), .content 元素向右浮动( float:right ):

.title {   float: left;   background-color: orange; } .content {   background-color: #f63;   float:right;   max-width: 80%; } 

效果如下:

试探列表中的::marker

上面的几种情形都是在Chrome浏览器下的测试效果,其实在Firefox浏览器也存在这样的现象,只是略有一点差别而以,比如最前面的示例,在Firefox下的效果就成这样了:

试探列表中的::marker

现在纠结了,怎么就成这样了呢?我们一起来看看。

查找原因

这个问题纠结了我很久,最后在 W3C规范 中找到原因所在。

先来看看 CSS2.1 中这样说:

outside

The marker box is outside the principal block box. The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order, but does require that for list items whose 'direction' property is 'ltr' the marker box be on the left side of the content and for elements whose 'direction' property is 'rtl' the marker box be on the right side of the content. The marker box is fixed with respect to the principal block box's border and does not scroll with the principal block box's content. In CSS 2.1, a UA may hide the marker if the element's 'overflow' is other than 'visible'. (This is expected to change in the future.) The size or contents of the marker box may affect the height of the principal block box and/or the height of its first line box, and in some cases may cause the creation of a new line box. Note: This interaction may be more precisely defined in a future level of CSS.

inside

The marker box is placed as the first inline box in the principal block box, before the element's content and before any :before pseudo-elements. CSS 2.1 does not specify the precise location of the marker box.

给我的理解,列表中的项目符号就是一个 ::marker ,就称他是列表独有的隐式元素吧,而这个 ::marker 具有Box Module的特性。在列表中占有自己的位置。在CSS2.1中对其讲述的不够太多,那么在 CSS3中 ,对其做出了更详细的描述。

根据自己的理解(不知道理解的对不对),我将 li 绘制了一张图来阐述其 ::marker :

试探列表中的::marker

看图或许更好的理解,那么我们使用一个元素来模拟 ::marker :

<ul class="list">   <li><span class="title">标题</span><span class="content">内容内容内容内容内容内容内容内容</span></li>   <li><span class="title">标题</span><span class="content">内容内容内容内容内容内容内容内容</span></li> </ul> <ul class="marker">   <li><span class="marker">•</span><span class="title">标题</span><span class="content">内容内容内容内容内容内容内容内容</span></li>   <li><span class="marker">•</span><span class="title">标题</span><span class="content">内容内容内容内容内容内容内容内容</span></li> </ul>  <style>   .marker {     list-style-type: none;   } </style> 

来模拟一下 .title 浮动之后的 ::marker :

ul {   width: 200px;   list-style-position: inside; } .marker {   list-style-type: none; }  .title {   float: left;   background: orange; } .content {   display: block;   overflow: hidden; } 

对比一下默认的 ::marker 和模拟的 ::marker 效果:

试探列表中的::marker

上图是 list-style-positionoutside 时的效果,下图是 list-style-positioninside 时的效果:

试探列表中的::marker

这样一来,问题所在就很明显了。既然找到根源,那么要解决就容易多了。

解决方案

如果不纠结为什么会如此?仅从解决问题上来讲的话,方法很多。就我所知道的就有:

  • 修改标签
  • 自定义向左浮动元素的 display
  • 使用伪类 :before
  • 使用背景图像

接下来,咱位一起来看看各解决方案是怎么处理的。

修改标签

这种方法比较简单,给浮动元素外面嵌套一个容器:

<ul>   <li>     <div class="box">       <span class="title">我是标题</span>       <span class="content">我是描述内容,我就是描述内容,我不是别的</span>     </div>   </li>   ...   <li>     <div class="box">       <span class="title">我是标题</span>       <span class="content">我是描述内容,我就是描述内容,我不是别的</span>     </div>   </li> </ul> 

CSS:

<style>   * {     margin: 0;     padding: 0;   }   ul {     width: 350px;     margin: 50px auto;     border: 1px solid green;     list-style-position: inside;   }   li {     border-bottom: 1px solid #ccc;     padding: 20px 0;     font-size: 16px;   }   li:after {     content:"";     display: table;     clear:both;   }   .box{     display: inline-block;     max-width: 100%;     vertical-align: top;   }   .title {     float: left;     background-color: orange;   }   .content {     background-color: #f63;     overflow: hidden;     display: block;   } </style> 

此时效果如下:

试探列表中的::marker

由于 .box 容器宽度为 100% ,至使内容断行显示,稍作修改:

.box{   display: inline-block;   max-width: 90%;   vertical-align: top; } 

这里有一个较为蛋疼的问题,就是 max-width 不好控制,主要因为我们都不清楚 ::marker 的宽度是多少,根据以往经验, ::marker 的默认宽度为 1em ,尝试使用 calc() 来计算一下:

.box{   display: inline-block;   max-width: calc(100% - 1em);   vertical-align: top; } 

这样就完美了:

试探列表中的::marker

如果你纠结 calc() 兼容问题或者性能问题,那只有最土的办法,在调试工具中调度:

试探列表中的::marker

修改子元素的 display 属性

既然知道 li::marker 会受到子元素 float 的影响,那么反过来思考一下,将 li 的默认 list-style-type 设置为 none ,然后在浮动元素 .title 上重置为 display:list-item;

<ul>   <li>     <span class="title">我是标题</span>     <span class="content">我是描述内容,我就是描述内容,我不是别的</span>   </li>   ...   <li>     <span class="title">我是标题</span>     <span class="content">我是描述内容,我就是描述内容,我不是别的</span>   </li> </ul> 

CSS:

* {   margin: 0;   padding: 0; } ul {   width: 350px;   margin: 50px auto;   border: 1px solid green;   list-style-position: inside; } li {   border-bottom: 1px solid #ccc;   padding: 20px 0;   font-size: 16px;   list-style-type: none; } li:after {   content:"";   display: table;   clear:both; } .title {   float: left;   background-color: orange;   display: list-item;   list-style-type: disc; } .content {   background-color: #f63;   overflow: hidden;   display: block; } 

效果如下:

试探列表中的::marker

使用伪类

使用伪类也是一种方案,可以使用 :before 配合 字符编码 ,来生成列表符号。

* {   margin: 0;   padding: 0; } ul {   width: 350px;   margin: 50px auto;   border: 1px solid green;   list-style-position: inside; } li {   border-bottom: 1px solid #ccc;   padding: 20px 0;   font-size: 16px;   list-style-type: none; } li:after {   content:"";   display: table;   clear:both; } .title {   float: left;   background-color: orange; } .title:before {   content:"•";   display: inline-block; } .content {   background-color: #f63;   overflow: hidden;   display: block; } 

效果如下:

试探列表中的::marker

对于无序列表,这种解决方案也算是能接受,甚至还可以配合 content 绘制圆点(不做过多阐述),不过换成别的项目符号,就略显吃力。

对于有序列表可以使用CSS3中的 counter()

<ol>   <li>     <span class="title">我是标题</span>     <span class="content">我是描述内容,我就是描述内容,我不是别的</span>   </li>   ...   <li>     <span class="title">我是标题</span>     <span class="content">我是描述内容,我就是描述内容,我不是别的</span>   </li> </ol> 

CSS:

* {   margin: 0;   padding: 0; } ol {   width: 350px;   margin: 50px auto;   border: 1px solid green;   list-style-position: inside;   counter-reset: list-item; } li {   border-bottom: 1px solid #ccc;   padding: 20px 0;   font-size: 16px;   list-style-type: none;   counter-increment: list-item; } .title:before {   content: counter(list-item) '.';   display: inline-block; } li:after {   content:"";   display: table;   clear:both; } .title {   float: left;   background-color: orange; } .content {   background-color: #f63;   overflow: hidden;   display: block; } 

效果如下:

试探列表中的::marker

话又说回来,无序列表使用字符编码,对于其他方面的字符不好控制,其实也可以使用 counter() 函数,配合其第二个值来控制:

.title:before {   content: counter(list-item, disc);   display: inline-block; } 

试探列表中的::marker

变更一下,

.title:before {   content: counter(list-item, square);   display: inline-block; } 

效果如下:

试探列表中的::marker

也就是说,只要使用 counter() 配合其第二个参数值(参数值选择 list-style-type ),就可以类似控制列表一样控制其项目符号。

有关于 counter() 相关的介绍可以猛击这里。

使用背景图片

使用背景图片,我认为是不尽人意的一种方案,如果列表项目符号不是圆,需要额外制作图像,特别是有序列表的时候,会让你抓狂。在这个方案中也抛弃了有序列表的解决。

另外,此处示例使用的是gradient来模拟圆点:

li {   border-bottom: 1px solid #ccc;   padding: 20px 0;   font-size: 16px;   list-style-type: none;   background: radial-gradient(circle at center, #000 10%, #000 20%, #fff 30%,transparent 100%);   background-size: 20px 20px;   background-repeat: no-repeat;   background-position: 0 19px;   padding-left: 20px; } 

效果如下:

试探列表中的::marker

未来解决方案

在CSS3提出了 ::marker 的 规范 。一旦浏览器支持这个规范,我们就可以像类似使用 :before 这样的伪类来控制项目列表符。来看一个简单的示例:

<ol>   <li>This is the first item.</li>   <li>This is the second item.</li>   <li>This is the third item.</li> </ol> 

CSS:

li { list-style-type: lower-roman; } li::marker { margin: 0 3em 0 0; color: blue; font-weight:bold; } 

对应的效果如下:

试探列表中的::marker

到目前为止,你是还看不到效果的。你懂得。

根据上例推断,那么我们就可以给 ::marker 设置浮动样式。如此一来,就不会跑到浮动的子元素后面了。试目以待。以后也不需要纠结了。

总结

本文从一个血案引起对 ::marker 的初探。从而借助列表的 ::marker 特性,暂时采用几种不同的方案解决了问题。不过这几种方案都是换汤不换药,我们也只能拭目以待 ::marker 到来。

试探列表中的::marker

大漠

常用昵称“大漠”,W3CPlus, Sass中国 创始人,目前就职于手淘。中国Drupal社区核心成员之一。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。

正文到此结束
Loading...