CSS

CSS Specificity 计算

深入 CSS

Posted by Nicodechal on 2020-02-16

Specificity 用于决定那一条 CSS 属性和一个元素最相关,并将该属性应用到该元素上。Specificity 基于匹配规则进行计算,匹配规则由一系列 CSS 选择器合成。

一个例子

下面首先通过一个例子来说明 Specificity 的使用方式。假设有如下的 html 文档:

1
2
3
<div id="test">
<span>Text</span>
</div>

和下面的 CSS 文档:

1
2
3
div#test span { color: green; }
div span { color: blue; }
span { color: red; }

下面直接给出不同匹配规则对应的 Specificity 值:

1
2
3
div#test span { color: green; } /* 0-1-0-2 */
div span { color: blue; } /* 0-0-0-2 */
span { color: red; } /* 0-0-0-1 */

此时从前往后逐位比较可以发现第一条的 Specificity 值更大,因此上述 html 文档中 Text 的颜色为绿色,与这三条规则所在的位置无关。

下面说明 Specificity 值的计算。

Specificity 计算

Specificity 值由 abcd 组成,每一个数使用下面规则计算:

  1. 如果使用 style 属性声明而不是选择器,则 a = 1 否则 a = 0
  2. 计算选择器中 ID 属性的数量 ( 例如 #hello,不包括 [id=hello] ),记为 b
  3. 计算选择其中其他属性 ( 包括 [id=hello] ) 和伪类的数量,记为 c
  4. 计算选择器中元素名称数量和伪元素的数量,记为 d

得到的 Specificity 记为 a-b-c-d

下面是一些来自文档的例子:

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 */

一些例外

关于 !important 的例外

尽量不要使用 !important。考虑下面的实践:

  1. 尽量使用 specificity 而不是 !important
  2. 只在页面使用的 CSS 中用来重写外部的 CSS。
  3. 在写插件时不要使用。
  4. 不在全站范围的 CSS 中使用。

相对于使用 !important,考虑:

  1. 尽可能利用 CSS 层叠。
  2. 使用更具体的规则。
  3. 通过重复选择器来得到更高的 specificity,例如:#myId span -> #myId#myId span

下面情况可以使用 !important

  1. 重写行内样式。
  2. 重写高 specificity 的规则。

下面方式可以重写 !important 规则:

  1. 高 specificity 的 !important 规则。
  2. !important 后添加的相同选择器的 !important 规则。

当然,最好还是重写前面的 !important 规则来避免使用 !important

:is():not() 的例外

计算 specificity 时,这两者不计为伪类,但其参数包含的选择器用来计算 specificity。

1
2
3
4
5
6
7
8
/* 0-0-1-2 */
div.outer p {
color: orange;
}
/* 0-0-1-2 */
div:not(.outer) p {
color: blueviolet;
}

:where() 的例外

该伪类的 specificity 总是 0。

其他注意事项

  1. 树层次上的接近不会影响 specificity 的计算。
  2. 直接目标样式相对于继承样式优先考虑。

参考资料

Specificity - MDN
specifishity
CSS - Specificity
Specificity Calculator