4.2 样式的层叠和继承
要想掌握样式表,弄清样式层叠和继承这两个概念是关键。浏览器根据层叠和继承规则确定显示一个元素时各种样式属性采用的值。每个元素都有一套浏览器呈现页面时要用到的CSS属性。对于每一个这种属性,浏览器都需要查看一下其所有的样式来源。前面已经讲过三种定义样式的方式(元素内嵌、文档内嵌和外部样式表),但是要知道,样式还有另外两个来源。
4.2.1 浏览器样式
浏览器样式(更恰当的名称是用户代理样式)是元素尚未设置样式时浏览器应用在它身上的默认样式。这些样式因浏览器而略有差异,不过大体一致。以a元素(超链接)为例,想想没有特别为它定义样式时浏览器会怎样显示。代码清单4-10所示为一个不含任何样式的简单HTML文档。
代码清单4-10 不含样式的HTML文档
<! DOCTYPE HTML> <html> <head> <title>Example</title> </head> <body> <a href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a href="http://w3c.org">Visit the W3C website</a> </body> </html>
该例只是在前面例子的基础上做了一点改动,去掉了所有样式。从图4-7中可以看到浏览器是如何显示a元素的。
图4-7 超链接元素的默认样式
我们对浏览器应用于链接的样式早已习以为常,以至于不会留意到它的存在。但是只要驻目细看,样式的细节就会浮现出来。链接的文字内容被显示为蓝色,而且带有下划线。据此推测,浏览器相当于应用了类似代码清单4-11所示的样式。
代码清单4-11 推测出来的a元素的默认浏览器样式
a { color: blue; text-decoration: underline; }
虽然不是每一个HTML元素都有默认的浏览器样式,但是多数元素都有。本书介绍HTML元素的各章都会介绍常见浏览器一般都会应用的典型默认样式。关于a元素的介绍参见第8章。
4.2.2 用户样式
大多数浏览器都允许用户定义自己的样式表。这类样式表中包含的样式称为用户样式。这个功能用的人不多,不过,那些要定义自己的样式表的人往往比较看重这一点,一个特别的原因是这可以让有生理不便的人更容易使用网页。
各种浏览器都有自己管理用户样式的方式。以谷歌的Chrome为例,它会在用户的个人配置信息目录中生成一个名为Default\User StyleSheets\Custom.css的文件。添加到这个文件中的任何样式都会被用于用户访问的所有网站,只不过要依下节所讲的层叠规则行事。代码清单4-12所示简例展示了我在自己的Custom.css文件中加入的一条样式。
代码清单4-12 在用户样式表中添加样式
a { color: white; background:grey; text-decoration: none; padding: 2px; }
这条样式的作用对象是a元素,它盖过了默认的浏览器样式。图4-8显示了重新载入代码清单4-10中的HTML文档后上述用户样式的效果。
图4-8 定义用户样式
4.2.3 样式如何层叠
明白了浏览器所要查看的所有样式来源之后,现在可以看看浏览器要显示元素时求索一个CSS属性值的次序。这个次序很明确:
(1)元素内嵌样式(用元素的全局属性style定义的样式);
(2)文档内嵌样式(定义在style元素中的样式);
(3)外部样式(用link元素导入的样式);
(4)用户样式(用户定义的样式);
(5)浏览器样式(浏览器应用的默认样式)。
设想用户需要显示一个a元素。浏览器需要知道的一件事情是其文字应显示为哪种颜色。为了解决这个问题,它需要为CSS属性color找到一个值。首先它会查看所要呈现的那个元素是否具有一条设定了color值的元素内嵌样式。比如下面这种:
<astyle="color:red"href="http://apress.com">Visit the Apress website</a>
如果不存在元素内嵌样式,那么接下来浏览器会看看是否有一个style元素包含着作用于那个元素的样式。比如下面这种:
<style type="text/css">
a {
color: red;
}
</style>
如果不存在这样的style元素,那么浏览器接下来会查看用link元素载入的样式表。依次类推,直到找到一个color属性值或查完用户定义样式。在后面一种情况下,最终使用的将是浏览器默认样式中的值。
前述次序表中的前三个属性来源(元素内嵌样式、文档内嵌样式和外部样式表)合称作者样式。定义在用户样式表中的样式称为用户样式。由浏览器定义的样式则称为浏览器样式。
4.2.4 用重要样式调整层叠次序
把样式属性值标记为重要(important),可以改变正常的层叠次序,如代码清单4-13所示。
代码清单4-13 将样式属性标记为重要
<! DOCTYPE HTML> <html> <head> <title>Example</title> <style type="text/css"> a { color: black !important; } </style> </head> <body> <astyle="color:red"href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a href="http://w3c.org">Visit the W3C website</a> </body> </html>
在样式声明后附上!important即可将对应属性值标记为重要。不管这种样式属性定义在什么地方,浏览器都会给予优先考虑。从图4-9中可以看到重要属性的效果(印刷页面上看起来可能不太清楚)。此例中文档内嵌样式的color属性值盖过了元素内嵌样式中的值。
图4-9 重要属性值盖过元素内嵌样式属性值
提示
能凌驾于作者定义的重要属性值之上的只有用户样式表中定义的重要属性值。对于普通属性,作者样式中的值优先于用户样式中的值,而对于重要属性情况正好相反。
4.2.5 根据具体程度和定义次序解决同级样式冲突
如果有两条定义于同一层次的样式都能应用于同一个元素,而且它们都包含着浏览器要查看的CSS属性值,这时就需要另加砝码助天平上持平的双方一决高下。为了判断该用哪个值,浏览器会评估两条样式的具体程度,然后选中较为特殊的那条。样式的具体程度通过统计三类特征得出:
(1)样式的选择器中id值的数目;
(2)选择器中其他属性和伪类的数目;
(3)选择器中元素名和伪元素的数目。
第17章和第18章将会说明如何编写包含所有这些特征的选择器。浏览器将三类评估所得值结合起来,由此辨识出最特殊的样式并采用其属性值。代码清单4-14是有关具体程度的一个非常简单的例子。
代码清单4-14 样式的具体程度
<! DOCTYPE HTML> <html> <head> <title>Example</title> <style type="text/css"> a { color: black; } a.myclass { color:white; background:grey; } </style> </head> <body> <a href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a class="myclass" href="http://w3c.org">Visit the W3C website</a> </body> </html>
在评定具体程度时要按a-b-c的形式(其中每一个字母依次代表上述三类特征的统计结果)生成一个数字。它不是一个三位数。如果对某个样式算出的a值最大,那么它就是具体程度高的那个。只有a值相等时浏览器才会比较b值,此时b值较大的样式具体程度更高。只有a值和b值都分别相等时浏览器才会考虑c值。也就是说,1-0-0这个得分比0-5-5这个得分代表的具体程度更高。
本例中a.myclass这个选择器含有一个class属性,于是该样式的值为0-1-0(0个id值+1个其他属性+0个元素名)。另一条样式的具体程度值为0-0-0(因为它不包含id值、其他属性或元素名)。因此浏览器在呈现被归入myclass类的a元素时将使用a.myclass样式中设定的color属性值。对于所有其他a元素,使用的则是另一条样式中设定的值。从图4-10可以看出在本例中浏览器是如何选择和应用样式的。
图4-10 根据具体程度选用样式属性值
如果同一个样式属性出现在具体程度相当的几条样式中,那么浏览器会根据其位置的先后选择所用的值,规则是后来者居上。代码清单4-15所示例子就包含了两条具体程度相同的样式。
代码清单4-15 具体程度相同的样式
<! DOCTYPE HTML> <html> <head> <title>Example</title> <style type="text/css"> a.myclass1 { color: black; } a.myclass2 { color:white; background:grey; } </style> </head> <body> <a href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a class="myclass1 myclass2" href="http://w3c.org">Visit the W3C website</a> </body> </html>
此例style元素中定义的两条样式在具体程度上得分相同。浏览器在呈现页面上的第二个a元素时,为样式属性color选用的值是white,这是因为该值来自靠后的那条样式。其结果如图4-11所示。
图4-11 根据样式定义的先后选择属性值
为了证实浏览器是用这个方法选择所用的color属性值,代码清单4-16中的例子颠倒了两条样式的位置。
代码清单4-16 颠倒样式定义的次序
<! DOCTYPE HTML> <html> <head> <title>Example</title> <style type="text/css"> a.myclass2 { color:white; background:grey; } a.myclass1 { color: black; } </style> </head> <body> <a href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a class="myclass1 myclass2" href="http://w3c.org">Visit the W3C website</a> </body> </html>
不出所料,现在浏览器为color属性选择的是black这个值,如图4-12所示。
图4-12 改变样式定义次序的效果
根据样式的具体程度和定义次序确定选用的样式属性值,应该把各个属性分开考虑。本节的几个例子还定义了背景颜色属性的值。因为该值并非两个样式中都有,所以不会发生冲突,也就没有必要查找其他值。
4.2.6 继承
如果浏览器在直接相关的样式中找不到某个属性的值,就会求助于继承机制,使用父元素的这个样式属性值。如代码清单4-17所示。
代码清单4-17 CSS属性继承
<! DOCTYPE HTML> <html> <head> <title>Example</title> <style type="text/css"> p { color:white; background:grey; border: medium solid black; } </style> </head> <body> <a href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a class="myclass1 myclass2" href="http://w3c.org">Visit the W3C website</a> </body> </html>
本例关注的是浏览器应用于span元素(其父元素为p)的样式。图4-13是这个文档在浏览器中的显示效果。
图4-13 使用继承的CSS属性值
这个文档并没有在针对span元素的样式中设定color属性值,但是浏览器显示该元素的文字内容时却使用了前景色white。这个值系由父元素p继承而来。
令人挠头的是,并非所有CSS属性均可继承。这方面有条经验可供参考:与元素外观(文字颜色、字体等)相关的样式会被继承;与元素在页面上的布局相关的样式不会被继承。在样式中使用inherit这个特别设立的值可以强行实施继承,明确指示浏览器在该属性上使用父元素样式中的值。代码清单4-18示范了inherit的用法。
代码清单4-18 使用inherit
<! DOCTYPE HTML> <html> <head> <title>Example</title> <style type="text/css"> p { color:white; background:grey; border: medium solid black; } span { border: inherit; } </style> </head> <body> <a href="http://apress.com">Visit the Apress website</a> <p>I like <span>apples</span> and oranges.</p> <a class="myclass1 myclass2" href="http://w3c.org">Visit the W3C website</a> </body> </html>
此例定义了一条用于span元素的样式,其border属性值继承自父元素。文档的显示效果见图4-14。现在span元素和包含它的p元素均具有边框。
图4-14 使用inherit