计算选择器的特异性

每个 CSS Selector 都有自己的特定值。序列中的每个选择器都会增加序列的整体特异性。选择者属于三个不同的特异性组之一: ABc 。当多个选择器序列选择给定元素时,浏览器使用具有最高整体特异性的序列应用的样式。

A 组是最具体的,其次是 B 组,最后是 C 组。

通用选择器(*)和组合器(如 >~)没有特异性。

示例 1:各种选择器序列的特异性

#foo #baz {}      /* a=2, b=0, c=0 */

#foo.bar {}       /* a=1, b=1, c=0 */

#foo {}           /* a=1, b=0, c=0 */

.bar:hover {}     /* a=0, b=2, c=0 */

div.bar {}        /* a=0, b=1, c=1 */

:hover {}         /* a=0, b=1, c=0 */

[title] {}        /* a=0, b=1, c=0 */

.bar {}           /* a=0, b=1, c=0 */

div ul + li {}    /* a=0, b=0, c=3 */

p::after {}       /* a=0, b=0, c=2 */

*::before {}      /* a=0, b=0, c=1 */

::before {}       /* a=0, b=0, c=1 */

div {}            /* a=0, b=0, c=1 */

* {}              /* a=0, b=0, c=0 */

示例 2:浏览器如何使用特异性

想象一下以下的 CSS 实现:

#foo {
  color: blue;
}

.bar {
  color: red;
  background: black;
}

这里我们有一个 ID 选择器,它将 color 声明为蓝色,一个类选择器将 color 声明为红色,将 background 声明为黑色

两个声明将选择 ID 为 #foo.bar 类的元素。ID 选择器具有 A 组特异性,类选择器具有 B 组特异性。ID 选择器超过任意数量的类选择器。因此,来自 #foo 选择器的 color:blue; 和来自 .bar 选择器的 background:black; 将应用于该元素。ID 选择器的更高特异性将导致浏览器忽略 .bar 选择器的 color 声明。

现在想象一下不同的 CSS 实现:

.bar {
  color: red;
  background: black;
}

.baz {
  background: white;
}

这里我们有两个类选择器; 其中一个宣称 color红色background黑色,另一个宣称 background白色

具有 .bar.baz 类的元素将受到这两个声明的影响,但是我们现在遇到的问题是 .bar.baz 具有相同的 B 组特异性。CSS 的级联性质为我们解决了这个问题:由于 .baz .bar 之后定义的,我们的元素最终得到了来自 .bar红色 color,但是来自 .baz白色 background

例 3:如何操作特异性

可以操作上面示例 2 中的最后一个片段,以确保使用 .bar 类选择器的 color 声明而不是 .baz 类选择器的声明。

.bar {}        /* a=0, b=1, c=0 */
.baz {}        /* a=0, b=1, c=0 */

实现这一目标的最常见方法是找出可以应用于 .bar 选择器序列的其他选择器。例如,如果 .bar 类仅应用于 span 元素,我们可以将 .bar 选择器修改为 span.bar。这将给它一个新的 C 组特异性,它将覆盖 .baz 选择器的缺乏:

span.bar {}    /* a=0, b=1, c=1 */
.baz {}        /* a=0, b=1, c=0 */

然而,可能并不总是能够找到在使用 .bar 类的任何元素之间共享的另一个公共选择器。因此,CSS 允许我们复制选择器以增加特异性。我们可以使用 .bar.bar 代替 .bar(参见选择器的语法,W3C 推荐标准 )。这仍然选择具有 .bar 类的任何元素,但现在具有组 B 特异性的两倍 :

.bar.bar {}    /* a=0, b=2, c=0 */
.baz {}        /* a=0, b=1, c=0 */

!important 和内联样式声明

样式声明中的 !important 标志和 HTML style 属性声明的样式被认为具有比任何选择器更高的特异性。如果存在这些,它们影响的样式声明将否决其他声明,无论它们的特殊性如何。也就是说,除非你有多个声明包含适用于同一元素的同一属性的 !important 标志。然后,正常的特异性规则将适用于那些相互引用的属性。

因为它们完全覆盖了特异性,所以在大多数使用案例中都不赞成使用 !important。人们应该尽可能少地使用它。为了长期保持 CSS 代码的有效性和可维护性,增加周围选择器的特异性几乎总是比使用 !important 更好。

!important 并不令人讨厌的罕见例外之一是在实现通用助手类时,如 .hidden.background-yellow 类,它们应始终在任何遇到的地方覆盖一个或多个属性。即便如此,你还需要知道自己在做什么。在编写可维护的 CSS 时,你想要的最后一件事就是在整个 CSS 中使用 !important 标志。

最后一点

关于 CSS 特异性的一个常见误解是 ABC 组值应相互组合(a=1, b=5, c=1 => 151)。这是不是这种情况。如果是这种情况,拥有 20 个 B 组或 C 组选择器就足以分别覆盖单个 A 组或 B 组选择器。这三组应被视为具体的个体水平。特异性不能用单个值表示。

创建 CSS 样式表时,应尽可能保持最低的特异性。如果你需要将特异性提高一点来覆盖另一种方法,那就让它更高但尽可能低,以使其更高。你不应该有这样的选择器:

body.page header.container nav div#main-nav li a {}

这使得未来的变化更加困难并污染了 css 页面。

你可以计算出你选择的特殊性这里