本文带你深入理解 CSS 的选择器、内联样式、!importment的优先级。本章主要从重要程度、特异性(specificity)、层叠顺序三个方面来讨论。
为了深入了解这篇文章,我们先来熟悉一下一些基本定义
一、CSS 部分基础知识
1. CSS 规则组成
我们先来看一条常见的 css 规则:
1 | div { font-size : 16px } |
它的组成部分如下图所示:
从图中我们可以看出,一条 css 规则由一个选择器
+ 多条声明
组成。声明
又是由属性
和属性值
组成。而我们平常对比优先级就是同一个属性
,在不同css规则下面的优先级。
2. CSS 选择器种类
选择器有很多种,我们在熟悉一下:
类别 | 举例 |
---|---|
通用选择器 | * |
元素(标签)选择器 | p 、div |
id选择器 | #name |
类选择器 | .box 、.class |
伪类选择器 | :hover 、:first-child 、:not() |
属性选择器 | input[type=button] 、img[src$='xxx.png'] |
伪元素选择器 | ::before 、::after'] 、::first-letter |
上面就是列举出来的简单选择器
,类似于 后代选择器
、相邻兄弟选择器
都是由简单选择器组成的,可以称为复杂选择器。更详细的选择器分类请阅读 CSS 选择器 、 CSS Selectors Level 3
有的人说,为什么没有列举出来内联样式
和!important
,这是因为:
内联样式
规则的组成不含有选择器,只有样式声明!important
是样式声明中属性值的一部分,和选择器是两种概念
熟悉了这些定义,现在就让我们来分析优先级吧
二、重要程度
我们对比以下两张图片:
我们可以发现,第一张图片内联样式没有生效,第二张图片去掉了!important
后内联样式才生效。
我们可以总结出一些经验和结论:
- 当在一个样式声明中使用
!important
时,此声明将覆盖任何其他样式声明
。虽然,从技术上讲,!important
与优先级无关,但它与最终的结果直接相关。 内联样式
总会覆盖外部样式表的任何样式声明(除了含有!important
的样式声明),因此可看作是具有最高的优先级,参考CSS level 2。
虽然
!important
的优先级无限高,但是建议不要随便使用。
可能会有人问,如果我两个CSS规则里面都出现了
!important
,那优先级应该是什么样子的,这个问题就需要我们学习选择器的特异性(specificity)
,也就是我们常说的权重
。
三、选择器权重
为了方便理解,本文将使用
权重
来代表选择器的特异性
1. 权重规则
在参考W3C官网 CSS Selectors Level 3 文档后,我对选择器权重的计算规则是这样总结的:
首先创建一个 a-b-c
,这样的一个表达式(初始是 0-0-0
),然后按照下面的规则进行计算。
- 计算选择器中
ID 选择器
的数量,等于a
- 计算选择器中
类选择器
、属性选择器
和伪类选择器
的数量,等于b
- 计算
元素选择器
和伪元素选择器
的数量,等于c
通用选择器
、组合符 (+, >, ~, ' ')
不参与计算,不会影响优先级否定伪类 :not(selector)
中的简单选择器按照上面的规则进行计算,但否定伪类
本身(:not)不参与计算,尽管它也是一个伪类选择器
2. 权重举例
通过上面的操作,我们就可以算出一个CSS 选择器的权重。举个例子:
选择器 | 权重 |
---|---|
#name |
1-0-0 |
.user .info .name |
0-3-0 |
.box *[src=xx]:hover |
0-3-0 |
a span::before |
0-0-3 |
a span::before |
0-0-3 |
nav > a:hover::before |
0-1-3 |
ul#primary-nav li.active |
1-1-2 |
* |
0-0-0 |
ul ol+li |
0-0-3 |
#user .name :not(p) |
1-1-1 |
.a .b .c .d .e .f .g .h .i .j .k .l .m |
0-13-0 |
上面的例子都可以在网站 Specificity Calculator 上计算权重
注意: 我们发现最后一个例子的权重是 0-13-0
,中间的13没有进位,这是因为他们的进制数不是2也不是10,查看W3C官网上面只说是 in a number system with a large base
,没有给出具体值,但我认为这个值肯定肯定很大,>手动滑稽<
这样我们比较两个规则的优先级时,就可以按照权重的 a
、b
、c
依次进行比较,例如:1-0-0
> 0-2-0
> 0-1-4
,谁的权重大,谁优先级就高。
这样我们上面说的,两个规则都使用了
!important
的优先级问题,就暂时解决了,那就是比较两个规则的权重
,谁的权重大,谁优先级就高,但是有的人问了,如果这俩规则权重
也一样呢?所以我刚才只说了暂时解决,因为还有一个影响优先级的概念要我们了解,那就是层叠顺序
四、层叠顺序
W3C官网 CSS Cascading and Inheritance Level 3 文档中对层叠顺序的排序为:
顺序 | 来源 |
---|---|
1 | css 过渡(css transitions) |
2 | 用户代理 !important |
3 | 用户 !important |
4 | 页面作者 !important |
5 | CSS动画 |
6 | 页面作者 |
7 | 用户 |
8 | 用户代理 |
用户代理->浏览器默认样式; 用户->浏览器用户自定义样式; 页面作者->网页作者定义的样式表
此列表中数字小的来源的声明胜过数字大的来源的声明。
只有 CSS 声明,就是属性名值对,会参与层叠计算。这表示包含 CSS 声明以外实体的@规则不参与层叠计算,比如包含描述符(descriptors)的 @font-face 。对于这些情形,@规则是做为一个整体参与层叠计算,比如 @font-face 的层叠是由其描述符 font-family 确定的。如果对同一个描述符定义了多次 @font-face,则最适合的 @font-face 是做为一个整体而被考虑的。
包含在大多数@规则的 CSS 声明是参与层叠计算的,比如包含于 @media、 @documents 或者 @supports 的 CSS 声明,但是包含于 @keyframes 的声明不参与计算,正如 @font-face,它是作为一个整体参与层叠算法的筛选。
最后,注意 @import 和 @charset 遵循特定的算法,并且不受层叠算法影响。
经过层叠顺序,两个规则都使用了
!important
的优先级问题,现在就解决了,那就是比较两个规则的层叠来源
,谁的来源顺序优先,谁优先级就高,当来源一样时,哪条规则在代码文件靠后,谁就优先。