系列文章
- [ 面试系列 ] - 一:你是如何理解 HTML 语义化的?
- [ 面试系列 ] - 二:meta viewport 是做什么用的,怎么写?
- [ 面试系列 ] - 三:H5 是什么?
- [ 面试系列 ] - 四:两种盒模型分别说一下
- [ 面试系列 ] - 五:如何垂直居中?
- [ 面试系列 ] - 六:什么是 BFC?
- [ 面试系列 ] - 七:来看看这个变量提升?
说在前面的
既然要聊 CSS 选择器的优先级,那自然要先搞清楚几个问题:
- 什么是 CSS 选择器?
- CSS 有哪些选择器?
什么是 CSS 选择器?
关于这个问题,我们先把 MDN 的说明贴上:
CSS 选择器规定了 CSS 规则会被应用到哪些元素上。
备注:暂时没有能够选择 父元素、父元素的同级元素,或 父元素的同级元素的子元素 的选择器或者组合器。
这说的是人话,浅显易懂,想必就不用上翻译咕咕咕了。
CSS 有哪些选择器?
至于 CSS 有哪些选择器,我们看下面的表就清楚了:
| 选择器 | 语法 | 例子 |
|---|---|---|
| 通用选择器 | * | - |
| 类型选择器 | 节点名称 | div |
| 类选择器 | . + 类名 | .btn-primary |
| ID 选择器 | # + ID 名 | #form |
| 属性选择器 | [属性名] | [self]、[data-type="hidden"] |
| 伪类 | :伪类名 | :hover、:focus |
| 伪元素 | ::伪元素名 | ::before、::after |
事实上,CSS 还有组合器和组合选择器可以帮助我们更好的选择到目标元素,不过这和优先级关系不大,所以就不放在这里提了,有兴趣的同学可以看看工具书。
CSS 选择器优先级
要说明这个问题,我们先来看下面这个问题:
div {
background-color: red;
}
.div {
background-color: yellow;
}
#div {
background-color: blue;
}
想必大家都很清楚,最后 div 的颜色会是蓝色,那么为什么会出现这种情况呢?这就涉及到 CSS 选择器的优先级了。
关于 CSS 的优先级,大家通常第一时间会想到这个权重表:
| 权重 | 选择器 |
|---|---|
| 1000 | 内联 |
| 0100 | ID 选择器 |
| 0010 | 类、属性、伪类选择器 |
| 0001 | 标签、伪元素 |
总的来说,就是:
内联 > id 选择器 > 类、属性、伪类选择器 > 标签元素、伪元素
而事实上,上面的表是存在一定问题的。
首先,以个十百千的位数计算,会给人一种误解:大于 10 个的类选择器优先级会难道比 1 个 ID 选择器的优先级更高吗?
对于这个问题,我们来看个例子就明白了:
<style>
* {
margin: 0;
padding: 0;
}
div {
width: 100px;
height: 100px;
}
.c1.c2.c3.c4.c5.c6.c7.c8.c9.c10.c11 {
background-color: deeppink;
}
#i1 {
background-color: deepskyblue;
}
</style>
<body>
<div class="c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11" id="i1"></div>
</body>
结果如下:

显然,11 个类选择器优先级也并不比 1 个 ID 选择器的优先级更高,所以这种个十百千的计算方式是容易造成误解的。
想要搞清楚选择器优先级,可以参看 W3C 文章中对其的描述:
A selector’s specificity is calculated for a given element as follows:
- count the number of ID selectors in the selector (= A)
- count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B)
- count the number of type selectors and pseudo-elements in the selector (= C)
- ignore the universal selector

别急,翻译咕这就来:
- 首先,把权重分为 A、B、C 三个等级
- ID 选择器为 A 级
- 类、属性、伪类选择器为 B 级
- 标签、伪元素为 C 级
- 忽略通用选择器
- A > B > C,每个等级单独计算优先级,只有相等的时候才会向后比较,如果全部都相等,则后定义的样式覆盖先定义的样式
上面的文档没有指出内联,当然,根据经验,我们可以把内联当做 S 级,即:
S > A > B > C
通常计算 CSS 属性的时候,我们通过上面的方式就可以确定了,但严格来说,这种说法依然不够严谨,它没有考虑 CSS 的另一个重要特性:层叠(比如 !important)。
(tip:其实从 CSS 的中文名——层叠样式表——都可以见得层叠对于 CSS 的重要性)
层叠
我们先来看看 MDN 对其的描述:
层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称层叠样式表正是强调了这一点。
层叠算法决定如何找出要应用到每个文档元素的每个属性上的值。
其算法核心有如下:
- 首先过滤来自不同源的全部规则
- 对规则进行排序
排序优先级如下:
| 优先级序号 | 来源 |
|---|---|
| 1 | 用户代理 |
| 2 | 用户 |
| 3 | 页面作者 |
| 4 | CSS 动画 |
| 5 | 用户代理 !important |
| 6 | 用户 !important |
| 7 | 页面作者 !important |
| 8 | CSS 过渡 |
原文见:CSS Cascading and Inheritance Level 4
到这里又引出了两个新问题:
- 用户代理是什么,用户是什么,页面作者又是什么?
- 过度这么叼的咯?连
!important都惹不起。
我们先来看第一个问题:
| 用户代理 | 浏览器的默认样式表 |
| 用户 | 浏览器用户自定义的样式表 |
| 页面作者 | 网页作者定义的样式表 |
那么按照上面的规则,在层叠方面,优先级应该如下:
CSS 过渡 > 页面作者 !important > 用户 !important > 用户代理 !important > CSS 动画 > 页面作者 > 用户 > 用户代理
.one,
.two {
display: inline-block;
margin-right: 100px;
width: 100px;
height: 100px;
transition: 1s;
}
.one {
background-color: deeppink !important;
}
.two {
background-color: deeppink;
}
.one:hover,
.two:hover {
background-color: deepskyblue;
}
结果如下:

可见 transition 并没能影响到 !important,而在多平台实际测试下,发现最终优先级排序如下:
Chrome 80.0.3987.163、Safari 13.0.4、Edge 44.18362
CSS 动画 > 页面作者 !important > 用户 !important > 用户代理 !important > CSS 过渡 > 页面作者 > 用户 > 用户代理