2.9 层叠
好啦,大家现在的背景知识已经足够理解层叠了。层叠,就是层叠样式表中的层叠,是一种样式在文档层次中逐层叠加的过程,目的是让浏览器面对某个标签特定属性值的多个来源,确定最终使用哪个值。
层叠是CSS的核心机制,理解了它才能以最经济的方式写出最容易改动的CSS,让文档外观在达到设计要求的同时,也给用户留下一些空间,让他们能根据需要更改文档的显示效果(比如整体调整字号)。
2.9.1 样式来源
样式有多处来源。首先,如果告诉你浏览器有一个默认的样式表,你应该不会感到奇怪,因为你还没有写一行CSS呢,每个标签已经带了一定的样式。h1
是不是粗体,字号还挺大?em
是不是斜体?列表呢,是不是缩进而且还带项目符号或编号?
然后,有一个用户样式表。没错,用户也可以提供样式表,尽管这样的用户不多见。这个选择对于视障用户很有用,他们可以通过用户样式表,强制浏览器加载的所有网站都以更大的字号,更容易分辨的颜色显示内容。比如,某个视障用户可以增加如下样式:
body {font-size:200%;}
这样就把字号调大了一倍,继承同样也会起作用。
再有,就是作者样式表,也就是网页设计师(你)写的样式表。前面我们已经讲了作者给网页添加样式的三种方法:链接样式、嵌入样式和行内样式。
参见2.1节中的“为文档添加样式的三种方法”。
以下就是浏览器层叠各个来源样式的顺序:
- 浏览器默认样式表
- 用户样式表
- 作者链接样式表(按照它们链接到页面的先后顺序)
- 作者嵌入样式
- 作者行内样式浏览器会按照上述顺序依次检查每个来源的样式,并在有定义的情况下,更新对每个标签属性值的设定。整个检查更新过程结束后,再将每个标签以最终设定的样式显示出来。
举例来说,如果作者的链接样式表将p
的字体设定为Helvetica,而页面中有一条嵌入规则以相同的选择符把字体设定为Verdana,那么段落文本最终会以Verdana字体显示。因为浏览器是在读取链接样式表之后读取嵌入样式。但要是用户和作者样式表都没有为段落指定字体,就会使用浏览器默认样式表中指定的Times。
在接下来了解了层叠规则,知道了具体如何确定给某个页面元素应用何种样式之后,你对层叠的理解就会更加透彻。
2.9.2 层叠规则
以下是层叠机制的相关规则。
要了解有关层叠的更多信息,请参考这个链接:http://www.w3.org/TR/CSS2/cascade.html。
层叠规则一:找到应用给每个元素和属性的所有声明。浏览器在加载每个页面时,都会据此查到每一条CSS规则,标识出所有受到影响的HTML元素。
层叠规则二:按照顺序和权重排序。浏览器依次检查5个来源,并设定匹配的属性。如果匹配的属性在下一个来源也有定义,则更新该属性的值,如此循环,直到检查完页面中所有标签受影响属性的全部5个来源为止。最终某个属性被设定成什么值,就用什么值来显示。
声明也可以有权重。可以像下面这样为单独的声明增加权重:
p {color:green !important; font-size:12pt;}
空格!important分号(;)
用于加重声明的权重。
这条规则加重了将文本设置为绿色的权重。于是,就算层叠的下一来源给段落设定了其他颜色,最终的颜色值仍然还是绿色。说到底,就是一种特权,相当于你下了命令:就应用这个样式啦,其他来源一概不用考虑。不过,在使用这个特权之前,一定要知道你的这个!important
声明,很可能会让用户的个人设定不起作用,而用户的设定对他可能还非常重要。每次当你有使用这个特权的冲动时,最好先静下心来仔细分析一下自己的CSS,多数情况下都应该可以想出一种更好的替代方案。就我个人而言,我是基本上不用!important
声明的。
层叠规则三:按特指度排序。除了有点拗口之外,特指度(specificity)其实表示一条规则有多明确。如果没有特指度的考量,那为了让恰当的样式起作用,恐怕我们就免不了要频繁变换样式表中规则的顺序了。
我们知道,如果某个样式表中包含如下规则:
p {font-size:12px;}
p.largetext {font-size:16px;}
那么下面的段落
<p class="largetext">A bit of text</p>
将显示16像素高的文本,因为第二条规则的选择符既包含标签名,也包含类名,所以意义更明确(特指度更高),结果第二条规则会覆盖第一条规则中的同名属性。这个例子似乎太明显了,如果还是对同一个段落,有如下样式呢?
p {font-size:12px;}
.largetext {font-size:16px;}
答案是尽管两条规则都匹配同一个标签,但使用类选择符的规则胜出,文本还是16像素高。为什么呀?因为类名选择符比普通的标签选择符具有更高的特指度。一条规则的特指度,由它的选择符中包含多少个标签、类名和ID决定。
2.9.3 计算特指度
下面我们具体讲一讲怎么计算选择符的特指度。首先,有一个简单的记分规则,即对每个选择符都要按下面的“ICE”公式计算三个值:
I - C - E
ICE并非真正的三位数,只不过大多情况下把结果看成一个三位数没有问题,三位数最大的胜出。但是,千万得知道0-1-12与0-2-0相比,仍然是0-2-0的特指度更高。
三个字母间的短横线是分隔符,并非减号。针对这个公式的计分办法如下:
- 选择符中有一个ID,就在I的位置上加1;
- 选择符中有一个类,就在C的位置上加1;
- 选择符中有一个元素(标签)名,就在E的位置上加1;
- 得到一个三位数。好了,下面通过几个例子来理解特指度。
P 0-0-1特指度=1
p.largetext 0-1-1特指度=11
p#largetext 1-0-1特指度=101
body p#largetext 1-0-2特指度=102
body p#largetext ul.mylist 1-1-3特指度=113
body p#largetext ul.mylist li 1-1-4特指度=114
在此,每个选择符都比前一个选择符的特指度更高。
层叠规则四:顺序决定权重。如果两条规则都影响某元素的同一个属性,而且它们的特指度也相同,则位置最靠下(或后声明)的规则胜出。1
1 建议大家可以参考本书一位读者“流年”的文章“CSS优先级特性”:http://liunian.info/css-specificity.html。
嗯,亲爱的读者,层叠的概念确实不太好理解。特别是,如果你的CSS经验还不够多,那理解起来就更不容易了。不过,我总结了一个简化版的层叠规则(见下面“查理版简单层叠要点”),不仅适用于任何情况,而且也更容易记住。
查理版简单层叠要点
在这个查理版里,只要记住三条规则就够了。这三条规则适合所有情况。
规则一:包含ID的选择符胜过包含类的选择符,包含类的选择符胜过包含标签名的选择符。
规则二:如果几个不同来源都为同一个标签的同一个属性定义了样式,行内样式胜过嵌入样式,嵌入样式胜过链接样式。在链接的样式表中,具有相同特指度的样式,后声明的胜过先声明的。
规则一胜过规则二。换句话说,如果选择符更明确(特指度更高),无论它在哪里,都会胜出。
规则三:设定的样式胜过继承的样式,此时不用考虑特指度(即显式设定优先)。下面简单解释一下规则三。比如下面的标记
- <div id="cascade_demo"> <p id="inheritance_fact">Inheritance is <em>weak</em> in the Cascade</p></div>
和下面的规则
- div#cascade_demo p#inheritance_fact {color:blue;}2 - 0 - 2 (高特指度)
会导致单词“weak”变成蓝色,因为它从父元素
p
那里继承了这个颜色值。但是,只要我们再给
em
添加一条规则
- em {color:red;}0 - 0 - 1 (低特指度)
em
就会变成红色。因为,虽然它的特指度低(0-0-1),但em
继承的颜色值,会被为它明确(显式)指定的颜色值覆盖,就算(隐式)遗传该颜色值的规则的特指度高(2-0-2)也没有用。