CSS 选择器优先级详解,一次搞懂权重计算规则

CSS 选择器优先级详解,一次搞懂权重计算规则

如果你写过一段时间的 CSS,大概率遇到过这样的场景:明明写了样式,却死活不生效;改了一个类名,另一个地方的样式突然崩了;想覆盖一个第三方组件的样式,加了 !important 才勉强搞定,结果后面越改越乱。

这些问题的根源,往往都指向同一个东西------CSS 选择器优先级(Specificity) 。

这篇文章会把优先级的计算规则拆解得清清楚楚,看完之后你再也不用靠"猜"来写 CSS 了。


一、什么是选择器优先级?

当多个 CSS 规则同时匹配同一个元素,并且它们声明了同一个 CSS 属性时,浏览器需要决定:到底用谁的?

这个"决定规则"就是优先级。优先级高的胜出。

一句话记住核心原则:更具体的选择器,优先级更高。


二、优先级权重模型(四元组)

浏览器内部用一个 四位元组 (a, b, c, d) ​ 来表示每个选择器的优先级权重。从左到右,权重逐级递减。

位置 代表来源 权重级别
a​ 内联样式(style 属性) 最高
b​ ID 选择器(#header) 高
c​ 类选择器、属性选择器、伪类 中
d​ 元素(标签)选择器、伪元素 低

注意:这不是十进制数字,是独立的四位计数 。即使 d 的值超过 10,也不会进位到 c。比如 (0, 0, 0, 15) 仍然小于 (0, 0, 1, 0)。


三、各类选择器的权重明细

1️⃣ 内联样式 --- (1, 0, 0, 0)

css 复制代码
<div style="color: red;">Hello</div>

内联样式优先级极高,仅次于 !important。

2️⃣ ID 选择器 --- (0, 1, 0, 0)

css 复制代码
#header { color: blue; }

每个 ID 贡献 1 个 b 位。

3️⃣ 类选择器、属性选择器、伪类 --- (0, 0, 1, 0)

css 复制代码
.nav { }              /* 类选择器 */
[type="text"] { }     /* 属性选择器 */
:hover { }            /* 伪类 */

每出现一个,c 位 +1。多个叠加时累加:

css 复制代码
.nav.active:hover    /* (0, 0, 3, 0) */

4️⃣ 元素选择器、伪元素 --- (0, 0, 0, 1)

css 复制代码
div { }              /* 元素选择器 */
::before { }         /* 伪元素 */

每出现一个,d 位 +1。

5️⃣ 通用选择器、组合符、否定伪类

css 复制代码
* { }                /* 通用选择器 → (0, 0, 0, 0) */
div > p { }          /* > 组合符 → 不贡献权重 */
:not(.foo) { }       /* :not() 本身不贡献,但 .foo 贡献 (0,0,1,0) */

:not()、:is()、:where() 比较特殊,后面会单独讲。


四、复合选择器的权重计算

复合选择器(多个简单选择器组合)的权重 = 各部分权重之和。

示例逐个拆解

选择器 权重计算 四元组
div 1 个元素 (0, 0, 0, 1)
.nav 1 个类 (0, 0, 1, 0)
#app 1 个 ID (0, 0, 1, 0) → 更正:(0, 1, 0, 0)
div.nav 1元素 + 1类 (0, 0, 1, 1)
#app .nav li 1ID + 1类 + 1元素 (0, 1, 1, 1)
ul.nav li.item a:hover 2元素 + 2类 + 1伪类 (0, 0, 3, 2)

实战对比

css 复制代码
#header .nav li a { color: red; }     /* (0, 1, 2, 2) */
.nav-menu a { color: blue; }          /* (0, 0, 2, 1) */

前者胜出 ✅ --- 因为有 1 个 ID 选择器。


五、特殊规则

🔴 !important --- 终极武器

css 复制代码
.title { color: red !important; }

!important 不是选择器优先级的一部分,它是一个独立的覆盖机制:

diff 复制代码
!important 声明 > 内联样式 > ID > 类 > 元素 > 继承 > 默认

⚠️ 强烈建议 :除非在覆盖第三方组件且别无他法时,否则不要用 !important。一旦用了,后面想覆盖它只能再加 !important,最终变成"important 军备竞赛"。

🔴 :where() --- 权重归零

css 复制代码
:where(.foo, .bar) { color: red; }

:where() 内部的选择器 不贡献任何权重 ,整体权重为 (0, 0, 0, 0)。非常适合写 reset / base 样式。

对比:

css 复制代码
:is(.foo, .bar) { color: red; }    /* 取内部最高权重 → (0,0,1,0) */

🔴 :is() --- 取最高权重

csharp 复制代码
:is(#header, .nav) p { }
/* #header 是 ID (0,1,0,0),.nav 是类 (0,0,1,0) */
/* 取最高 → ID 胜出,所以整体权重 = (0,1,0,0) + (0,0,0,1) = (0,1,0,1) */

🔴 继承的样式

继承来的样式 优先级最低 ,甚至低于通配符 *。

css 复制代码
body { color: gray; }
* { color: black; }    /* 这个会覆盖 body 的继承值 */

六、完整优先级排序(从低到高)

less 复制代码
1. 浏览器默认样式
2. 继承的样式
3. 通用选择器 *、组合符
4. 元素选择器 div p
5. 类选择器 .nav
6. 属性选择器 [type="text"]
7. 伪类 :hover
8. 伪元素 ::before
9. ID 选择器 #header
10. 内联样式 style=""
11. !important

七、源码顺序决胜规则

当优先级完全相同时,后声明的规则胜出:

css 复制代码
.btn { color: red; }
.btn { color: blue; }   /* 这个生效 ✅ */

注意:是在样式表中的出现顺序,不是 HTML 中 class 的排列顺序。


八、常见误区

❌ 误区 1:class 数量多就一定赢

css 复制代码
.nav.item.active.tab.link { color: red; }     /* (0,0,5,0) */
#header { color: blue; }                      /* (0,1,0,0) */

ID 永远赢。5 个类打不过 1 个 ID。

❌ 误区 2:元素多就能覆盖类

css 复制代码
body div ul li a span { color: red; }   /* (0,0,0,6) */
.text { color: blue; }                  /* (0,0,1,0) */

1 个类 > 无数个元素选择器。

❌ 误区 3:HTML 里 class 写前面就优先

arduino 复制代码
<div class="red blue">text</div>
css 复制代码
.blue { color: blue; }
.red { color: red; }

生效的是 .red(后声明),跟 class 在 HTML 中的顺序无关。


九、实用建议

✅ 1. 保持选择器扁平

less 复制代码
/* ❌ 过度嵌套 */
#app .sidebar .menu li a { }

/* ✅ 扁平化 */
.menu-link { }

✅ 2. 用 class 作为主要样式手段

避免依赖 ID 和元素选择器来写样式,这样优先级更可控。

✅ 3. 善用 CSS 自定义属性做覆盖

css 复制代码
.card {
  background: var(--card-bg, white);
}

通过修改变量来覆盖样式,完全不需要操心优先级。

✅ 4. 组件库样式覆盖的正确姿势

css 复制代码
/* 用 :where 降低基础权重 */
:where(.btn) { padding: 8px 16px; }

/* 用普通类提升覆盖能力 */
.btn-primary { background: blue; }
相关推荐
姜鱼问生1 小时前
fail2ban 实战:从默认配置到生产可用
后端·算法·架构
JavaGuide2 小时前
DeepSeek Harness 重磅更新,Codex、Claude Code 子智能体进官方插件列表了,还有 Git 工作树和思考翻译!
前端·后端
Rain的Java大神之路2 小时前
[特殊字符]8年Java老兵转型AI Agent:90%的人挂在同一个坑,根本不用学Python!(万字实战复盘,建议收藏)
java·数据库·人工智能·后端·python·spring·ai编程
高频因子挖掘机3 小时前
A股分钟K线数据占内存:先缩小请求范围,还是换 DataFrame 工具?
后端·github·api
高级程序源3 小时前
springboot大学生体测系统72170-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·django·课程设计
打工仔折腾 AI3 小时前
极空间用 Docker 部署 Photopea:网页版 PS 的私有化实践
运维·后端·python·docker·容器·langchain·ai agent 实战
掘金者阿豪4 小时前
别只会docker stats了:cAdvisor把CPU、内存、磁盘和网络监控搬进Web页面
后端
企业数字化笔记4 小时前
多租户系统从哪里开始设计?租户身份、数据隔离、权限与套餐的整体方案
java·spring boot·后端·junit
10年前端老司机4 小时前
一次业务迭代:聊聊知识库多版本可追溯审核流程怎么做
人工智能·后端·agent