CSS 选择器优先级与组合
CSS 选择器的优先级计算、组合选择器、伪类与伪元素的使用边界。
#tech / dev / frontend
#resource / css
#type / concept
#status / growing
[!info] related notes
- 所属 MOC: CSS MOC
- 并列概念: CSS 选择器、伪类与伪元素
- 相关概念: css-variable
CSS 选择器优先级与组合
定义
选择器优先级(Specificity)是浏览器决定”当多条规则冲突时,哪条生效”的机制。它不是简单的”后写的覆盖先写的”,而是一套基于选择器类型的权重计算系统。
优先级计算
优先级用一个四元组 (a, b, c, d) 表示:
- a:
!important(尽量避免使用) - b:ID 选择器的数量
- c:类选择器、属性选择器、伪类的数量
- d:元素选择器、伪元素的数量
比较规则:从左到右逐位比较,数值大的优先。
/* 优先级: (0, 0, 0, 1) */
p { color: black; }
/* 优先级: (0, 0, 1, 0) — 更高 */
.text { color: blue; }
/* 优先级: (0, 1, 0, 0) — 更高 */
#title { color: red; }
/* 优先级: (0, 0, 1, 1) */
p.intro { color: green; }
/* 优先级: (0, 0, 2, 1) */
div p.intro { color: purple; }
常见选择器的优先级
| 选择器 | 优先级 |
|---|---|
*(通配符) | (0, 0, 0, 0) |
p(元素) | (0, 0, 0, 1) |
.class(类) | (0, 0, 1, 0) |
:hover(伪类) | (0, 0, 1, 0) |
[type="text"](属性) | (0, 0, 1, 0) |
#id | (0, 1, 0, 0) |
style 属性 | (1, 0, 0, 0) |
!important | 最高 |
特殊情况
- 通配符
*不影响优先级 :not()本身不计优先级,但其参数计:is()和:where()中,:where()的优先级始终为 0- 相同优先级时,后定义的规则覆盖先定义的
组合选择器
/* 后代选择器:匹配 .container 内所有 p */
.container p { }
/* 子元素选择器:只匹配直接子元素 */
.nav > li { }
/* 相邻兄弟选择器:紧跟其后的兄弟 */
h2 + p { }
/* 通用兄弟选择器:之后的所有兄弟 */
h2 ~ p { }
组合选择器对优先级的影响
/* 后代选择器:两个元素选择器 */
div p { } /* (0, 0, 0, 2) */
/* 交集选择器:类 + 元素 */
p.active { } /* (0, 0, 1, 1) */
/* 多类选择器 */
.btn.primary { } /* (0, 0, 2, 0) */
常用伪类
/* 状态伪类 */
a:hover { }
input:focus { }
button:active { }
a:visited { }
/* 结构伪类 */
li:first-child { }
li:last-child { }
li:nth-child(2n) { } /* 偶数行 */
li:nth-child(3n+1) { } /* 每三个一组的第一个 */
li:not(.disabled) { }
/* 表单伪类 */
input:valid { }
input:invalid { }
input:required { }
input:disabled { }
input:checked { }
常用伪元素
/* 虚拟内容 */
.icon::before { content: "★"; }
.tooltip::after { content: attr(data-tip); }
/* 文本样式 */
p::first-line { font-weight: bold; }
p::first-letter { font-size: 2em; }
/* 选中样式 */
::selection { background: yellow; }
/* 占位符 */
input::placeholder { color: #999; }
实际开发中的经验
避免过度嵌套
/* 不推荐:高优先级,难覆盖 */
body .main .content .card .title .text { }
/* 推荐:低优先级,易维护 */
.card-text { }
不要滥用 !important
/* 不推荐 */
.btn { background: red !important; }
/* 推荐:用更具体的选择器 */
.btn.btn-primary { background: red; }
伪类 vs 伪元素
- 伪类用一个冒号
:— 描述元素的状态 - 伪元素用两个冒号
::— 描述元素的虚拟部分 - 为了兼容老浏览器,
::before和::after常写成:before和:after
边界
- 内联样式(
style="")优先级高于所有选择器(除了!important) @layer中的层叠顺序可以覆盖优先级规则- 优先级是选择器级别的,不是属性级别的
- 相同优先级、相同来源时,后出现的规则覆盖先出现的
- 伪类和属性选择器的优先级相同
一句话记忆法
优先级按 ID > 类 > 元素 比较;伪类看状态(:hover),伪元素看部分(::before);少用 !important,用更具体的选择器。