打印

KB005: CSS 层叠

作者:武利剑

CSS 层叠及样式表来源

Web标准化运动的口号——分离、分离、分离。

在2003年的 SXSW 会议中, Steve Champeon 和 Nick Finck 做了一个名为“面向未来的全方位 Web 设计”的演讲,揭示了这种 Web 开发新方法的蓝图。 Steve 还给它取了个名称:渐进增强(Progressive Enhancement)。

从内容花生开始,将其标记为富含语义的 (X)HTML,接着给内容裹上一层富含奶油的 CSS。 最后,添加 JavaScript 作为糖果硬壳,这就做成了一颗可口无比的美味(并使得它不会在你手里融化)。1

CSS在其中扮演及其重要的角色。CSS 是 Cascading Style Sheets 的缩写,即层叠样式表。 为什么叫做“层叠样式表”呢?这跟它的特征有关系:层叠。

样式表可能有三种不同的来源:作者、用户和用户端。通俗的讲,作者就是开发者,用户就是使用浏览器的人,用户端就是浏览器。

这三种来源的样式表可能在范围上有重叠。比如,都设置了 P 元素的背景色和字体颜色。它们根据层叠的规则互相作用。

CSS 层叠对每一个样式规则2指定一个权重。如果要应用若干个规则,那么权重最大的那个规则具有优先权。

缺省时,作者样式表规则较用户样式表规则优先级高。不过,对于 "!important" 规则,保留其优先级。所有的用户规则和作者规则的权重都比浏览器缺省样式表中规则的权重大。

用户端样式表

来自浏览器的样式,被称作 UA style,是浏览器默认的样式。 比如,对于 DIV 元素,浏览器默认其 'display' 的特性值是 "block",而 SPAN 是 "inline"。

在 Firefox 的地址栏中输入:resource://gre/res/html.css,即可得到 Firefox 的浏览器默认样式表。

用户样式表

这个样式表是使用浏览器的用户,根据自己的偏好设置的样式表。

比如,用户希望所有 P 元素中的字体都默认显示成蓝色,可以先定义一个样式表,存成 css 文件。CSS 代码:

p{ color:blue; }

然后按下面的说明来设置即可。

1). IE

创建一个 CSS 文件,定义样式。然后,按如下图示,设置用户样式表,选择浏览,指向自定义样式。

IE 中设置用户样式表

2). Firefox

按以下步骤:

  1. 找到 Firefox 的 profile,Windows 下在这里:用户名\AppData\Roaming\Mozilla\Firefox\Profiles\dbsujefc.default\chrome。
    也可以这样打开: 帮助-->疑难排解信息-->应用基础-->打开所在文件夹-->chrome;
  2. 创建一个名为 userContent.css 的 CSS 文件,编辑加入所需要的样式。注意名字必须叫 userContent.css,不能更改。否则无效;
  3. 保存 userContent.css 文件然后重启 Firefox。

3). Chrome

Chrome 定义用户样式的方式暂未找到。

4). Safari

创建一个 CSS 文件,定义样式。然后,按如下图示,设置用户样式表。

Safari 中设置用户样式表

5). Opera

创建一个 CSS 文件,定义样式。然后,按如下图示,设置用户样式表。

Opera 中设置用户样式表

作者样式表

即开发者在开发网页时,所定义的样式表。

注:

  1. 以上内容译自Understanding Progressive Enhancement。最后一句话的幽默源自著名的巧克力广告词:只溶于口,不溶于手!
  2. 这里说到的“规则”,通俗的讲,就是定义的样式,或者 CSS 声明

!important 规则1

根据 CSS2.1 规范中的描述,'!important' 可以提高样式的优先级,它对样式优先级的影响是巨大的。 注意,'!important' 规则在 IE7 以前的版本中是被支持不完善。因此,经常被用作 CSS hack2

为了平衡开发者设置的样式和浏览器用户设定的样式,默认开发者的样式优于浏览器用户设置的样式;另外,声明了 '!important' 的样式优于普通声明。

注:

  1. 关于 '!important' 的详细信息,请参考 CSS2.1 规范 6.4.2 !important rules 中的内容;
  2. 见:

CSS 层叠顺序( Cascading order )

为了找到元素/特性组合的值,用户端必须应用如下的排列顺序:

  1. 对于目标媒介类型 (media type),找到存有疑问的元素和属性的所有声明。如果相关联的选择器匹配存有疑问的元素,并且目标媒介匹配包含声明并且和样式表的路径在所有链接上达到一致的 @media 规则中所有的媒介列表。
  2. 根据 CSS 样式的来源和重要性(是否含 !important ),给出了优先级的升序排列:
    • 用户端声明( UA declarations )
    • 一般用户声明( user normal declarations )
    • 一般作者声明( author normal declarations )
    • 加了 '!important' 的作者声明( author important declarations )
    • 加了 '!important' 的用户声明( user important declarations )
  3. 拥有相同重要性和来源的规则,按照 CSS specificity 来排序。此处,需要注意一下层叠顺序和选择器的特殊性1的关系。 选择器的特殊性是在相同来源,相同重要性的规则之间判定最终哪个规则会起作用。 比如, 同是开发者自己定义的样式,并且,没有使用 "!important" 规则,这样的样式才可以计算特殊性。
  4. 最后,根据先后次序来排列:如果两条规则具有相同的权重,相同的来源和相同的选择器特殊性,则后出现的规则超越先出现的规则。 引入的样式表( @import )中的规则被认为出现在样式表本身的所有规则之前。

例:

用户自定义样式,userContent.css:

p{
    background-color:black;
    color:red !important;
}

按照上节中的做法,给浏览器设置用户样式。

测试页面,test.html:

<style>
    p {
        color: green !important;
    }
</style>
<p style="background-color : white; color : blue;"> hello!! </p>

可自行测试结果。

注:

  1. 选择器的特殊性,即下节的 Selector's specificity

计算选择器的特殊性( Selector's specificity )

正如上面提到的,根据层叠顺序,优先级相同的样式,如何判断哪一条声明会起作用,取决于对其选择器特殊性的计算值。

例如,都是作者样式,并且没有使用 '!important' 规则:

<!DOCTYPE HTML>
<style type="text/css">
    div {
        width: 100px;
        height: 100px;
    }
    #c1 #c2 div.con {
        background-color: yellow;
    }
    div {
        background-color: black;
    }
    #c2 div {
        background-color: blue;
    }
    #c2 #content {
        background-color: red;
    }
</style>
<div id="c1">
    <div id="c2">
        <div id="content" class="con"></div>
    </div>
</div>

如上代码中,多个样式中的 'background-color' 同时作用于 content,那么最后,到底 content 应该会是什么颜色呢?

这就应该求助于选择器特殊性的计算规则了。

特殊性的值可以看作是一个由四个数组成的一个组合,用 a,b,c,d 来表示它的四个位置。 依次比较 a,b,c,d 这个四个数比较其特殊性的大小。比如,a 值相同,那么 b 值大的组合特殊性会较大,以此类推。 注意,W3C 中并不是把它作为一个 4 位数来看待的。

a,b,c,d 值的确定规则:

  1. 如果 HTML 标签的 'style' 属性中该样式存在,则记 a 为 1;
  2. 数一下选择器中 ID 选择器的个数作为 b 的值。比如,以上样式中包含 '#c1' 和 '#c2' 的选择器;
  3. 其他属性以及伪类(pseudo-classes)的总数量是 c 的值。比如,上面例子中的 '.con',':hover' 等;
  4. 元素名和伪元素的数量是 d 的值;比如上面例子中的 ‘div’。

现在,应用上面的规则,计算例子中各个样式的特殊性的值,结果为:

<!DOCTYPE HTML>
<style type="text/css">
    div {
        width: 100px;
        height: 100px;
    }
    #c1 #c2 div.con {     /* a=0 b=2 c=1 d=1 -> specificity = 0,2,1,1 */
        background-color: yellow;
    }
    div {                 /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
        background-color: black;
    }
    #c2 div {             /* a=0 b=1 c=0 d=1 -> specificity = 0,1,0,1 */
        background-color: blue;
    }
    #c2 #content {        /* a=0 b=2 c=0 d=0 -> specificity = 0,2,0,0 */
        background-color: red;
    }
</style>
<div id="c1">
    <div id="c2">
        <div id="content" class="con"></div>
    </div>
</div>

可见,'#c1 #c2 div.con" 的特殊性( [0,2,1,1] )最高,是背景色应该是黄色。

W3C 官方给出的例子:

*             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
------------------------------------------------------------------------
<HEAD>
<STYLE type="text/css">
 #x97z { color: red }
</STYLE>
</HEAD>
<BODY>
<P ID=x97z style="color: green">
</BODY>

如上代码中,P 中字体的颜色应该为绿色。

非 CSS 显示的优先级

用户端应该优先考虑那些不是来自样式表的显示属性。如果是这样,这些非CSS呈现提示必须被转换到相应的CSS规则, 且其特殊性为零( 既,使其特殊性最低 ),并认为它的引入处于样式表的最顶端。它们可能被后续的样式规则所覆盖,

在 HTML 中,任何不属于以下列表中的属性都需要以显示属性对待:abbr, accept-charset, accept, accesskey, action, alt, archive, axis, charset, checked, cite, class, classid, code, codebase, codetype, colspan, coords, data, datetime, declare, defer, dir, disabled, enctype, for, headers, href, hreflang, http-equiv, id, ismap, label, lang, language, longdesc, maxlength, media, method, multiple, name, nohref, object, onblur, onchange, onclick, ondblclick, onfocus, onkeydown, onkeypress, onkeyup, onload, onload, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onreset, onselect, onsubmit, onunload, onunload, profile, prompt, readonly, rel, rev, rowspan, scheme, scope, selected, shape, span, src, standby, start, style, summary, title, type (except on LI, OL and UL elements), usemap, value, valuetype, version.

例如,对于 align1 属性,各浏览器应当将它转换成 CSS 中的浮动。

注:

  1. 相关问题和解释,请参见:W3Help -- RX8015: IE6 IE7 IE8(Q) 没有完全正确地将 IMG、OBJECT、IFRAME、TABLE 元素的 align="left|right" 理解为浮动

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6.10
Chrome 7.0.517.0 dev
Safari 5.0.2
Opera 10.62
测试页面:  
本文更新时间: 2010-09-15

关键字

CSS 层叠 !important 规则 样式表来源 层叠顺序 特殊性 选择器 非CSS 显示