CSS 选择器优先级
CSS 选择器优先级
Introduction
在 CSS 开发中,当多条规则作用于同一个元素时,哪条规则生效?答案由选择器优先级(Specificity)决定。这是 CSS 中最核心也最容易出错的机制之一。很多奇怪的样式不生效、覆盖失败、权重冲突问题,追根溯源都是对优先级理解不准确。
CSS 优先级并非简单的"后来者居上",而是一套基于 specificity 计算公式的权重系统。本文将深入解析这套机制的工作原理、计算方法、常见误区以及实战技巧,帮助你在复杂项目中精准控制样式覆盖。
一、基础语法
1.1 选择器类型概览
| 选择器类型 | 示例 | 优先级层级 |
|---|---|---|
| 通配选择器 | * | 最低 |
| 元素选择器 | div, p | 低 |
| 类选择器 | .btn, .active | 中 |
| 属性选择器 | [type="text"], [href^="https"] | 中 |
| 伪类选择器 | :hover, :nth-child(2) | 中 |
| ID 选择器 | #header, #nav | 高 |
| 伪元素选择器 | ::before, ::placeholder | 高 |
| 内联样式 | <div style="color:red"> | 最高 |
1.2 Specificity 计算公式
specificity 用一个四元组 (a, b, c, d) 表示,读取规则如下:
(a, b, c, d)
| | | └── 元素选择器、伪元素选择器(如 div, p, ::before)
| | └───── 类选择器、属性选择器、伪类(如 .btn, [type], :hover)
| └──────── ID 选择器(如 #header)
└─────────── 内联样式(如 style="color:red")
比较规则:从左到右逐个比较,先比较 a,a 大的优先级高;a 相同则比较 b,以此类推。
1.3 !important 的特殊性
!important 会覆盖正常样式规则,但自身仍受 specificity 约束:
/* !important 与 specificity 结合:specificity 高的 !important 仍优先 */
#header .nav li { color: blue !important; } /* (1,1,1) + !important */
.nav li { color: red !important; } /* (0,1,1) + !important */
/* 结果:蓝色生效,因为 (1,1,1) > (0,1,1) */
二、代码示例
2.1 Specificity 计算演示
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>CSS 优先级演示</title>
<style>
/* 优先级 (0,0,1) - 1个元素选择器 */
p { color: gray; }
/* 优先级 (0,1,1) - 1个类 + 1个元素 */
p.intro { color: orange; }
/* 优先级 (0,2,1) - 2个类 + 1个元素 */
p.intro.bold { color: red; }
/* 优先级 (0,1,2) - 1个类 + 2个元素 */
.container p { color: green; }
/* 优先级 (1,0,1) - 1个ID + 1个元素 */
#main p { color: purple; }
/* 优先级 (1,1,1) - 1个ID + 1个类 + 1个元素 */
#main p.intro { color: blue; }
/* 优先级 (0,0,2) - 2个元素 */
body div p { color: yellow; }
</style>
</head>
<body>
<div id="main" class="container">
<p class="intro bold">这段文字的颜色是什么?</p>
</div>
</body>
</html>
2.2 伪类选择器优先级陷阱
/* :not() 本身不增加权重,但它的参数会增加 specificity */
#header:not(.hidden) { color: red; } /* (1,1,0) - ID=1, 括号内类=1 */
#header.nav { color: blue; } /* (1,1,0) - ID=1, 类=1 */
/* 两者优先级相同(1,1,0),后者覆盖前者 */
2.3 属性选择器 vs 类选择器
/* 属性选择器和类选择器优先级相同,都是 (0,1,0) */
[type="email"] { border: 1px solid #ccc; } /* (0,1,0) */
.email-field { border: 1px solid #ccc; } /* (0,1,0) */
/* 同一文件中后面出现的会覆盖前面的(平级时后来居上) */
2.4 行内样式与 !important 综合示例
<!-- HTML -->
<div id="box" class="box primary">测试文字</div>
<!-- CSS -->
#box { color: red; } /* (1,0,0) */
.box { color: green; } /* (0,1,0) */
div { color: blue; } /* (0,0,1) */
.primary { color: purple; } /* (0,1,0) */
/* 最终结果:红色,ID 选择器权重最高 */
<!-- 加内联样式 -->
<div id="box" class="box primary" style="color: orange;">测试文字</div>
/* 内联 (1,0,0,1) > ID (1,0,0),结果:橙色 */
<!-- ID 加 !important -->
#box { color: red !important; } /* (1,0,0) + !important */
/* !important 覆盖内联,结果:红色 */
三、运行效果
代码示例 2.1:class="intro bold" 的 <p> 标签,最终颜色为 蓝色。计算过程如下:
#main p.intro→ (1,1,1) 最高#main p→ (1,0,1).container p→ (0,1,1)p.intro.bold→ (0,2,1)p.intro→ (0,1,1)body div p→ (0,0,2)p→ (0,0,1)- 无内联无
!important→ 红色(ID 选择器) - 加内联 → 橙色(内联覆盖 ID)
- ID 加
!important→ 红色(!important覆盖内联)
代码示例 2.4:
浏览器 DevTools 中可以清楚看到每条规则的优先级标注,在 Elements 面板的 Styles 面板中,规则旁会显示 specificity 值,如 (0,1,0)。
四、常见问题
Q1: 为什么写了 .nav .item { color: red; } 却覆盖不了 .item { color: blue; }?
因为 .item 的 specificity 是 (0,1,0),而 .nav .item 是 (0,2,0),后者更高,正确覆盖了前者。如果发现没覆盖,请检查:选择器拼写是否正确、是否有其他更高优先级的规则(如 ID 选择器)、是否被 !important 阻挡、是否因为层叠顺序被同优先级的后续规则覆盖。
Q2: 通配符 * 的优先级是多少?
(0,0,0),比任何非 * 选择器都低。但注意:* 会增加 CSS 解析开销,生产环境建议慎用。
Q3: !important 和内联样式哪个优先?
!important 更优先。比较方式:先比较是否有 !important,!important > 内联样式 > 普通规则;同为 !important 时,再比较 specificity。
Q4: 如何覆盖一个 !important 规则?
有三种方法:写一个更高 specificity 的 !important 规则;在同文件中写一个相同 specificity 的 !important 规则(后来居上);内联样式无法覆盖 !important,只能找更高 specificity 的 !important。
Q5: :nth-child() 和 .class 哪个优先级高?
:nth-child(2) 是伪类,优先级等于 (0,1,0),与 .class 相同。同优先级下,后定义的规则生效。
Q6: 多个选择器连写(如 div#header.nav)如何计算?
div#header.nav
└─ 元素 div → (0,0,1)
└─ ID header → (1,0,0)
└─ 类 nav → (0,1,0)
合计 → (1,1,1)
五、延伸阅读
最佳实践建议:
- 尽量避免使用
!important,它会破坏样式的可预测性 - 优先使用类选择器而非 ID,保持样式表的可复用性
- 复杂项目中使用 CSS 变量(Custom Properties)管理主题色,减少 specificity 战争
- 使用 DevTools 的 Styles 面板审查优先级,定位覆盖问题
- 建立团队 CSS 规范:统一选择器写法,避免深层嵌套(超过 3 层)
深入理解 specificity,是写出可维护、可预测 CSS 的基石。遇到样式覆盖问题时,先打开 DevTools 查权重,再动手写覆盖规则。