CSS选择器优先级

用户代理将样式规则应用到HTML中,不仅要考虑继承性,还要考虑选择器之间的特殊性,这种特殊性直接决定了样式的优先级。

继承是从一个元素向其后代元素传递属性值所采用的机制。

确定应当向一个元素应用哪些值时,用户代理不仅考虑继承,还要考虑特殊性,另外需要考虑声明本身的来源,这个过程就称为层叠。

上面总结起来就是:

  • 在同一来源中根据其选择器特殊性排列出优先级。
  • 再将不同来源(外部 < 内部 < 内联)按优先级排序。

何为特殊性

有时候匹配同一个匹配结果的选择器可以有很多种写法:

h1 { color: red; }
body h1 { color : green; } //胜利✌️

如以上CSS样式规则,最终h1到底是红色还是绿色,再比如:

#info { background: blue; } ///胜利✌️
p { background: red; }
<p id="info">一段重要信息。</p>

那么请问,id为info的段落标签p的最终背景颜色是红色还是绿色呢?

要想解决这种冲突的问题,就必须引入选择器特殊性的概念!

很显然,上面的两个例子中的一对声明中最终只能有一个结果(前提是他们的同一属性名冲突了)。答案就在于每个选择器的特殊性(specificity),对于每个规则,用户代理会计算选择器的特殊性,并将这个特殊性附加到规则中的每个声明。如果一个元素有两个或多个冲突的属性声明,那么有最高特殊性的生命就会胜出。

实际上,选择器特殊性并不能解决全部的冲突情况,所有样式冲突的解决都由层叠来处理。

选择器的特殊性由选择器本身的组件确定,特殊性值表述为4个部分,如:

[0101]

选择器具体特殊性的规则如下:

  • 对于选择器中给定的各个ID属性值,加 0,1,0,0 。
  • 对于选择器中给定的各个类属性值、属性选择器或伪类,加 0,0,1,0 。
  • 对于选择器中给定的各个元素和伪元素,加 0,0,0,1 。伪元素是否具有特殊性?在这方面CSS2有些自相矛盾,不过CSS2.1很清楚的指出,伪元素具有特殊性,而且特殊性为 0,0,0,1,同元素特殊性相同。
  • 结合符(+ > [] ^= $=等等特殊符号)和通配符(*)对特殊性没有任何贡献,此外通配符的特殊性为 0,0,0,0。全是0有什么意义呢?当然有意义!子元素继承祖先元素的样式根本没有特殊性,因此当出现这种情况后,通配符选择器定义的样式声明也要优先于子元素继承来的样式声明。因为就算特殊性是0,也比没有特殊性可言要强。

一旦确定一个选择器的特殊性,这个值将授予其所有相关声明:

h1 { color: silver; background: black; }

上面h1的特殊性表示为 0,0,0,1 。这个值将授予其所有声明——各个声明得到的特殊性也就是 0,0,0,1 。如下:

h1 { color: silver; }
h1 { background: black; }

答疑解惑

ID选择器与属性为id的属性选择器

上边介绍特殊性规则时指出——ID选择器的特殊性为0,1,0,0 ,属性选择器的特殊性为0,0,1,0 。

如果属性选择器为id呢:

p[id = 'total'] { color: red; }
#total { color: blue; } //胜利✌️

显然,尽管出现这种会疑惑的情况,依然可以套用特殊性规则。

第一个0的空缺留给了谁?

0,0,0,0 。这四个0中,后四个0分别被ID、属性和元素选择器占据,那么第一个0的空缺留给了谁?——内联样式!

第一个0是为内联样式声明保留的,他比所有其他声明的特殊性都要高:

h1 { color: red; }
<h1 style="color: blue;">hi hi~~</h1>  //胜利✌️

显然,上方示例中,第一个选择器特殊性为0,0,0,1 。第二种内联样式的写法特殊性为1,0,0,0 。所以内联样式胜出。

常规是用来打破的,内联样式也得靠边站!

有时某个声明非常重要,超过了其他声明,CSS2.1称之为重要声明:

h1 { color: red !important; }    //胜利✌️
<h1 style="color: blue;">hi hi~~</h1>

在重要声明的结束分号之前插入 !important 来标志该声明的重要性。

重要性的声明并没有特殊的特殊性值,不过要与非重要声明分开考虑。实际上,所有!important声明会分组在一起,重要声明的特殊性冲突会在重要声明的分组中解决,而不会与非重要声明相混,如此以来便清晰明了。

类似的,所有非重要声明也归为一组,在该组中使用特殊性解决冲突。如果重要声明与非重要声明发生冲突,那么不是声明之间的胜出,而是不同组(重要声明组、非重要声明组)之间的胜出,胜出的总是重要声明组中的声明。

层叠

CSS所基于的方法就是让样式层叠在一起,这是通过结合继承和特殊性做到的。

规则总应该是简单纯粹的,层叠遵循的规则如下:

  • 找出所有相关规则,这些规则都包含与一个给定元素匹配的选择器。
  • 按显示权重对应到该元素的所有声明排序。标志!important的规则的权重要高于没有!important标志的规则。按来源对应到给定元素的所有声明排序。共有3种来源:创作人员、读者和用户代理。正常情况下,创作人员的样式要胜过读者的样式。有!important标志的读者样式要强于所有其他样式,这包括有!important标志的创作人员样式。创作人员样式和读者样式都比用户代理的默认样式要强。
  • 按特殊性对应用到给定元素的所有声明排序。有较高特殊性的元素权重要大于有较低特殊性的元素。
  • 按出现顺序对应用到给定元素的所有声明排序。一个声明在样式表或文档中越后出现,它的权重就越大。如果样式表中有导入的样式表,一般认为出现在导入样式表中的声明在前,主样式表中的所有声明在后。

精炼的总结起来:

  • 读者的重要声明
  • 创作人员的重要声明
  • 创作人员的重要声明
  • 读者的正常声明
  • 用户代理声明

创作人员通常只需考虑前4个权重级别,因为任何声明都会胜过用户代理样式。


本篇完 ·END·

一个小小的样式规则的应用,用户代理都要做如此繁琐的排序与计算,因此,我们的CSS要尽可能的简便而使用,站在用户代理的角度,在保证实现效果的前提下,极大的减少复杂选择器的使用,以最少的用户代理的计算时的时间复杂度,从而提升渲染性能。

版权声明 » 自由转载-署名-非商用-相同方式共享

作者署名 » 陈帅华-探索技术艺术与国学之美

发布日期 » 2017年12月6日 星期三

我要留言