CSS选择器优先级由(a,b,c,d)四元组严格逐位比较决定,a为内联样式数、b为ID数、c为类/属性/伪类数、d为标签/伪元素数;高位相等才比低位,如(0,1,1,0)>(0,0,3,0),且!important不改变权重仅提升声明级别。

CSS选择器的优先级不是靠“谁写在后面”或“类名多不多”来决定的,而是由可量化的 (a, b, c, d) 权重分值严格比较得出。只要算出这个四元组,冲突根源立刻清晰——不需要猜、不用删代码、不依赖 DevTools 的“划掉样式”视觉提示。
怎么手算 (a, b, c, d) 权重
别信“1000/100/10/1”的简化加法,它在复杂选择器下会误导判断(比如 .a.b.c.d.e 算成 50,却输给了 #x span 的 101)。必须按四位独立计数:
-
a:只看是否含内联样式(style="..."),有则为1,否则0 -
b:数 ID 选择器个数,#header #nav是2,#main[data-id]仍是1(属性选择器不计入b) -
c:统计所有类名(.btn)、属性选择器([type="submit"])、伪类(:hover、:not(.disabled))——注意::not()括号内的选择器也参与计分,:not(#x)会加1到b -
d:只计标签名(div、p)和伪元素(::before),ul > li::marker是3
示例:#user .profile[data-active] a:hover → a=0, b=1(仅 #user), c=3(.profile + [data-active] + :hover), d=1(仅 a)→ 权重为 (0,1,3,1)。
为什么 .btn.btn-primary 压不住 #form .btn
表面都是“两个类”,但权重天差地别:.btn.btn-primary 是 (0,0,2,0),#form .btn 是 (0,1,1,0)。比较时先看 b 位:1 > 0,直接胜出,根本不会比后面的 c。这类问题在覆盖第三方 UI 库样式时高频出现,根源往往是库用了 ID 或嵌套了高 b/c 组合。
立即学习“前端免费学习笔记(深入)”;
- 排查时第一反应不该是“我类名不够多”,而是打开 DevTools → Computed 面板 → 找被划掉的样式 → 看右侧 specificity 值(如
0,1,1,2),再反推哪个选择器贡献了高位 - 若发现第三方用了
#modal,你就别用纯类去硬刚,要么改用.modal-root这类无 ID 的语义化类,要么主动加一层.my-app #modal(但慎用,污染全局) - 嵌套写法如
section > article div p span em权重达(0,0,0,6),看似精准,实则脆弱——一个简单的.highlight((0,0,1,0))就能反超
!important 不是解药,是警报灯
!important 不改变选择器本身的权重,只是给某条声明开“特权通道”。它优先级高于内联样式,但滥用后果严重:
- 后续想覆盖它,必须也加
!important,形成恶性循环 - DevTools 中所有带
!important的样式都标黄,干扰你识别真实权重冲突点 - 它绕过了 CSS 的层叠设计哲学,等于放弃结构治理
真正该做的是:把 !important 当作调试标记。比如 .modal .close:hover 总被覆盖,与其加 !important,不如把类名改成 .modal-close,让语义和权重对齐——这比堆砌选择器更可持续。
最易被忽略的点:权重比较永远从左到右逐位进行,b 位存在一个 ID 就基本宣告低权重选择器出局;而 :not() 括号里的内容会实实在在计入对应位,不是“装饰”。算错一位,整个判断就偏了。


















