CSS 优先级(特异性)怎么算:ID/类/标签权重与样式不生效排查
讲清 CSS 优先级如何按内联、ID、类、标签分列算分,为什么一个 ID 压过一百个类,!important 为什么跳出比较,以及样式不生效时该怎么一步步查到真凶。
CSS 优先级(特异性)怎么算,以及样式为什么不生效
写 CSS 写久了,迟早会遇到一条让人血压升高的事:你明明写了一条规则,颜色就是不变。打开 devtools 一看,样式被划了一道删除线。这背后几乎都是同一件事在起作用:优先级,也叫特异性(specificity)。把它算明白,90% 的"样式不生效"当场就能定位。
优先级到底是一个怎样的数
很多人脑子里把优先级当成一个分数。这是误解。它其实是一组分列的计数,一般写成 (a, b, c) 三元组,外加一个更高的内联档:
- 内联 style 属性:权重 1000,常单独写成 (1,0,0,0) 那一档。
- ID 选择器(
#header):a 列,权重 100。 - 类 / 属性 / 伪类(
.btn、[type]、:hover):b 列,权重 10。 - 元素 / 伪元素(
div、::before):c 列,权重 1。 - 通配符
*和组合器(>、+、~、后代空格):一律不计。
记 1000 / 100 / 10 / 1 这套数只是方便心算。引擎真正做的是一列一列从左往右比:a 相同才看 b,b 相同才看 c。列与列之间从不进位。
一个 ID 真的压过一百个类
因为是一列一列比、不进位,所以结论有点反直觉但很硬:#x 是 (1,0,0),叠了一百个类的选择器是 (0,100,0)。先比 a 这一列,1 对 0,#x 直接赢下,后面 b 有多大都不看了。
那句流传很广的"256 个 class 会进位成一个 ID",只在某个上古 IE/Netscape 版本里短暂成立过,之后一直是以讹传讹。现代任何引擎都不进位。所以想靠多堆几个类去压过一个 ID,方向就错了,你需要的是自己也上一个 ID,或者把对方的 ID 降权。
算一个真实例子
拿这条选择器:#sidebar .link a:hover。逐项数一遍:
#sidebar→ ID,a +1.link→ 类,b +1a→ 元素,c +1:hover→ 伪类,b +1
合起来是 (1, 2, 1)。如果同一个元素上还有一条 .link--muted,它是 (0, 1, 0)。两条放一起比:先比 a,1 对 0,第一条直接胜出,.link--muted 的颜色就被划掉了。这正是"我的样式怎么不生效"最常见的原型,胜负在第一列就分完了。
不想一个个手数的时候,我自己的习惯是直接把几条选择器各占一行丢进 CSS 优先级计算器,它会把每条算成三元组并高亮谁赢、是哪一位分出的胜负。有一次我和同事为一条 #app .card 争了十分钟,粘进去两秒就看到是 a 列(他那条带 ID)说了算,争论当场结束,比开 devtools 翻 cascade 还快。
!important 为什么是另一套规则
!important 不在 (a, b, c) 三元组里。它会跳出正常的优先级比较,不看三元组,直接压过任何不带 important 的声明。所以它确实很猛,但也正因为猛,容易留坑。
两条都带 !important 的时候,又退回正常规则:先比优先级,再比源码顺序,更靠后、更具体的那条 !important 仍然压过更早的。于是项目里一旦 !important 满天飞,你就会陷入"再加一个 important 盖过上一个"的循环。这就是所谓的优先级战争。
正确顺序是:先问能不能用一个更具体的选择器解决,实在不行再动 !important,而且最好只留给工具类这种确实需要无条件覆盖的场景。
样式不生效,按这个顺序查
遇到一条 CSS 没生效,别急着加 !important,按这条路走基本都能找到:
- 先看元素上有没有行内
style。它高过你在样式表里能写的任何选择器,包括#id#id#id。 - 再看有没有别的规则带
!important。 - 都没有,就比两条规则的三元组,从 a 列往右看哪一列分出胜负。
- 三元组完全打平,那是源码顺序决定,后面的赢,把你的规则挪到后面即可。
顺手说两个降权和抬权的小技巧。想给某条规则降权又不动 HTML,用 :where() 把它的 ID 包起来,:where(#sidebar) .link 会算成 (0,1,0),就当那个 ID 不存在。想抬一点权重又不想上 !important,把类自己叠一遍,.btn.btn 算 (0,2,0)。
避免优先级战争
优先级战争的根子,几乎都是基础样式写得太重。组件库尤其要小心:任何高于 (0,1,0) 的默认值,都是将来某个使用方"不复制你的优先级就盖不掉"的工单。所以基础样式尽量压在单类一层,需要保持可覆盖的部分用 :where() 清零,真正需要锁死的才上 ID 或 !important。
算清楚之后,顺手的活儿也轻松不少:写完规则可以用 CSS 格式化工具把缩进和分组理顺,review 时一眼看清每条选择器有多重。优先级从来不玄学,它只是一组一列一列、从不进位的计数。数清楚了,样式就不会再偷偷不生效。
Made by Toolora · Updated 2026-06-13