学习使用css伪元素 :after, :before


本文来源:http://coding.smashingmagazine.com/2011/07/13/learning-to-use-the-before-and-after-pseudo-elements-in-css/

中文翻译:http://yukun.im/css/331

如果你经常关注各种网页设计博客,你可能已经注意到了,:before 和 :after 伪元素已经在前端开发中得到相当多的关注,我们有很好的理由这样做,特别是伦敦的这位博客作者Nicolas Gallagher做的实验,让伪元素赢得不少曝光。

Pseudo-element-icons in Learning To Use The :before And :after Pseudo-Elements In CSS

Nicolas Gallagher 用伪元素把语意化的HTML创作出84个图标.

为了配合这次曝光(并从这种不断增长的趋势中获益),我把我认为已经相当全面的伪元素的东西拼到了一起。本文的目标是看过用伪元素做出的很酷的事情,但是在自己尝试之前,想知道这是什么CSS技术的人。

虽然CSS规范中包含了其它伪元素,但本文只关注 :before, :after。所以,方便起见,这里的“伪元素”特指这两个伪元素。

什么是伪元素?

伪元素,就像它的名字一样,创建一个假的元素,插入到特定元素之前或之后。

伪元素的伪“pseudo”是一个希腊词的音译,基本意思是“说谎,骗人的,假的”。因此这个名称很恰当,因为它们实际上不改变文档。而是插入一个幽灵般的元素,用户可以看见,也可以应用CSS样式。

基本语法

:before 和 :after 伪元素使用简单(不像其他CSS属性一样需要一大堆供应商前缀【译注:如-moz-,-webkit-等】)。下面是一个简单的例子

#example:before{
​ content: "#";
}

#example:after {
​ content: ".";
}

这个例子中有两点需要注意,首先我们用 #example:before 和 #example:after 指向同一个元素,但是它们才是伪元素。

其次,没有 content 属性(这个属性是 generated content module定义的),伪元素不会起作用。所以伪元素需要指向一个元素,不然就没有地方插入 content 的内容。

这个例子中,id 为 example 的元素 会有一个 “#”在前面,一个“.”在后面。

语法的一些注释

你也可以给 content 属性赋空值,使伪元素看上去就像没有内容的盒子,像这样:

#example:before {
​ content: "";
​ display:block;
​ width: 100px;
​ height:100px;
​ float:left;
}

但是不能删掉 content 属性。删掉伪元素就无法工作了,至少要保留空引号作为它的值。

你可能也见到过用两个冒号的伪元素(::before 和 ::after),这个以前讨论过,简单点说这两种语法没有什么区别,只是为了区别伪类(单冒号)和CSS3中的伪元素(双冒号)。

最后还有一点,技术上来说,也可以全局定义伪元素,不指向一个特定元素,像这样

:before {
​ content: "#";
}

上面的代码是合法的,但没什么用。它将给DOM中所有的元素之前加上“#”,甚至你删除<body>中的所有元素,仍然看到两个“#”:一个是在<html>之前,一个是在<body>之前。

插入的内容的特点

如前所述,插入的内容在页面的源代码不可见,只在CSS可见。

此外插入的元素默认是内联元素【译注:inline】(或者在HTML5中,归类于text-level semantics)。所以要给插入的元素设置height, padding, margin通常需要明确定义为块级元素【译注:block-level】。

下面简短的介绍如何设置伪元素的样式,从我的编辑器中看:

Styles-pseudo-elements in Learning To Use The :before And :after Pseudo-Elements In CSS

在这个例子中,我高亮显示了要给目标元素之前和之后插入的内容设置的样式。伪元素这种方式有点独特,因为在一个声明中同时插入了内容和样式。

还要注意CSS继承规则适用于插入的元素,例如你定义了<body>的字体 Helvetica, Arial, sans-serif, 伪元素也会和其他元素一样继承这些字体样式。

同样,伪元素也不会继承不应该继承的样式(比如 padding, margin)。

在谁前面或后面?

你预料 :before 和 :after 伪元素可能会插入内容到目标元素之前或之后,但是,正如上面提到的,不是这样的。

插入的内容是做为目标元素的子元素,但是置于目标元素其它内容的“前面”或“后面”。

为了证明这一点,看下面的代码。首先在HTML中。

<p>Other content.</p>

下面是插入伪元素的CSS:

p.box {
​ width:300px;
​ border:solid 1px white;
​ padding:20px;
}

p.box:before {
​ content: "#";
​ border:solid 1px white;
​ padding:2px;
​ margin:0 10px 0 0;
}

在HTML中,你只能看到一个class为box的段落,段落里有”Other content”(如果你查看页面的源代码,看到的是一样的内容),在CSS中给段落设置width, padding, border。
然后是伪元素,在这种情况下,“#”被插入到段落内容“之前”,随后给它设置border, padding, margin。

这是我浏览器中看到的结果:【译注:参看http://jsbin.com/asuhav

Learning To Use The :before And :after Pseudo-Elements In CSS

外层盒子是段落。白色边框的“#”符号被插入到段落“内容”之前,而不是段落之前。

插入非文本内容

之前简略的提到,你可以给content属性设置空字符串值或者设置文本值。基本上,你还有两个额外的选择作为content属性的值。

首先,你可以设置一个URL指向一个图片,就像设置CSS背景图像一样:

p:before {
​ content: url(image.jpg);
}

注意【译注:最外层】没有引号。如果URL包括在引号中,将会插入一个字符串“url(image.jpg);”,而不是插入图片。【译注:content: url(“image.jpg”); 这样好像是可以的】

当然也可以包含一个 Data URI ,就像设置CSS背景图像一样。

你也可以设置一个 attr(X) 形式的函数。根据规范会“返回一个字符串,选择的对象的X属性的值”。下面是一个例子:

a:after {
​ content: attr(href);
}

什么是 attr() 函数?他返回指定的属性的值作为字符串插入到伪元素中。

上面都代码会把页面中所有的<a>元素的herf值放到<a>后面。这可以用来设置打印样式,把所有链接的地址打印出来。

你也可以用这个属性获取元素的title属性,甚至 microdata 值,当然不是所有的例子都实用/可用。但有些情况下一些特定的属性作为伪元素的内容很实用。

虽然能够获取图片的title或者alt属性的值很实用,但不能这样用。请记住,伪元素必须是一个元素的子元素。图像,是voud(或者empty)元素,没有子元素,所以这里不起作用。这同样适用于其他void 元素,比如<input>。

可怕的浏览器支持

与任何蓄势待发的前端技术一样,第一个问题就是浏览器支持情况。但在这里不是太大的问题。【译注:考虑的中国的国情…】

支持 :before 和 :after 伪元素的浏览器:
Chrome 2+,
Firefox 3.5+ (3.0 部分支持),
Safari 1.3+,
Opera 9.2+,
IE8+ (有一些bug),
几乎所有的移动浏览器【译注:同样,国情决定了…】

唯一的真正问题(毫不奇怪),IE6 和 IE7 不支持。所以如果你的受众是前端领域(或者其他IE用户少的市场),你可以自由的使用伪元素。

伪元素不危险

幸运的是,不支持伪元素不会在成巨大的可用性问题。在大多数情况下,伪元素都是起装饰(或辅助)作用,在不支持的浏览器中不会出错。所以,如果你的受众有很多IE用户,也可以在一定程度上使用它们。

几个提醒

如前所述,伪元素的内容不会出现在DOM中。这些元素不是真正的元素。因此大部分辅助设备不能访问它们。所以不要用伪元素生成重要内容,造成可用性和可访问性问题。

另外一个提醒,Firebug等开发工具看不到伪元素生成的内容。因此,如果过度使用会造成可维护性问题,调试过程会慢很多。

(作者更新:留言中提到,Chrome developer tools 可以看到伪元素的样式,但是DOM中还是看不到内容)

以上涵盖了实战前需要的所有知识。同时,为进一步了解CSS伪元素,记得点开文章中的链接了解更多。