我们能真切地感觉到,选择器是CSS的核心部分。如果没有它们的话,我们除了把属性嵌入到每个元素里,就没有其他办法能把样式应用在元素上了,那真是太糟糕了。通过选择器赋予的选择任何形式任何种类元素的能力,我们可以只用很少的几行CSS完成很大一部分样式设置工作。
本章我们将深入探讨如何巧妙地使用选择器,并且概述一下哪些类型的选择器被普遍支持且应用最为广泛。
1 伪类与伪元素
在CSS中有两种“伪”字头的选择器:伪类(pseudo-class)和伪元素(pseudo-element)。CSS2.1中的伪类有:
:link——未访问过的链接;
:visited——访问过的链接;
:hover——鼠标悬停的元素;
:focus——获取焦点的元素;
:active——激活的元素(例如一个被单击的链接元素);
:first-child——作为其他元素第一个子元素的元素;
:lang()——根据元素的lang属性确定的元素。
CSS2.1中的伪元素有:
::first-line
::first-letter
::before
::after
那么区别在哪儿呢?区别就在于这些伪选择器影响文档的方式不同。伪类的表现有点儿像给文档添加类,而伪元素的效果就好像有元素被插入到了文档中。
以::first-letter为例,假设你要把每个h1的第一个字母增大到其他字母的两倍半(如图
2-1所示),很简单:
h1::first-letter {font-size: 250%;}
这就仿佛CSS和标记被修改成了这样:
h1 first-letter {font-size: 250%;}
<h1><first-letter>H</first-letter>owdy, y’all!</h1>
图2-1 放大h1的第一个字母
这真的是浏览器内部真实发生的情况吗?谁知道呢,反正结果确实像是发生了这种情况,因此才有了“伪元素”这个名字。
类似地,伪类的表现就像是它们使文档中的元素被添加了新的类。例如,想象一下若对于每一个作为其他元素第一个子元素的元素,浏览器给它们都附加了一个名为first-child的类,然后就可以像下面这样为它们应用样式了:
li.first-child {border-left: none;}
只需要简单地把点改成冒号就变成了li:first-child,就可以得到同样的最终效果,而不用费力把类添加到所有标记上。
有时还会看到伪元素使用双冒号的语法,这是在CSS2.1之后引入的。截至撰写本书之时,还没有哪个浏览器要求你必须在伪元素前面使用双冒号的,一个冒号就可以了。
另外提醒读者,CSS3增加了如下一些伪类,截至撰写本书之时,它们中的大部分还没有被广泛地支持:
:target
:root
:nth-child()
:nth-of-type()
:nth-last-of-type()
:first-of-type
:last-of-type
:only-of-type
:only-child
:last-child
:empty
:not()
:enabled
:disabled
:checked
2 为目标元素添加样式
当希望指向文档中的某个片段时,目标(target)会非常有用。什么,怎么实现?其实非常简单:
<a href="http://example.com/law.html#sec2-7">Section 2.7</a>
任何人单击这个链接(如果浏览器处理正确的话)将不止跳转到目标页,而且还会跳到页面中文档片段标识符(地址中的#sec2-7部分)出现的地方。这有时是通过锚点(anchor)实现的,但是只用ID来实现会更好一些。例如有如下两种场景:
<h3><a name="sec2-7">Exceptions</a></h3>
<h3 id="sec2-7">Exceptions</h3>
这两种情况下,当浏览器跳到了文档中的目标位置时,都不会有任何视觉提示告知你已经到达了目标位置,而使用:target伪类就可以给出视觉提示。例如,若想让作为某个文档片段标识符目标的h3拥有特定的提示效果(如图2-2所示),可以这样写:
h3:target {color: maroon;
background: #FFA;}
图2-2 突出显示目标元素(另见彩插图2-2)
当然,你或许想把这个样式应用到任何目标元素上,而不管它是什么元素,那么只需要把h3换成一个通用选择器即可,就像这样:
*:target {color: maroon;
background: #FFA;}
从技术上讲,这种情况下通用选择器也是可选的,可以把这个选择器简单地写成:target。
如果想让目标样式多一点Web 2.0的感觉,还可以设置一个渐隐背景的效果。你懂的,就是那种“你已经完成了某项操作,所以页面上的一块背景会从黄色变到白色,让你知道已经完成了该项操作”的效果。通过:target和一个动画GIF可以很容易地实现这种效果,只需要创建一个从黄色渐变到白色(如果白色是你网站的背景色的话)的动画并且把它当做背景图像。
*:target {background: url(/pix/yellow-fade.gif);}
3 特殊性
你很难快速地把特殊性(specificity)这个词读3遍,而若想彻底地掌握它甚至更难。但是,它却是理解CSS规则之间相互作用的关键。
特殊性是一个选择器“特殊程度”的数字表示,有3样东西经常被用来确定选择器的特殊性:
每个元素描述符贡献0,0,0,1;
每个类、伪类或者属性描述符贡献0,0,1,0;
每个ID描述符贡献0,1,0,0。
先不要抓狂,来看几个小例子。
div ul ul li 0,0,0,4 4个元素描述符
div.aside ul li 0,0,1,3 1个类描述符,3个元素描述符
a:hover 0,0,1,1 1个伪类描述符,1个元素描述符
div.navlinks a:hover 0,0,2,2 1个伪类描述符,1个类描述符,2个元素描述符
#title em 0,1,0,1 1个ID描述符,1个元素描述符
h1#title em 0,1,0,2 1个ID描述符,2个元素描述符
希望这些能够帮助你理解特殊性是如何计算的。那么,为什么是逗号呢?因为可以这么说,每个“级别”的特殊性的值都是相互独立的。因此,具有一个单独的类描述符的选择器会比由13个元素描述符组成的选择器拥有更高的特殊性。
.aside /* 0,0,1,0 */
div table tbody tr td div ul li ol li ul li pre /* 0,0,0,13 */
第一个选择器左数第三位的“1”胜过了第二个选择器同样位置的“0”,基于这样的事实,第二个选择器第四位的“13”(在这个非常有限的例子中)就毫无意义了。逗号可以使我们看得更清楚,否则选择器的特殊性可能被写成“10”和“13”,从而造成后者特殊性更高的假象(在CSS的早期还没有引入逗号分隔符时,这是经常出现的误解)。
还有另外一种常见的误解,就是特殊性的结构相近问题。例如,假设有如下两个选择器:
ul li {font-style: normal;}
html li {font-style: italic;}
它们当中哪个会赢呢?它们都有两个元素描述符,这意味着它们的特殊性都是0,0,0,2。其实,后写的会赢,与html元素相比ul元素在文档中的位置离li元素更近也不管用。特殊性只是单纯的数值,它不会以任何方式评估页面的结构。结果,列表元素将全部变成斜体,因为当特殊性相等时后声明的规则会胜出。
因为我说过有3样东西影响特殊性,所以你或许想知道特殊性标识符第一位的0是干嘛用的。其实,第一个0是用于行内样式(inline style)的,且仅用于行内样式。因此,如果有下面这样的样式和标记,则div的背景将会是蓝色。
div#header {background: purple;} /* 0,1,0,0 */
<div id="header" style="background: blue;"> <!-- 1,0,0,0 -->
——摘自《精彩绝伦的CSS》