如何从css生成内容和计数组件中得到益处

编辑推荐: 掘金是一个高质量的技术社区,从 CSS 到 Vue.js,性能优化到开源类库,让你不错过前端开发的每一个技术干货。 点击链接查看最新前端内容,或到各大应用市场搜索「 掘金」下载APP,技术干货尽在掌握中。

特别声明:此篇文章由D姐根据的英文文章原名《How To Benefit From CSS Generated Content And Counters》进行翻译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://coding.smashingmagazine.com/2013/04/12/css-generated-content-counters以及作者相关信息

——作者:

——译者:D姐

用css生成内容是在css2规范中首次提出的。几年来,这个特性只有少数的web开发者使用,原因就是浏览器对于他们的兼容性不好。随着2009年IE8的发布,生成内容被再次提出,而且很多有趣的实现也首次采用生成内容的方式。在这篇文章里面,我们将讨论如何使用生成内容

生成内容是什么?

在技术角度来说,生成内容是简单的通过css在dom树上创建一个抽象的结构。因此,在实践中,生成内容只存在于网络文档布局中

利用js访问生成的内容,可以通过获取content属性的文本值

var test = document.querySelector('#test');
var result   = getComputedStyle(test, ':before').content;
var output = document.querySelector('#output');
output.innerHTML = result;	

查看在线案例

插入生成内容

在实际元素内容前后插入内容,使用:before 和:after伪元素。为了区别伪元素,我们可以使用下面的伪标签。

<p>
  <before>Start</before>
    Actual content
  <after>End</after>
</p>	

我们的样式可能这样:

p:before {
  content: "Start";
}

p:after {
  content: "End";
}	

查看在线案例

记住,如果你用css3的规范验证css文件,那么:before 和 :after伪元素应该写成::before 和 ::after。否则,css验证器会报错。

正如你所见,插入的这两个字符串都是content的属性。这个属性接受如下值:

  • none, normal:伪内容不生成;
  • <string>:表示一个包含在引号中的文本字符串;
  • url():这个方法可以让我们插入一个外部资源(通常是一个图片),也可以使用background-image;
  • counter():counters()这些方法插入计数器(详见下文);
  • attr(attribute):这个方法让我们可以插入给定元素的属性值;
  • open-quote, close-quote, no-open-quote, no-close-quote:这些值可以自动产生引号标记

记住,产生元素会占据页面的一定空间,而且他们的存在会影响浏览器对父元素的尺寸计算值。

插入字符串

在前面的例子中,我们在现有元素的内容前后插入两个简单的字符串。生成内容也可以让我们通过转义符号插入更复杂的内容:

p:before {
  content: "\00A7";
  padding-right: 0.2em;
}	

查看在线案例

在双引号之间的转义符号是16进制的unicode值。我们也可以把简单的字符串跟unicode符号结合:

p:before {
  content: "( " "\00A7" " )";
  padding-right: 0.2em;
}	

查看在线案例

如果你需要,可以在Alan Wood的网站上查到所有的unicode符号的列表

请注意,所有的文本内容都需要用content的属性插入,空格和tabs也可以通过键盘插入到页面中。

使用web字体插入icon

Web字体可以通过生成内容来插入图标。依赖于web字体库,你可以插入简单的字符或是unicode序列:

@import url(http://weloveiconfonts.com/api/?family=brandico);

p:before {
  content: "\f303";
  padding-right: 0.3em;
  font-family: 'brandico', sans-serif;
  font-size: 22px;
}	

查看在线案例

在这里例子中,我们插入了twitter的icon。我们的代码可能如下这样写:

.icon-twitter:before {
  content: "\f303";
  padding-right: 0.3em;
  font-family: 'brandico', sans-serif;
  font-size: 22px;
}	

插入图片

我们可以通过url()方法插入图片:

a:before {
   content: url(link.png);
   padding-right: 0.2em;
}	

查看在线案例

正如你所见,这个方法跟background-image属性有相同的效果。

插入属性值

通过attr()方法可以插入元素的属性值:

a[href]:after {
  content: "( " attr(href) " )";
  padding-left: 0.2em;
  color: #000;
  font: small "Courier New", Courier, monospace;
}	

查看在线案例

我们只是插入了一个href属性值,是一个简单的字符串。

插入计数器

自动增长的数值是由css的两个属性控制的,他们是counter-reset 和counter-increment。计数器由这些属性定义,然后用counter() 和 counters()方法获取内容属性。

counter-reset计数器重置属性可以包含一个或多个计数器的名字(例如“标识符”),后面可以跟一个可选的整数参数。通过counter-increment属性设置增长的整数值,可以作用在任何元素。他的默认值是0.负值是容许的。

counter-increment计数器增量属性是类似的,他们基本的不同是这个增加一个计数器,其默认增量是1.负值是容许的。

现在我看一个例子,他的结构如下:

<dl>
  <dt>term</dt>
  <dd>description</dd>
  <dt>term</dt>
  <dd>description</dd>
  <dt>term</dt>
  <dd>description</dd>
</dl>	

我们想给列表中的每一个dt添加一个正数编号,css中这样写:

dl {
  counter-reset: term;
}
dt:before {
  counter-increment: term;
  content: counter(term);
}	

查看在线案例

第一个规则设置定义列表的计数器。这个叫‘范围’。计数器的名字(或标识符)是term。不管为我们的计数器选择的名字是什么,必须与计数器增量属性值一致(当然,这个名字应该是有意义的)。

第二个规则,我们用伪元素:before给dt元素添加,因为我们想要在现有元素内容前正好插入计数器。让我们再仔细看看第二条规则中的第二个声明。counter()方法接受我们的标识符(term)作为他的参数,content属性产生计数器。

数值和元素内容之间没有空间。如果我们想要添加空间的,即在数字后面留一个空间,我们可以用content属性插入如下字符串:

dt:before {
  content: counter(term) ". ";
}	

查看在线案例

注意,引号之间的字符串。这个空间插入就像我们在键盘上键入他们一样。事实上,content属性可以看做是javascript中的document.write()方法,除了他不能添加真实的文档内容。简单的说,content属性仅仅是创建一个抽象的dom树,而不能修改他。

如果你想通过附加的伪元素,可以给计数器添加更多的其他样式。例如:

dt:before {
  content: counter(term);
  padding: 1px 2px;
  margin-right: 0.2em;
  background: #ffc;
  color: #000;
  border: 1px solid #999;
  font-weight: bold;
}	

查看在线案例

我们设置了一个背景色,添加一些padding和margin-right,字体加粗,计数器外面添加一个1px的边框。现在我们的计数器就更有吸引力了。

此外,计数器可以是负数的。当我们处理一个负数计数器,我们应该应用一点数学知识,即这里是关于加减的正负数。例如,如果我们需要编号从0开始,我们可以这样写:

dl {
  counter-reset: term -1;
}
dt:before {
  counter-increment: term;
  content: counter(term) ". ";
}	

查看在线案例

通过设置counter-reset减1,然后增量为1,那么结果就是0,编号就是从0开始。负数计数器可以结合跟正数计数器结合,那会带来一个很有趣的效果。考虑一下这个例子:

dl {
  counter-reset: term -1;
}
dt:before {
  counter-increment: term 3;
  content: counter(term) ". ";
}

查看在线案例

正如你所见,加减的正负数结合产生一个功能强大的计数器。而我们只需要一个简单的计算,就可以完成自动编号的功能。

另一个有趣的特性在于,可以用css计数器实现嵌套的能力。事实上,编号可以按照如1.1, 1.1.1, 2.1这样的分段进行的。给我们的列表添加一个分段,编写如下:

dl {
  counter-reset: term definition;
}
dt:before {
  counter-increment: term;
  content: counter(term) ". ";
}
dd:before {
  counter-increment: definition;
  content: counter(term) "." counter(definition) " ";
}	

查看在线案例

这个例子类似于第一个,但是这个例子中有两个计数器term和definition。两个计数器的范围设置在第一个规则和lives的dl元素上。第二个规则,在每个列表项前面插入第一个计数器。这个规则并不是很有趣,因为他的作用已经知道。然而,最后一个规则是我们这段代码的核心,因为他可以执行以下操作:

  1. 给dd元素添加第二个计数器(definition);
  2. 插入第一个计数器(term),留一定空间
  3. 插入第二个计数器(definition),留一定空间

注意,第二步和第三步都是通过content属性,用:before伪元素附加计数器的。

另一个有趣的事情是计数器可以“自我嵌套“。某种意义上,重置计时器可以在后代元素(或伪元素)上自动创建一个新的计数器实例。这个对于xhtml中的列表是有用的,可以嵌套到任意深度。然而,为每个列表指定一个计数器并不总是可行的,因为他有可能产生一些冗余代码。基于这个原因,counters()方法是有用的。这个方法创建一个字符串,这个字符串包含给定计数器范围内的,所有同名的计数器。然后计数器用一个字符串分割。采用如下结构:

<ol>
  <li>item</li>
  <li>item
    <ol>
      <li>item</li>
      <li>item</li>
      <li>item
        <ol>
          <li>item</li>
          <li>item</li>
        </ol>
      </li>
    </ol>
  </li>
</ol>	

下面的css将嵌套列表项计数为1, 1.1, 1.1.1等等:

ol {
  counter-reset: item;
  list-style: none;
}
li {
  display: block;
}
li:before {
  counter-increment: item;
  content: counters(item, ".") " ";
}	

查看在线案例

这个例子,我们仅为每个嵌套层级设置项目计数器。而不是写三个不同的计数器(例如item1, item2, item3),从而为每个嵌套的ol元素创建出三个不同的范围,这里我们依靠counters()来实现这个目的。第二个规则是重要的,需要进一步解释。因为有序列表本身有默认的标示(例如数字),我们把这些元素从列表项变成块级元素,进而去掉这些标示。记住,只有display: list-items的元素才有标示。

现在我们认真看一下第三个规则,他才是真正的幕后工作的。第一个声明在最外层列表前设置计数器。然后,第二个声明counters()方法,为内层列表创建了所有计数器的实例。这个方法结构如下:

  1. 他的第一个参数是给定计数器的名字,后面紧跟一个逗号;
  2. 第二个参数是一个双引号之间的空间。

注意,我们已经在counters()方法后面插入了一个空格,保证数字跟列表项当前内容之间是分离的。

默认计数器是用十进制格式化的。可是,用list-style-type也可以修改计数器的样式。用默认语法counter(name)(没有样式)或or counter(name, 'list-style-type')修改默认格式。实际中,推荐以下样式:

  • decimal
  • decimal-leading-zero
  • lower-roman
  • upper-roman
  • lower-greek
  • lower-latin
  • upper-latin
  • lower-alpha
  • upper-alpha

不要忘记我们正在跟数字系统打交道。也要记住规范没有定义超出字母排序的字母系统要如何显示。例如,超过小写拉丁文的26个字母后是没有定义的。所以,对于长列表而言,推荐用数字做计数器:

dl {
  counter-reset: term definition;
}
dt:before {
  counter-increment: term;
  content: counter(term, upper-latin) ". ";
}
dd:before {
  counter-increment: definition;
  content: counter(definition, lower-latin) ". ";
}	

查看在线案例

也可以给counters()方法添加样式:

li:before {
  counter-increment: item;
  content: counters(item, ".", lower-roman) " ";
}	

查看在线案例

注意counters()方法也可以接受第三个参数(小写罗马字符),作为参数列表中的最后一项,用逗号跟前一个参数分开。然而,counters()方法不容许我们为每一个嵌套级别设置不同的样式。

总结

随着新浏览器的诞生,我们使用css生成内容来创建我们布局中的字符串和图形。生成内容无疑对每一个开发人员来说,是一个很棒的工具,是应该值得学习的。

扩展阅读:

  1. CSS Lists and Counters Module Level 3
  2. CSS Generated Content for Paged Media Module
  3. Generated content, automatic numbering, and lists
  4. css counters兼容性
  5. Automatic numbering with CSS Counters
  6. CSS Content
  7. Styling Elements With Glyphs, Sprites and Pseudo-Elements
  8. 学习使用:before和:after伪元素
  9. CSS Counters: counter-increment and Friends
  10. An introduction to CSS pseudo-element hacks
  11. Making CSS Count Backwards
  12. AUTOMATIC FIGURE NUMBERING WITH CSS COUNTERS
  13. On CSS counters plus a CSS3 Reversi UI
  14. CSS3美化有序列表
  15. CSS content, counter-increment 和 counter-reset详解

译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!

关于D姐

网名different,前端攻城师一名,现居北京,对css3、javascript、前端UI等前端开发有浓厚兴趣,请关注我:新浪微博

如需转载烦请注明出处:

英文原文:http://coding.smashingmagazine.com/2013/04/12/css-generated-content-counters/

中文译文:http://www.w3cplus.com/css3/css-generated-content-counters.html

返回顶部