象在问题2(第二个困难之处)中提到过的,CSS的规则匹配可能非常复杂。为了解决这个困难,规则需要被转换一下,以便更容易使用。
在解析完样式表之后,根据选择器,规则中加入了一个或多个哈希映射。它们通过id,通过类名,通过标签名映射,而一般的对任何东西的映射并不适用于这种类型。如果选择器是个id,会给id映射添加规则,如果是类的话会添加到类映射等等。
这种转换使得匹配规则容易的多了。不再需要查找每个声明—我们可以从映射中抽取出某个元素的规则。这种优化排除了95%以上的规则,因此在匹配过程中甚至都不需要考虑它们。
作为示例我们看看下面的样式规则:
p.error {color:red} #messageDiv {height:50px} div {margin:5px}
第一个规则会被插入类映射。第二个插入id映射,第三个插进标签映射。
对于以下的HTML片断:
<p class="error">an error occurred </p> <div id=" messageDiv">this is a message</div>
我们会首先试图找到p元素的规则。类映射会包含一个“error”键,在它下面是”p.error”规则。div元素在id映射(键是id)和标签映射中都会有相应规则。因此剩下的唯一工作是找出通过键值抽取出的规则哪一条真的匹配上。
例如如果div的的规则是:
table div {margin:5px}
它仍然会从标签映射中抽取出来,因为键是最右端的选择器,但它匹配不上我们的div元素,因为它没有我们的表前缀。
Webkit和Firefox都会做这种转换。
以正确的级联顺序应用规则
样式对象有与每个可视化属性(所有CSS属性,但是更常见一些)对应的属性。如果此属性没有被任何匹配上的规则定义—那么一些规则可以从父元素样式对象继承过来。另外的属性则使用缺省值。
当有超过一个定义的时候问题来了—需要用级联顺序来解决这个问题。
样式表级联顺序
一个样式属性可以在几个样式表中出现,同一个样式表中也能出现多次。这意味着应用样式规则的次序非常关键。这被称为“级联”顺序。根据CSS2规范,级联顺序是(从低到高):
1 浏览器定义
2 用户一般定义
3 编写者一般定义
4 编写者关键定义
5 用户关键定义
浏览器定义最不重要,用户定义覆盖编写者定义只在它被标注为关键时才会发生。相同级别的定义会先用特殊性,然后是它们被指定的顺序排序。HTML的可视化属性会被转换成匹配的CSS定义。他们被作为低优先级的编写者定义对待。
特殊性
选择器的特殊性在CSS规范中如下定义:
1 如果定义是一个“style”属性而非选择器的规则,那么计为1,否则是0(=a)
2 计算选择器中ID属性的个数(=b)
3 计算选择器中其它属性和伪类的个数(=c)
4 计算选择器中元素名和伪元素的个数(=d)
把4个数字连成a-b-c-d(以最大的数字进制为准)得到特殊性。
你需要使用的数字进制依你在分类中具有的最大值而定。
例如,如果a=14,你可以用16进制。在很少出现的情形下当a=17时你可能需要一个17进制。后面这种情形可能发生在象这样的选择器上:html body div div p …(选择器里有17个标签。。。不太可能发生)。
一些例子:
* {} /* 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 */
对规则排序
规则匹配上之后,它们依据级联规则排序。Webkit对小列表使用冒泡排序,对大的使用归并排序。Webkit通过对规则重载“>”操作符来实现排序:
static bool operator >(CSSRuleData& r1, CSSRuleData& r2) { int spec1 = r1.selector()->specificity(); int spec2 = r2.selector()->specificity(); return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2; }
渐进的处理
Webkit使用一个指示是否所有顶层样式表(包括@imports)已被加载的标识。如果在附加到文档时样式尚未完全加载—放置暂停标识并且在文档里做标记,它们会在样式表加载后重新计算。
(待续)
原创文章,作者:苏葳,如需转载,请注明出处:https://www.swmemo.com/2084.html