CSS 保姆级指南 --- 层叠、继承、属性值计算

198 阅读7分钟

层叠

层叠这个概念源自 CSS 的全称 --- 层叠样式表(Cascading Style Sheets),是 CSS 中最基础的概念之一。

通常页面元素的样式受多个因素的影响,作者样式表(开发者编写的 CSS 文件)、浏览器默认样式表元素标签的属性都可以决定一个元素在页面的样式。

那么同一种样式,就可能多次应用到同一个元素上,从而发生声明冲突。而浏览器自动处理声明冲突,决定元素的某一个样式具体用哪一个声明的过程就是层叠。

层叠.png


层叠解决声明冲突的手段是权重计算
权重计算从前到后主要涉及 3 个参考维度:重要性特殊性源次序

发生声明冲突时,首先比较重要性,重要性指的是各冲突声明的来源,它们的重要性依次是:

  • 作者样式表中的 !important 样式
  • 作者样式表中的普通样式
  • 浏览器默认样式表

除非万不得已,否则不推荐使用 !important,因为其权重太高,如果未来要覆盖它,只能用另外一个 !important。长此以往,样式表中就会充满 !important,增加代码维护难度。


最终,元素会应用重要性最高的样式声明。
如果重要性一致,则继续比较各冲突声明的特殊性。

特殊性主要比较的是声明冲突样式的选择器选中的范围,选择器选择的范围越窄,则越特殊
具体规则:通过选择器,计算出一个 4 位数(x-x-x-x)特殊值,哪个选择器的特殊值大,哪个就越特殊,它御下的样式代码越可能在冲突中胜出。

⭐4 位数的计算

  • 千位,如果是内联样式,记 1,否则记 0
  • 百位,等于所有选择器中 id 选择器的数量
  • 十位,等于选择器中所有类选择器属性选择器伪类选择器的数量
  • 个位,等于选择器中所有元素选择器伪元素选择器的数量
  • 另外通配符选择器子元素选择器兄弟元素选择器等也具有特殊性,不过它们的特殊性极低,以至于比 4 位数的个位权重还小,但仍比没有特殊性的继承属性值权重高

特殊值的位数之间只要相差 1,差距就会很大,因为它们是 256 的进制,100 个属性选择器的权重也没 1 个 id 选择器的高。

权重计算.png

当前面两个参考维度都相同时,则比较源次序,代码书写靠后的样式胜出。


利用层叠,我们经常做的操作就是重置浏览器默认样式,因为不同浏览器默认样式不同,可能导致网页在不同浏览器展示效果不同,而作者样式表的样式总会覆盖浏览器样式表。

常用的重置样式表:normalize.css、reset.css、meyer.css

另外一个常见的应用就是 a 标签的 “爱恨法则”,利用层叠让 a 标签的 4 种伪类选择器设置的样式都正确显示。
详情可参考博文:CSS “爱恨原则”

继承

继承.png

CSS 具有继承机制,某些样式不仅会应用到指定元素,还会应用到它的后代元素

这个机制是严格单向的,也就是说某个元素的某个 CSS 属性值绝不会向上传播。

具体某个 CSS 属性能不能被继承,需要查阅 MDN 文档

不过有一些经验性总结,可以帮助记忆,通常:

  • 大多数盒模型属性都不能继承,如:marginpaddingborderbackground、...
    • 原因是可以想象的,如果这些属性能继承,那文档将会变得很混乱
  • 大多数与文字内容相关的属性都能继承,如:colorfont-sizefont-family、...

如果某个 CSS 属性是可继承的,且在某一元素上声明了该值,那么该值就会沿着元素树一直向下传递,直到没有更多的后代元素来继承这个值。

基于此,且由于大多数情况下同一个页面字体和文字风格相同,开发者会在 htmlbody 标签统一声明页面文字相关的 CSS 属性。

传递.png

在谷歌浏览器调试工具中可以清楚的知道哪些样式是继承的,还有继承于谁。

浏览器继承.png

带有 Inherited from xxx 标题的模块描述的便是继承属性。
亮色的属性代表的是继承值应用成功的属性,暗色代表的是没有继承成功的祖先元素声明的其他属性。


另外重要的一点是,继承的值没有特殊性,不参与权重计算
所以当期望的继承效果没有展现时,可以从以下几个方面排查:

  • CSS 属性本身是否具备可继承性
  • 是否针对元素该属性已经有了作者样式表声明,继而导致层叠机制掩盖了继承机制
  • 是否针对元素该属性已经有了浏览器默认样式表声明,继而导致层叠机制掩盖了继承机制

既然层叠会从多方面影响继承,那么继承岂不是很不好管理?
于是,CSS 为控制继承提供了五个特殊的通用属性值(所有 CSS 属性都可以接收的值)。

  • inherit:开启继承,设置该值会使子元素属性值和父元素相同【不管该属性是否具有可继承性
  • initial:设置属性值为默认值
  • revertrevert-layerunset 平时基本很少用,而且用处也不大

至于为什么层叠会掩盖继承,请参见接下来的 CSS 属性值计算篇章。

CSS 属性值计算

页面渲染是按照 html 文档的树形结构顺序,一个元素一个元素依次渲染的,而渲染每个元素的前提条件就是:该元素的所有 CSS 属性都必须有值

从常识出发都可以感觉到,如果不是所有样式都有确切的值,那浏览器如何对其具体绘制。

一个元素从所有 CSS 属性都没值,到所有属性都有值的过程就是 CSS 属性值计算过程。

计算过程.png

计算过程一共有 4 步,层叠继承都是其中的步骤之一,单靠某一个机制并不能最终决定 CSS 属性的具体值。

  • 确定声明值,这一步会把样式表(包括:作者样式表、浏览器默认样式表)中没有冲突的声明作为 CSS 属性值。

这一步后,但凡声明过且没有声明冲突的 CSS 属性都有了具体值,不会再受后面步骤影响。

  • 层叠冲突,对样式表有冲突的声明使用层叠规则,确定其 css 属性值。

这一步后,有声明冲突的 CSS 属性也有了具体值,至此,样式表上书写过的 CSS 属性已确定完成。

  • 继承,经过前面两步,对仍然没有值的属性,若可以继承,则继承父元素的值。

这一步针对的是一个元素所有的,没有在样式表中声明的,且有可继承性的 CSS 属性。
因为这一步在层叠之后,所以层叠才能掩盖继承。

  • ④ 经过前三步,对仍然没有值的属性,使用默认值

了解了这个过程后,我们知道 CSS 样式不是单纯地靠解决声明冲突来确定的,许多现象也都能理解了,比如经典的:a 元素无法继承父元素颜色的问题

a.png

a1.png

普通情况下,a 元素无法继承父级元素的颜色值。

这是由于 a 元素在浏览器的默认样式表里设置有颜色值。根据属性值计算过程,第三步继承,是在前两步属性没有获得属性值的情况下才生效。

a2.png

a 元素在第一步确定声明值的时候,color 属性就已经从浏览器默认样式表获得 color 属性值了,自然不会走第三步。

解决思路:浏览器默认样式表不能改变,a 元素 color 属性的确定只能是在第 ① 步,于是只能在作者样式表显示声明这个属性。

解决:用 a { color: inherit; } 强制其继承父元素的值。

写在最后

One day you'll leave this world behind.So live a life you will remember! --- Avicii

我是暮星,一枚有志于在前端领域证道的攻城狮。

优质前端内容持续输出中......,欢迎点赞 + 关注 + 收藏

点赞.jpeg