基于本文回答
0
评论

CSS 选择器的优先级(Specificity)是如何计算的?

知识点图片

CSS 选择器的优先级(Specificity,也称为“权重”)是浏览器决定当多个 CSS 规则应用到同一个元素时,哪一个规则生效的算法。

计算优先级通常使用一个 (A, B, C, D) 的四位数值模型来表示。

1. 权重的计算规则 (A, B, C, D)

我们将优先级看作四个等级,从左到右,权重依次降低。

  • A (Inline Styles): 行内样式。
    • 如果样式是写在 HTML 标签的 style="..." 属性中,A = 1。
  • 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. 比较规则(决胜负)

当浏览器比较两个选择器的优先级时,遵循 “逐级比较,高位优先” 的原则:

  1. 先看 A 列: 谁大谁赢。如果 A 列相同,看 B 列。
  2. 再看 B 列: 谁大谁赢。如果 B 列相同,看 C 列。
  3. 再看 C 列: 谁大谁赢。如果 C 列相同,看 D 列。
  4. 最后看 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. 总结图谱

优先级从高到低排列:

  1. !important (核武器,慎用)
  2. 行内样式 (style="") -> (1, 0, 0, 0)
  3. ID 选择器 (#id) -> (0, 1, 0, 0)
  4. 类 / 属性 / 伪类 (.class, [attr], :hover) -> (0, 0, 1, 0)
  5. 元素 / 伪元素 (div, ::before) -> (0, 0, 0, 1)
  6. 通配符 / 关系符 (*, +, >) -> (0, 0, 0, 0)

口诀: ID 大于 类,类 大于 标签,行内样式最强,!important 无敌。

右滑查看面试常问