特别声明:此篇文章由D姐根据Gabriele Romanato的英文文章原名《How To Benefit From CSS Generated Content And Counters》进行翻译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://coding.smashingmagazine.com/2013/04/12/css-generated-content-counters以及作者相关信息
——作者:Gabriele Romanato
——译者: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元素上。第二个规则,在每个列表项前面插入第一个计数器。这个规则并不是很有趣,因为他的作用已经知道。然而,最后一个规则是我们这段代码的核心,因为他可以执行以下操作:
- 给dd元素添加第二个计数器(definition);
- 插入第一个计数器(term),留一定空间
- 插入第二个计数器(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()方法,为内层列表创建了所有计数器的实例。这个方法结构如下:
- 他的第一个参数是给定计数器的名字,后面紧跟一个逗号;
- 第二个参数是一个双引号之间的空间。
注意,我们已经在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生成内容来创建我们布局中的字符串和图形。生成内容无疑对每一个开发人员来说,是一个很棒的工具,是应该值得学习的。
扩展阅读:
- CSS Lists and Counters Module Level 3
- CSS Generated Content for Paged Media Module
- Generated content, automatic numbering, and lists
- css counters兼容性
- Automatic numbering with CSS Counters
- CSS Content
- Styling Elements With Glyphs, Sprites and Pseudo-Elements
- 学习使用:before和:after伪元素
- CSS Counters: counter-increment and Friends
- An introduction to CSS pseudo-element hacks
- Making CSS Count Backwards
- AUTOMATIC FIGURE NUMBERING WITH CSS COUNTERS
- On CSS counters plus a CSS3 Reversi UI
- CSS3美化有序列表
- 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