Skip to content

层叠就是解决声明冲突的过程。

以 a 元素为例,浏览器会给 a 元素设置一个默认样式,例如字体颜色,我们也可以给 a 元素设置字体颜色,当我们给 a 元素设置字体颜色之后,我们设置的颜色就和浏览器默认样式冲突了,这就是一个声明冲突。声明冲突并不止发生在我们自己的样式和浏览器样式之间,很多时候我们写的代码自己都会有声明冲突。

css
a{
	color:red;
}
.active{
	background:red;
	color:white;
}
html
<a href="">Lorem.</a>
<a href="" class="active">Deleniti.</a>
<a href="">Facere!</a>
<a href="">Facilis!</a>
<a href="">Itaque.</a>

以上代码,给所有 a 元素设置了红色,并且让其中一个 class 属性为 active 的 a 元素设置了一个白色字体,并且让其背景变为了红色。我们会发现,对于类名为 active 的元素,它本身也是 a 元素,我们给所有的 a 元素设置了红色的字体颜色,而我们又单独给他设置了白色的字体颜色,与此同时浏览器本身又给 a 元素设置了一个默认的字体颜色,所以这一个 a 元素其实被设置了三次字体颜色,那 a 元素最终总得选定一个颜色吧,这个选择最终颜色的过程,其实就是层叠。

说抽象一点,一个元素可能会被设置多次相同的属性,例如有两个 CSS 规则给同一个元素设置了两个冲突的属性,例如一个让字体变红,一个让字体变黑,这就产生了声明冲突,这个元素最终只会在这些冲突规则中选定一个规则,这个选定规则的过程就是重叠,这个过程也是解决声明冲突的过程。

在一些教程中称重叠为 CSS 权重的计算,这种说法稍显片面

重叠一共分为三步,经过以下三步之后,最终一定能选中一个唯一的样式作用于元素。

  1. 比较重要性
  2. 比较特殊性
  3. 比较源次序

用语言来叙述就是,当属性发生冲突时,浏览器会先比较重要性,选择一个更为重要的规则,如果规则同等重要,那么就比较特殊性,选取更为特殊的规则,如果特殊性也一样,则比较源次序,选择源次序最靠后的规则。

比较重要性实际上就是看其是作者样式表还是浏览器默认样式表,作者样式表优先级一定高于浏览器默认样式表,同时在浏览器默认样式表中,带上 important 语法修饰的样式是最重要的样式,拥有最高优先级。
比较特殊性本质上是在比较选择器,总体规则就是选择器的范围越窄越特殊。

重要性排序

  1. 作者样式表中的important 样式
  2. 作者样式表中的普通样式
  3. 浏览器默认样式表中的样式

important 样式

CSS 属性允许我们使用 important 修饰,在属性值后添加 !important 即可,如下:

css
a{
	color:red !important;
}

important 修饰的样式拥有最高优先级,但在任何情况之下,都不太推荐使用 important 修饰

如果经过重要性比较之后还出现冲突,比如冲突的规则都是同等重要性,则需要比较特殊性,选择比较特殊的样式,特殊性本质上是根据选择器计算出一个四位数(XXXX)。

这四位数的计算规则是按位计算的

  • 千位:如果是内联样式则记作 1,否则记为 0
  • 百位:选择器中所有 id 选择器的数量
  • 十位:选择器中所有类选择器、属性选择器、伪类选择器的数量
  • 个位:选择器中所有元素选择器、伪元素选择器的数量

比较规则本身是从高位向低位的,这也符合我们算术比较的逻辑,即先选择千位更高的,若千位相同,选择百位更高的,若百位相同,则选择十位更高的,若十位相同,选择个位更高的。这里还需要注意的是,这里的个、十、百、千其实是一个形象的说法,这是四个独立的数字,并没有进位这一说法。

如果不用个十百千这种形象的说法,大概就可以描述为如下文字:

  1. 如果是内联样式,选择内联样式
  2. 如果都不是内联样式,选择 id 选择器数量更多的样式
  3. 如果 id 选择器数量形同,把类选择器、属性选择器、伪类选择器数量分别相加,选择数量最多的那一个
  4. 如果还是选择不出来,则将元素选择器、伪元素选择器数量相加,选择出最多的那一个

关于权重进位

一些资料或者一些老师可能会说权重是 256 进制,或者说 256 个类选择器等价于一个 id 选择器,但实际上并不是,每一位是进制都可以认为是无穷,或者说根本不存在进位,这是本人亲手测试的得出的结论,读者不信也可以自己去试。MDN 也没有提到进位这一说法,256 进制的说法可能源于浏览器远古版本的设计缺陷。

若特殊性也一样,则根据源次序选择,选择代码顺序靠后的样式,代码顺序这一点一定可以解决冲突。

一般来说,我们记注以下原则即可

  • 我们写的样式一定可以覆盖浏览器默认样式
  • important 修饰的样式拥有最高优先级,甚至超过内联样式
  • 从权重考虑,内联样式>id 选择器> 类选择器=属性选择器=伪类选择器 > 元素选择器 = 伪元素选择器
  • 一般有 id 选择器则比较 id 选择器的数量,如果一样多,才比较类选择器的数量
  • 同样权重的基础下,后写的样式优先级更高

层叠这一块看起来很难很多,但实际上开发中很少会碰到,如果设置了某个样式没有生效,我们才会去主动考虑是不是层叠或者说权重的问题,所以不要怕。

层叠的重要应用就是重置样式表,一般来说,我们会专门写一个叫重置样式表的文件,每次写页面之前都会先引入这个重置样式表文件,这个重置样式表专门用于覆盖浏览器的默认样式,以消除不同浏览器默认样式的误差。这个重置样式表可以自己写,也可以用现成的,比如 normalize.css 或者 reset.css 或者 meyer.cee

为什么是爱恨法则

爱恨法则在 3.选择器 介绍过,所谓爱恨法则,就是书写伪类样式时,应该按照 link, visited, hover, active 的顺序来书写。
首先,这四个选择器的优先级都相同,所以影响优先级的只有书写顺序,所以这么写本质上还是考虑到书写顺序,即后写的样式会覆盖先前样式。
这里一步一步把这个顺序推出来,首先,link 表示未被访问的样式,如果 link 写在 hover 和 active 之后,会导致未被访问链接的 hover 和 active 失效,这个其实很好理解,首先对于一个没有访问的超链接,我把鼠标悬浮上去,此时它被 hover 和 link 两个选择器选择,如果把 link 写在了 hover 后面,那会优先使用 link 的规则,这就直接导致了 active 设置的样式失效。
hover 必须写在 active 之前,因为 active 本身是鼠标按下的样式,鼠标按下的时候,鼠标一定是悬浮在元素上的,也就是说鼠标你得先悬浮到元素上面你才能按下它,所以 active 理应比 hover 有更高的优先级,否则不管你按不按下使用的都是 hover 的样式,active 就直接失效了。
visited 必须写在 hover 之前,如果 visited 在 hover 之后,那么 visited 比 hover 有更高的优先级,那么对于一个被访问过的链接,不管鼠标是否悬停在上面,应用的都会是 visited 的样式,这里就导致了 hover 样式的失效。
至于 link 和 visited 的顺序,其实无所谓,只是习惯上将 link 写在 visited 之前。

以笔记为镜,可以知得失