CSS 选择器的优先级(Specificity)是如何计算的?
CSS 选择器的优先级(Specificity,也称为“权重”)是浏览器决定当多个 CSS 规则应用到同一个元素时,哪一个规则生效的算法。
计算优先级通常使用一个 (A, B, C, D) 的四位数值模型来表示。
1. 权重的计算规则 (A, B, C, D)
我们将优先级看作四个等级,从左到右,权重依次降低。
- A (Inline Styles): 行内样式。
- 如果样式是写在 HTML 标签的
style="..."属性中,A = 1。
- 如果样式是写在 HTML 标签的
- B (ID Selectors): ID 选择器。
- 例如:
#header,#app。每出现一个,B + 1。
- 例如:
- C (Classes, Attributes, Pseudo-classes): 类、属性、伪类选择器。
- 类选择器:
.container,.red - 属性选择器:
[type="text"],[href] - 伪类选择器:
:hover,:nth-child,:focus - 每出现一个,C + 1。
- 类选择器:
- D (Types, Pseudo-elements): 元素(标签)、伪元素选择器。
- 元素选择器:
div,h1,p - 伪元素选择器:
::before,::after,::placeholder - 每出现一个,D + 1。
- 元素选择器:
注意:
- 通配符 (
*)、组合符号 (+,>,~,) 和 否定伪类 (:not) 本身对优先级没有影响(权重为 0)。但是,:not()内部 的选择器会参与计算。
2. 具体的计算示例
让我们通过几个例子来计算权重值 (A, B, C, D):
| 选择器 | A (行内) | B (ID) | C (类/属性/伪类) | D (标签/伪元素) | 权重值 |
|---|---|---|---|---|---|
h1 |
0 | 0 | 0 | 1 | 0-0-0-1 |
div h1 |
0 | 0 | 0 | 2 | 0-0-0-2 |
.title |
0 | 0 | 1 | 0 | 0-0-1-0 |
h1.title |
0 | 0 | 1 | 1 | 0-0-1-1 |
.nav .list .item |
0 | 0 | 3 | 0 | 0-0-3-0 |
#main |
0 | 1 | 0 | 0 | 0-1-0-0 |
#main div.content |
0 | 1 | 1 | 1 | 0-1-1-1 |
style="color: red" |
1 | 0 | 0 | 0 | 1-0-0-0 |
3. 比较规则(决胜负)
当浏览器比较两个选择器的优先级时,遵循 “逐级比较,高位优先” 的原则:
- 先看 A 列: 谁大谁赢。如果 A 列相同,看 B 列。
- 再看 B 列: 谁大谁赢。如果 B 列相同,看 C 列。
- 再看 C 列: 谁大谁赢。如果 C 列相同,看 D 列。
- 最后看 D 列: 谁大谁赢。
重要提示: 权重是不能进位的。
- 例如:11 个类选择器 (
.c1.c2...c11) 的权重是(0, 0, 11, 0)。 - 1 个 ID 选择器 (
#id) 的权重是(0, 1, 0, 0)。 - 虽然 11 > 1,但在优先级比较中,ID 所在的 B 列级别更高。所以 1 个 ID 永远大于 无数个 Class。
4. 特殊情况与例外
(1) !important
!important 不是选择器,而是一个声明修饰符。它具有最高优先级,会覆盖行内样式(Inline Style)。
- 规则: 只有另一个
!important才能覆盖!important。 - 比较: 如果两个冲突的规则都用了
!important,则再回头去比较它们所属选择器的权重。
(2) 源码顺序 (Source Order)
如果两个选择器的权重完全相同,那么后定义(在 CSS 文件中写在后面)的规则会覆盖先定义的规则。
- “后来者居上”。
(3) :is(), :not(), :has()
这几个伪类本身不增加权重,但以其参数中权重最高的选择器为准。
- 例如:
:is(#id, .class)的权重等于#id的权重(0, 1, 0, 0)。 - 例如:
:not(p)的权重等于p的权重(0, 0, 0, 1)。
(4) :where()
这是一个特例。:where() 中的选择器权重总是 0。
- 这在编写库或重置样式时非常有用,因为它可以轻易被用户自定义的样式覆盖。
5. 总结图谱
优先级从高到低排列:
!important(核武器,慎用)- 行内样式 (
style="") ->(1, 0, 0, 0) - ID 选择器 (
#id) ->(0, 1, 0, 0) - 类 / 属性 / 伪类 (
.class,[attr],:hover) ->(0, 0, 1, 0) - 元素 / 伪元素 (
div,::before) ->(0, 0, 0, 1) - 通配符 / 关系符 (
*,+,>) ->(0, 0, 0, 0)
口诀: ID 大于 类,类 大于 标签,行内样式最强,!important 无敌。
右滑查看面试常问