题图、摄影:锦此
当我们在讨论CSS选择器优先级的时候,我们再讨论什么?
其实很多人都对此有点模糊,那我换个方式问: 一个CSS属性的最终值是怎么来?
回答 : CSS属性的最终值是通过层叠计算得来的。
那什么是层叠计算呢?
我通俗的理解,其实就是先计算再重叠。
层叠是CSS的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在CSS处于核心地位,CSS的全称层叠样式表正是强调了这一点。
计算过程
计算的过程指的是用户代理(浏览器只是用户代理的一种“实例”)在渲染HTML的时候,对CSS进行层叠计算的过程(这里不讨论浏览器的渲染、重绘等触发时机)。
为了方便理解,这里只针对一个属性值(padding
)进行讨论,其他的属性值都是一样的过程。
demo:
<div class="taobao_com" id="taobao_com" data-show="true">
<div class="taobao"></div>
<p>taobao.com</p>
</div>
div{
padding:1px;
}
.taobao_com{
padding:12px;
}
div .taobao{
padding:123px;
}
.taobao_com .taobao{
padding:1234px;
}
在属性的计算之前,会对每个文档元素的每个属性上的值进行排序 (W3C文档地址):
1. Transition declarations [CSS3-TRANSITIONS]
2. Important user agent declarations
3. Important user declarations
4. Important override declarations [DOM-LEVEL-2-STYLE]
5. Important author declarations
6. Animation declarations [CSS3-ANIMATIONS]
7. Normal override declarations [DOM-LEVEL-2-STYLE]
8. Normal author declarations
9. Normal user declarations
10.Normal user agent declarations
排序靠前的会比靠后的优先级要高。 1 > 2 > 3 > 4 > 5 ...
假设我们的用户代理是Chrome浏览器,那么当我们要计算A:<div class="taobao"></div>
的padding
值的时候,会有这么一个排序过程(简化):
1. 浏览器中对该标签的``padding``默认值
2. 开发者对该标签的 ``padding`` 进行声明的值
3. 浏览器中对该标签的 ``padding`` 进行 **!important** 声明的值
4. 开发者对该标签的 ``padding`` 进行 **!important** 声明的值
5. 开发者在CSS动画中对 ``padding`` 进行声明的值
那么在demo里面,分别写了4条padding
的的声明,但都会被排在层叠顺序的开发者对该标签的 padding
进行声明的值之中,而且padding
没有更高权重的声明了,那么就会对这个声明队列里的声明进行优先级的计算。
所以,!important 跟选择器优先级是什么关系?
优先级的计算
优先级的计算首先是 选择器权重 的优先级计算,然后是 声明先后顺序 的优先级计算。
选择器优先级的权重计算
这时候,才到了选择器优先级登场。
选择器的权重并不是网上很多人说的
选择器通过权重值 1(div等)、10(class)、100(id)
这样的方式进行的,这只是别人理解出来的东西,真正的计算原理可以翻阅W3C文档选择器权重的计算。
文档指出:
A selector’s specificity is calculated for a given element as follows:
1.count the number of ID selectors in the selector (= A)
2.count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B)
3.count the number of type selectors and pseudo-elements in the selector (= C)
4.ignore the universal selector
文档也给了例子:
Examples:
* /* a=0 b=0 c=0 */
LI /* a=0 b=0 c=1 */
UL LI /* a=0 b=0 c=2 */
UL OL+LI /* a=0 b=0 c=3 */
H1 + *[REL=up] /* a=0 b=1 c=1 */
UL OL LI.red /* a=0 b=1 c=3 */
LI.red.level /* a=0 b=2 c=1 */
#x34y /* a=1 b=0 c=0 */
#s12:not(FOO) /* a=1 b=0 c=1 */
.foo :matches(.bar, #baz)
/* Either a=1 b=1 c=0
or a=0 b=2 c=0, depending
on the element being matched. */
大致意思就是,把权重分为了 A,B,C 三个级别,A > B > C , A,B,C 直接各自计算。也就是会优先计算 A 的权重,如果相等会计算 B 的权重,以此类推。
所以才有了100、10、1的说法,但很不准确。
结合之前的demo,我们来计算下权重值:
1:
div { /*type selectors*/
padding:1px; /*a = 0 , b = 0 , c = 1*/
}
2:
.taobao_com{ /*class selectors*/
padding:12px; /*a = 0 , b = 1 , c = 0*/
}
3:
div .taobao{ /*type selectors + class selectors*/
padding:123px; /*a = 0 , b = 1 , c = 1*/
}
4:
.taobao_com .taobao{ /* class selectors + class selectors */
padding:1234px; /*a = 0 , b = 2 , c = 0*/
}
由于A位相等,B位差异的最大值在 4 ,得到的结果将会是 第4条声明 胜出:
1. a = 0 , **b = 0** , c = 1
2. a = 0 , **b = 1** , c = 0
3. a = 0 , **b = 1** , c = 1
4. a = 0 , **b = 2** , c = 0
声明先后顺序
当 A 、B 、C 所计算的权重都相等时(ABC三个值相等)相等时,后面声明的值将会是最终的计算值。
最终得到了CSS属性的值。
值得注意的是,MDN这个关于层叠的文档的中文版是错的。
另外,(MDN文档)写到:
同时仍应注意用@keyframes @规则定义的值会覆盖全部普通值,但会被!important的值覆盖。
W3C文档也有指出:
- Important user declarations
- Animation declarations [CSS3-ANIMATIONS]
但在Chrome浏览器(65.0.3325.162(正式版本) (64 位))测试的时候,
.img {
animation: doudong 1.5s linear infinite alternate;
transform: rotate(100deg) !important;
}
transfrom 的 !important 并没有覆盖 animation 的。
https://codepen.io/jincdream/pen/xWPQZM
难道我理解错了?