Skip to main content

CSS 属性层叠

层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称层叠样式表正是强调了这一点。

根据重要性排序如下,后面的更重要:

  1. 资源顺序/CSS 位置
  2. 优先级
  3. 重要程度

CSS 属性的层叠

层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。

  • CSS 的翻译是层叠样式表,什么是层叠呢?

    • 对于一个元素来说,相同一个属性我们可以通过不同的选择器给它进行多次设置;
    • 那么属性会被一层层覆盖上去;
    • 但是最终只有一个会生效;
  • 那么多个样式属性覆盖上去,哪一个会生效呢?

    • 判断一:选择器的权重,权重大的生效,根据权重可以判断出优先级;
    • 判断二:先后顺序,权重相同时,后面设置的生效;

哪些 CSS 实体会参与层叠计算

只有 CSS 声明,就是属性名值对,会参与层叠计算。这表示包含 CSS 声明以外实体的 @规则不参与层叠计算,比如包含描述符(descriptors)的@font-face。对于这些情形,@规则是做为一个整体参与层叠计算,比如 @font-face 的层叠是由其描述符 font-family 确定的。如果对同一个描述符定义了多次 @font-face,则最适合的 @font-face 是做为一个整体而被考虑的。

包含在大多数 @规则的 CSS 声明是参与层叠计算的,比如包含于@media@documents或者@supports的 CSS 声明,但是包含于@keyframes的声明不参与计算,正如@font-face,它是作为一个整体参与层叠算法的筛选。

最后,注意 @import@charset 遵循特定的算法,并且不受层叠算法影响。

CSS 声明的源

CSS 层叠算法期望通过挑选 CSS 声明来给 CSS 属性设置正确的值。CSS 声明可以有不同的来源:

  • 浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称用户代理样式。一些浏览器通过使用真正的样式表,而其他则通过代码模拟,但无论是哪种情形都应是不可被检测的。而且部分浏览器允许用户修改用户代理样式。尽管 HTML 标准对用户代理样式做了诸多限制,浏览器仍大有可为,具体表现在不同浏览器间会存在重大的差异。为了减轻开发成本以及降低样式表运行所需的基本环境,网页开发者通常会使用一个 CSS reset 样式表,强制将常见的属性值转为确定状态。
  • 网页的作者可以定义文档的样式,这是最常见的样式表。大多数情况下此类型样式表会定义多个,它们构成网站的视觉和体验,即主题。
  • 读者,作为浏览器的用户,可以使用自定义样式表定制使用体验。

尽管 CSS 样式会来自这些不同的源,但它们的作用范围是重叠的,而层叠算法则定义了它们如何相互作用。

层叠顺序

层叠算法决定如何找出要应用到每个文档元素的每个属性上的值。

  1. 它首先过滤来自不同源的全部规则,并保留要应用到指定元素上的那些规则。这意味着这些规则的选择器匹配指定元素,同时也是一个合适的 @规则(at-rule)的一部分。

  2. 其次,它依据重要性对这些规则进行排序。即是指,规则后面是否跟随者 !import 以及规则的来源。层叠是按升序排列的,这意味着来着用户自定义样式表的 !important 值比用户代理样式表的普通值优先级高:

    来源重要程度
    1用户代理样式表中的声明(例如,浏览器的默认样式,在没有设置其他样式时使用)普通
    2用户样式表中的常规声明(由用户设置的自定义样式)普通
    3作者样式表中的常规声明(由 web 开发人员设置的样式)普通
    4CSS 动画见下节
    5作者样式表中的 !important 声明!important
    6用户样式表中的 !important 声明!important
    7用户代理样式表中的 !important 声明!important
    8css 过渡 (css transitions)
  3. 假如层叠顺序相等,则使用哪个值取决于优先级

选择器的权重

按照经验,为了方便比较 CSS 属性的优先级,可以给 CSS 属性所处的环境定义一个权值(权重)

  • !important:10000
  • 内联样式:1000
  • id 选择器:100
  • 类选择器、属性选择器、伪类:10
  • 元素选择器、伪元素:1
  • 通配符:0

一个选择器的优先级可以说是由三个不同的值(或分量)相加,可以认为是百(ID)十(类)个(元素)——三位数的三个位数:

  • ID:选择器中包含 ID 选择器则百位得一分。
  • :选择器中包含类选择器、属性选择器或者伪类则十位得一分。
  • 元素:选择器中包含元素、伪元素选择器则个位得一分。
  • 内联样式style 属性内的样式声明,优先于所有普通的样式,无论其优先级如何。这样的声明没有选择器,但它们的优先级可以理解为 1-0-0-0;即无论选择器中有多少个 ID,它总是比其它任何优先级的权重都要高。
  • !important:可以用来覆盖所有上面所有优先级计算,用于修改特定属性的值,能够覆盖普通规则的层叠。
备注
  • 通用选择器(*)、组合符(+>~、' ')和调整优先级的选择器(:where())不会影响优先级。

  • !important 改变了层叠的常规工作方式,它会使调试 CSS 问题非常困难,特别是在大型样式表中。

  • 覆盖 !important 唯一的办法就是另一个 !important 具有相同优先级而且顺序靠后,或者更高优先级。

    • 只需再添加一条 带 !important 的 CSS 规则,再给这个给选择器更高的优先级(添加一个标签,ID 或类);或是添加一样选择器,把它的位置放在原有声明的后面(总之,最后定义一条规则比胜)。

      table td {
      height: 50px !important;
      }
      .myTable td {
      height: 50px !important;
      }
      #myTable td {
      height: 50px !important;
      }
    • 使用相同的选择器,但是置于已有的样式之后:td { height: 50px !important; }

    • 干脆改写原来的规则,以避免使用 !important

否定(:not())和任意匹配(:is())伪类本身对优先级没有影响,但它们的参数则会带来影响。参数中,对优先级算法有贡献的参数的优先级的最大值将作为该伪类选择器的优先级。

选择器ID元素优先级
h10010-0-1
h1 + p::first-letter0030-0-3
li > a[href*="en-US"] > .inline-warning0220-2-2
#identifier1001-0-0
button:not(#mainBtn, .cta)1011-0-1
一些经验法则
  • 一定要优先考虑使用样式规则的优先级来解决问题而不是 !important
  • 只有在需要覆盖全站或外部 CSS 的特定页面中使用 !important
  • 永远不要在你的插件中使用 !important
  • 永远不要在全站范围的 CSS 代码中使用 !important
  • 与其使用 !important,你可以:
    • 更好地利用 CSS 级联层 @layer属性
    • 使用更具体的规则。在您选择的元素之前,增加一个或多个其他元素,使选择器变得更加具体,并获得更高的优先级。div#test span
    • 对于上一种的一种特殊情况,当您无其他要指定的内容时,请复制简单的选择器以增加特异性。.myClass.myClass span
<div class="css-cascade-demo">
<div class="normal">
<p>这段文字是使用元素选择器 div.css-cascade-demo p</p>
<div class="fz-class">
<p>这段文字使用类选择器 div.css-cascade-demo div.fz-class</p>
<div title="fz-title">
<p>这段文字使用属性选择器 div.css-cascade-demo div[title="fz-title"]</p>
<div id="fz-id">
<p>这段文字使用 id 选择器 div.css-cascade-demo div#fz-id</p>
<div class="fz-style">
<p style="font-size: 30px;">这段文字使用了内联样式,字体大小为 30px.</p>
<div class="fz-important">
<p>这段文字使用了类选择器 + !important 声明,字体大小为 36px.</p>
<div id="fz-important">
<p>这段文字使用了 id 选择器 + !important 声明</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
http://localhost:3000/css-cascade.html

这段文字是使用元素选择器 div.css-cascade-demo p

这段文字使用类选择器 div.css-cascade-demo div.fz-class

这段文字使用属性选择器 div.css-cascade-demo div[title="fz-title"]

这段文字使用 id 选择器 div.css-cascade-demo div#fz-id

这段文字使用了内联样式,字体大小为 30px.

这段文字使用了类选择器 + !important 声明,字体大小为 36px.

这段文字使用了 id 选择器 + !important 声明