CSS 选择器优先级

小飞兽 HTML/CSS 322 次阅读 2026-05-26

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.1class="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)

    代码示例 2.4

    • 无内联无 !important → 红色(ID 选择器)
    • 加内联 → 橙色(内联覆盖 ID)
    • ID 加 !important → 红色(!important 覆盖内联)

    浏览器 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)
    

    五、延伸阅读

    最佳实践建议:

    1. 尽量避免使用 !important,它会破坏样式的可预测性
  • 优先使用类选择器而非 ID,保持样式表的可复用性
  • 复杂项目中使用 CSS 变量(Custom Properties)管理主题色,减少 specificity 战争
  • 使用 DevTools 的 Styles 面板审查优先级,定位覆盖问题
  • 建立团队 CSS 规范:统一选择器写法,避免深层嵌套(超过 3 层)

深入理解 specificity,是写出可维护、可预测 CSS 的基石。遇到样式覆盖问题时,先打开 DevTools 查权重,再动手写覆盖规则。