本文带你深入理解 CSS 的选择器、内联样式、!importment的优先级。本章主要从重要程度特异性(specificity)层叠顺序三个方面来讨论。

为了深入了解这篇文章,我们先来熟悉一下一些基本定义

一、CSS 部分基础知识

1. CSS 规则组成

我们先来看一条常见的 css 规则:

1
div { font-size : 16px }

它的组成部分如下图所示:

从图中我们可以看出,一条 css 规则由一个选择器 + 多条声明组成。声明又是由属性属性值组成。而我们平常对比优先级就是同一个属性,在不同css规则下面的优先级。

2. CSS 选择器种类

选择器有很多种,我们在熟悉一下:

类别 举例
通用选择器 *
元素(标签)选择器 pdiv
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,没有给出具体值,但我认为这个值肯定肯定很大,>手动滑稽<

这样我们比较两个规则的优先级时,就可以按照权重的 abc 依次进行比较,例如:
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 的优先级问题,现在就解决了,那就是比较两个规则的 层叠来源,谁的来源顺序优先,谁优先级就高,当来源一样时,哪条规则在代码文件靠后,谁就优先。