选择器权重计算方式(Specificity):怎么“算分”、怎么“比大小”?
面试速答(30 秒版 TL;DR)
- 选择器权重(Specificity)常用四段记法:
A-B-C-D(逐列比较,不做加法)。 - 计算规则(面试够用版):
A:内联样式(style="")算 1(注意:这是“内联声明”的权重,不是选择器片段)。B:#id每个 +1。C:.class、[attr]、:hover/:focus/:nth-child(...)等伪类每个 +1。D:div等标签、::before/::after等伪元素每个 +1。
*、组合符(空格、>、+、~)都不加分。:where(...)特例:它本身和参数都计为 0;!important不属于权重计算(它属于层叠的“重要性赛道”)。
这篇只讲“权重怎么算”。更完整的层叠比较顺序建议看:CSS 不同选择器的权重与层叠规则。
一、权重到底在解决什么问题
当同一元素的同一属性被多条 CSS 声明同时命中时,浏览器需要决定“听谁的”。在层叠(Cascade)流程里,权重是其中一关,用来比较“选择器谁更具体”。
关键提醒:很多“我算出来权重更大却没生效”的问题,其实是输在:
!important@layer- 书写顺序(权重打平时后写覆盖先写)
二、A-B-C-D 四段法(可直接背)
| 选择器/声明片段 | 计入段 | 例子 |
|---|---|---|
| 内联样式(声明) | A + 1 | <div style="color:red"> |
| ID 选择器 | B + 1 | #app |
| 类选择器 | C + 1 | .card |
| 属性选择器 | C + 1 | [disabled]、[type="text"] |
| 伪类 | C + 1 | :hover、:focus、:not(...)、:is(...) |
| 标签选择器 | D + 1 | div、a |
| 伪元素 | D + 1 | ::before、::marker |
不加分(高频坑):
*通配符:0- 组合符:0(空格、
>、+、~) :where(...):0(包括参数)
三、比较规则:逐列比较,不做加法
权重比较是“字典序”:
- 先比
A A相同再比BB相同再比CC相同再比D
例子(面试常用):
0-1-0-0(一个 ID)永远大于0-0-999-999(再多 class/tag 也赢不了 ID).a .b与.a > .b权重相同:都是0-0-2-0(组合符不计分)
四、现代伪类的“特殊计分规则”(建议背 3 个)
1):is(...) / :not(...) / :has(...)
它们本身当作伪类,但权重按参数选择器来算(面试表达法:取参数里“最强的那一个”的权重)。
/* :is(.a, #b) 的权重相当于 #b(因为 #b 更“强”) */
.x :is(.a, #b) {
color: red;
}
常见误区:有人以为逗号里的每个都“累计加分”,这是错的。
2):where(...)
:where(...) 的权重恒为 0:参数再具体也不加权重。
/* 权重 = 0:它通常用于“写默认样式但不抢权重” */
:where(.btn.primary #danger) {
color: #c00;
}
3):nth-child(...) / :nth-of-type(...)
它们属于伪类,计入 C + 1。括号里的 2n+1 不影响权重;但如果写了 of <selectorList>(较新语法),会按其中选择器带来的规则计算(面试可简单说“它也是按参数选择器算”)。
五、典型题与标准答法(可直接照读)
Q1:.a#b 和 #b 谁更大?
答:.a#b 更大。#b 是 0-1-0-0,.a#b 是 0-1-1-0。
Q2:为什么我把 HTML 里 class 顺序换了,样式没变?
答:class 在 HTML 里的先后顺序不参与权重比较。权重打平时通常比的是“CSS 书写顺序(后写赢)”。
Q3:!important 算不算权重?
答:不算。!important 是层叠里更靠前的一关,属于“重要性赛道”,不是 Specificity 的计分项。
易错点/坑
- 把权重当成“加法总分”去比较(错,逐列比)。
- 认为组合符会加分(错)。
:where()还在按参数算分(错,恒 0)。- 只会算权重,不会排层叠的前置关卡:
!important、@layer、书写顺序。
速记要点(可背诵)
- 权重
A-B-C-D:内联 / ID / class-attr-伪类 / tag-伪元素。 - 逐列比,不做加法;组合符和
*不加分。 :where()恒 0;:is/:not/:has按参数里最强的算。