样式的层叠权重值

样式的层叠权重值

在学习 CSS 的过程当中,我们可能听说过特指度(I-C-E)这个东西,简单来说特指度表示一个 CSS 选择器表达式的重要程度,通常称为 I-C-E 计算公式,其中的 I 对应着 IdC 对应着 classE 对应着 Element,在针对一个 CSS 选择器表达式的时候,遇到一个 Id 就往特指度数值中加 100,遇到一个 class 就往特指度数值中加 10,遇到一个 Element 就往特指度数值中加 1

但是上面的这种计算方式是存在一定问题的,虽然在大多数情况下,按照这样的理解得出的结论是没有问题的,但是在一些比较特殊的情况之下还是存在问题的,所以今天就抽了点时间,让我们一起来深入的了解一下 CSS 当中的样式层叠权重值,既然是样式层叠权重值,那么我们就先来看看什么是权重

CSS 权重

CSS 权重,我们可以简单理解为 CSS 优先级,下面是 MDN 对优先级的描述

浏览器通过优先级来判断哪一些属性值与一个元素最为相关,从而在该元素上应用这些属性值,优先级是基于不同种类选择器组成的匹配规则

我们通过一个小例子来加深理解

1
<p class="text" id="text">我是一段简单的文本。</p>
1
2
3
4
5
6
7
.text {
color: red;
}

#text {
color: blue;
}

运行后可以发现,p 标签文本的颜色为蓝色,那么为什么会是蓝色呢?这就是 CSS 权重来决定的,它决定最终用哪个样式作用到 p 标签上,在网上流传的说法当中,关于选择器各级别的优先级,大家应该已经很清楚了,常见的说法是下面这样的

1
!important > 内联 > ID > 类 > 标签/伪类/属性选择 > 伪对象 > 通配符 > 继承

那么这个顺序是怎么得出来的呢?

实际上在 CSS 2.1 规范当中关于 具体性(specificity) 的定义当中,描述是非常明确的,根据 CSS 规范,具体性越明确的样式规则,权重值越高,而计算权重值的依据,并不像是我们在开头部分所描述的 class10,标签是 1Id100 之类的,虽然这样在大多数情况下能够得到正确的结果

选择器权重值的计算

下面我们就根据 CSS 规范,来具体的看一下选择器权重值到底是如何进行计算的,主要有以下四点

  • A,如果规则是写在标签的 style 属性中(内联样式),则 A = 1,否则 A = 0,对于内联样式,由于没有选择器,所以 BCD 的值都为 0,即 A = 1B = 0C = 0D = 0(简写为 1, 0, 0, 0
  • B,计算该选择器中 Id 的数量(例如 #header 这样的选择器计算为 0, 1, 0, 0
  • C,计算该选择器中伪类及其它属性的数量,包括类选择器、属性选择器等,不包括伪元素(例如 .logo[id='site-logo'] 这样的选择器,计算为 0, 0, 2, 0
  • D,计算该选择器中伪元素及标签的数量(例如 p:first-letter 这样的选择器,计算为 0, 0, 0, 2

我们可以根据上面的规则来对应看看下面这些示例

1
2
3
4
5
6
7
8
9
10
* {}              /* 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 */

根据这样的定义,在网上有很多文章简单地把规则归纳为

针对内联样式的权重值是 1000Id 选择器的权重值是 100Class 选择器的权重值是 10,标签选择器的权重值是 1,整条规则中的所有选择器权重值相加得到整个样式规则的权重值,数字越大权重值越高

大多数情况下,按照这样的理解得出的结论没有问题,但是遇到下面这样的情况就出现问题了

1
2
3
4
5
/* 样式一 */
body header div nav ul li div p a span em { color: red }

/* 样式二 */
count { color: blue }

如果按照错误的计算方法来进行计算的话,样式一的权重值是 11,样式二的权重值是 10,所以如果这两条规则用于同一个元素,则该元素应该是红色,但是实际的结果却是蓝色的,所以我们按照规范当中提到的方式再次来看看上面的示例,针对例子中的样式一权重值应该是 0, 0, 0, 11,而样式二的权重值是 0, 0, 1, 0,根据规范

计算权重值时 A, B, C, D 四组值,从左到右,分组比较,如果 A 相同,比较 B, 如果 B 相同,比较 C,如果 C 相同,比较 D,如果 D 相同,则后定义的优先

我们可以得出,样式二和样式一的 A、B 相同,而样式二的 C 大于样式一,所以不管 D 的值如何,样式二权重值都大于样式一,这就是正确的答案

特殊的 !important

在按照 ABCD 四组计算比较之外,在定义样式的时候,还可以对某一个属性应用 !importantCSS 规范中规定

!important 用于单独指定某条样式中的单个属性,对于被指定的属性,有 !important 指定的权重值大于所有未用 !important 指定的规则

例如下面这个示例

1
2
3
4
5
/* 样式一 */
#header nav ul li.current { color: red; font-weight: bold; }

/* 样式二 */
li:hover { color: blue !important; font-weight: normal; }

就整条规则而言,样式一的权重值为 0, 1, 1, 3,而样式二的权重值仅为 0, 0, 0, 2,所以应用于相同元素时,应该样式一生效,但是对于 color 这个属性,由于在样式二中用 !important 做了指定,因此 color 将应用样式二的规则,而 font-weight 则按照规定用样式一的规则

因此 !important 的作用只有在具有唯一性时才能提现,但是我们永远无法预料自己什么时候又需要覆盖一个已经指定了 !important 的属性,所以最好的办法就是不要使用 !important

这里有一个需要注意的地方,如果多条规则中都对同一个属性指定了 !important 呢?这时候 !important 的作用相互抵销,依然按照 ABCD 四组计算比较,更多详细内容可以参考 MDN 上的 例外的 !important 规则

下面是一些来自 MDN 上的一些经验法则

  • 一定要优先考虑使用样式规则的优先级来解决问题而不是 !important
  • 只有在需要覆盖全站或外部 CSS 的特定页面中使用 !important(比如引入了某些框架)
  • 永远不要在全站范围的 CSS 上使用 !important
  • 永远不要在你的插件中使用 !important

取而代之,你可以

  • 更好的利用 CSS 级联属性
  • 使用更具体的规则,在您选择的元素之前增加一个或多个元素,使选择器变得更加具体,并获得更高的优先级

比如下面这个例子

1
2
3
<div id="test">
<span>Text</span>
</div>
1
2
3
div#test span { color: green }
span { color: red }
div span { color: blue }

上面例子中,无论你 CSS 语句的顺序是什么样的,文本都会是绿色的,因为这一条规则是最具有针对性,优先级最高的(同理,无论语句顺序怎么样,蓝色的规则都会覆盖红色的规则),我们在之前提到了 !important 的相关规则和使用方式,但是在某些情况下,使用 !important 可能是无可避免的,那么我们该如何来覆盖 !important 呢,主要有下面三种方式

  • 再添加一条带 !importantCSS 规则,其实就是要么给这个选择器更高的优先级(添加一个标签,IdClass),或是添加一样的选择器,把它的位置放在原有声明的后面
  • 使用相同的选择器,但是置于已有的样式之后
  • 或者改写原来的规则,以避免使用 !important

关于 inherit

除了直接指定到元素上的样式规则以外,每个属性值还有一个可能为 inherit 的值,表示元素的该样式属性继承自父级元素,与父级元素的定义一致,比如

1
2
3
4
5
<ul class="list">
<li class="item">
<span>某些文字</span>
</li>
</ul>
1
.list .item { color: red }

在上例当中,我们将 licolor 样式设定为红色,但是并未针对 span 标签指定 color 属性,但是 span 中的文字会显示为红色,这就是因为 spancolor 属性默认值为 inherit,对于 inherit 的属性,只要记住一点,即继承而来的属性值,权重永远低于明确指定到元素的定义,只有当一个元素的某个属性没有被直接指定时,才会继承父级元素的值,我们稍微调整一下

1
2
3
.list .item { color: red }

span { color: blue }

同样的例子,第一条规则按照 ABCD 四组计算的权重为 0, 0, 2, 0,第二条规则的权重为 0, 0, 0, 1,虽然第一条规则的权重更高,但是它是针对 li 元素的直接指定,并不是针对 span 元素定义的,所以计算 spancolor 属性权重值时,实际上就是 inherit 的红色与直接指定的蓝色的对比,按照规则,只要有直接指定的值(蓝色),就不会再取继承的值(红色),所以 span 中的文字显示为蓝色

这条规则最典型的场景就是链接文字的颜色,由于一般浏览器自带的样式表都有针对 a 标签的颜色及下划线的直接指定,所以网页样式表中对 a 标签的父级元素指定 color 属性及 text-decoration 属性,通常不会起作用

1
2
3
<p class="txt">
<a href="#">父级元素设置的 color 和 text-decoration 对我不起作用</a>
</p>
1
2
3
4
.txt {
color: red;
text-decoration: none;
}

我们在浏览器可以看到,尽管我们给 a 标签的父级 p 设置了颜色红色和去除下划线,但是 a 标签依然是蓝色的和带下划线的,即使你给它们都加上 !important 也无效,但是我们可以通过下面的 reset 来改变这一点

1
2
3
4
5
6
7
8
9
a {
color: inherit;
text-decoration: inherit;
}

.txt {
color: red;
text-decoration: none;
}

可以看到,父级设置的样式生效了,由于我们的样式表对 a 标签直接指定了 colortext-decoration 属性值,覆盖了浏览器的默认样式,所以在没有特别指定 a 标签的颜色和下划线定义的前提下,会从父级元素 p 继承,因此链接会显示红色和没有下划线

特别补充,inheritCSS1 规范中并未定义,所以 IE6/IE7/IE8QuirksMode 不支持

关于 a 标签,我们再来看下面这个有趣的例子

1
<a href="#">鼠标划入时,我的颜色是?</a>
1
2
3
4
5
6
7
a:hover {
color: red;
}

a:link {
color: blue;
}

我们希望的效果是鼠标移入 a 标签的时候,文字变成红色的,但是实际仍然是蓝色,那么为什么会这样呢?其实,a 标签的四个伪类(:link:hoveractivevisited)的优先级是一样的,所以这时候就看它们在样式文件中的顺序了,后面的会覆盖前面的,为了避免出现这样的情况,我们在写 a 标签的伪类的时候,要注意它们的顺序,遵循 :link:visited:hover:active

实例

最后我们再来看一个实例,加深一下印象,如下,请问两个 p 标签的文本颜色分别是什么?

1
2
<p class="blue red">我的颜色是?</p>
<p class="red blue">我的颜色是?</p>
1
2
3
4
5
6
7
.red {
color: red;
}

.blue {
color: blue;
}

你可能觉得一个颜色为红,一个为蓝,但是运行后可以发现,它们都是蓝色的,所以这里就需要注意了,class 中的类名的顺序并不会影响样式的优先级,而是由它们在样式文件中的先后顺序决定的,后面定义的优先级更高

总结

  • 权重决定了你的 CSS 规则怎么样被浏览器解析直到生效
  • 权重决定了哪一条规则会被浏览器应用到元素上
  • 每个选择器都有自己的权重
  • 权重的大小跟选择器的类型和数量有关
  • 样式的优先级跟样式的定义顺序有关,后面的覆盖前面的
  • 一条样式规则的整体权重值包含四个独立的部分 [A, B, C, D]
  • A 表示内联样式,只有 10 两个值
  • B 表示选择器中 Id 的数量
  • C 表示选择器中类、属性、伪类选择器的数量
  • D 表示选择器中伪元素及标签的数量
  • 比较时,从低位到高位(从 AD)分别比较,高位相同时才比较低位
  • 如果两个选择器作用到同一元素上,则权重高者生效
  • 如果两个选择器权重相同,则最后定义的规则被计算到权重中(后面定义的 CSS 规则权重要更大,会覆盖前面的 CSS 规则)
  • 标签选择器的权重永远都比一个类选择器的权重低,无论有多少个,除非使用 !important
  • !important 标记指定的属性权重值最高,多次指定时相互抵消,应尽量减少 !important 的使用
  • inherit 而来的属性定义,优先级低于任何直接指定的属性值

参考

# CSS

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×