CSS 層疊樣式表簡介

**層疊**是一種演算法,它定義了使用者代理如何組合來自不同來源的屬性值。層疊定義了來源和層,當多個來源層疊層@scope塊為元素上的某個屬性設定值時,優先順序最高的來源和層將優先使用。

層疊位於 CSS 的核心,正如其名稱所強調的那樣:**層疊**樣式表。當選擇器匹配某個元素時,將應用來自優先順序最高的來源的屬性值,即使來自較低優先順序來源或層的選擇器具有更高的特異性

本文解釋了什麼是層疊以及CSS宣告層疊的順序,涵蓋層疊層和來源型別。瞭解來源優先順序是理解層疊的關鍵。

來源型別

CSS 層疊演算法的工作是按順序選擇 CSS 宣告,以確定 CSS 屬性的正確值。CSS 宣告來自不同的來源型別:**使用者代理樣式表**、**作者樣式表**和**使用者樣式表**。

儘管樣式表來自這些不同的來源,並且可以在這些來源中的每個來源的不同中,但它們在預設作用域方面存在重疊;為了使這能夠正常工作,層疊演算法定義了它們如何互動。在處理互動之前,我們將在接下來的幾個部分中定義一些關鍵術語。

使用者代理樣式表

使用者代理或瀏覽器具有基本樣式表,這些樣式表為任何文件提供預設樣式。這些樣式表稱為**使用者代理樣式表**。大多數瀏覽器為此目的使用實際的樣式表,而其他瀏覽器則在程式碼中模擬它們。最終結果是一樣的。

一些瀏覽器允許使用者修改使用者代理樣式表,但這很少見,並且無法控制。

雖然 HTML 規範對使用者代理樣式表設定了一些約束,但瀏覽器擁有很大的自由度:這意味著瀏覽器之間存在一些差異。為了簡化開發過程,Web 開發人員可以使用 CSS 重置樣式表,例如normalize.css,它在開始進行更改以滿足其特定需求之前,將所有瀏覽器的常用屬性值設定為已知狀態。

除非使用者代理樣式表在某個屬性旁邊包含!important,使其成為“重要”,否則作者樣式(包括重置樣式表)宣告的樣式將優先於使用者代理樣式,而不管關聯選擇器的特異性如何。

作者樣式表

**作者樣式表**是最常見的樣式表型別;這些是 Web 開發人員編寫的樣式。如上所述,這些樣式可以重置使用者代理樣式,並定義給定網頁或應用程式的設計樣式。作者或 Web 開發人員使用一個或多個連結或匯入的樣式表、<style>塊和使用style屬性定義的內聯樣式來定義文件的樣式。這些作者樣式定義了網站的外觀——其主題。

使用者樣式表

在大多數瀏覽器中,網站的使用者(或讀者)可以選擇使用自定義的使用者樣式表來覆蓋樣式,以根據使用者的意願定製體驗。根據使用者代理的不同,使用者樣式可以進行配置,可以直接配置或透過瀏覽器擴充套件程式新增。

層疊層級

層疊順序基於來源型別。每個來源型別內的層疊基於該型別中層疊層的宣告順序。對於所有來源——使用者代理、作者或使用者——樣式可以在命名或匿名層內或層外宣告。當使用layerlayer()@layer宣告時,樣式將被放置到指定的命名層中,或者如果沒有提供名稱則放置到匿名層中。在層之外宣告的樣式被視為屬於最後一個宣告的匿名層的一部分。

在深入探討每個來源型別中的層疊層之前,讓我們先了解一下層疊來源型別。

層疊順序

層疊演算法決定如何為每個文件元素的每個屬性查詢要應用的值。以下步驟適用於層疊演算法

  1. 相關性:它首先過濾來自不同來源的所有規則,以僅保留適用於給定元素的規則。這意味著選擇器匹配給定元素並且是適當media at-rule一部分的規則。
  2. 來源和重要性:然後根據其重要性對這些規則進行排序,即它們後面是否緊跟著!important,以及它們的來源。暫時忽略層,層疊順序如下
    順序(從低到高) 來源 重要性
    1 使用者代理(瀏覽器) 普通
    2 使用者 普通
    3 作者(開發者) 普通
    4 CSS @keyframe 動畫
    5 作者(開發者) !important
    6 使用者 !important
    7 使用者代理(瀏覽器) !important
    8 CSS 過渡
  3. 特異性:如果來源相同,則會考慮規則的特異性來選擇一個值或另一個值。比較選擇器的特異性,特異性最高的宣告獲勝。
  4. 作用域臨近性:當優先順序相同的來源層中的兩個選擇器具有相同特異性時,作用域規則中到作用域根的DOM層次結構向上跳躍次數最少的屬性值獲勝。有關更多詳細資訊和示例,請參閱如何解決@scope衝突
  5. 出現順序:在優先順序相同的來源中,如果某個屬性存在競爭值,這些值位於匹配特異性和作用域臨近性相同的選擇器的樣式塊中,則應用樣式順序中的最後一個宣告。

層疊順序是升序,這意味著動畫優先於普通值,無論這些值是在使用者、作者還是使用者代理樣式中宣告的,重要值優先於動畫,過渡優先於重要值。

注意: 過渡和動畫

由動畫@keyframes設定的屬性值比所有普通樣式(沒有設定!important的樣式)更重要。

transition中設定的屬性值優先於所有其他設定的值,即使是標記為!important的值。

層疊演算法在特異性演算法之前應用,這意味著如果:root p { color: red;}在使用者樣式表(第 2 行)中宣告,而較不特定的p {color: blue;}在作者樣式表(第 3 行)中,則段落將為藍色。

基本示例

在更深入地瞭解層疊層如何影響層疊之前,讓我們來看一個涉及跨各種來源的多個 CSS 源的示例,並逐步完成層疊演算法的步驟

這裡我們有一個使用者代理樣式表、兩個作者樣式表和一個使用者樣式表,HTML 中沒有內聯樣式

使用者代理 CSS

css
li {
  margin-left: 10px;
}

作者 CSS 1

css
li {
  margin-left: 0;
} /* This is a reset */

作者 CSS 2

css
@media screen {
  li {
    margin-left: 3px;
  }
}

@media print {
  li {
    margin-left: 1px;
  }
}

@layer namedLayer {
  li {
    margin-left: 5px;
  }
}

使用者 CSS

css
.specific {
  margin-left: 1em;
}

HTML

html
<ul>
  <li class="specific">1<sup>st</sup></li>
  <li>2<sup>nd</sup></li>
</ul>

在這種情況下,li.specific規則內的宣告應該應用。

再次強調,層疊演算法有五個步驟,按順序執行

  1. 相關性
  2. 來源和重要性
  3. 特異性
  4. 作用域臨近性
  5. 出現順序

1px用於列印媒體。由於其媒體型別缺乏相關性,因此將其從考慮範圍中移除。

沒有宣告標記為!important,因此優先順序順序是作者樣式表優先於使用者樣式表優先於使用者代理樣式表。根據來源和重要性,使用者樣式表中的1em和使用者代理樣式表中的10px將被移除考慮。

請注意,即使.specific1em上的使用者樣式具有更高的特異性,它也是使用者樣式表中的普通宣告。因此,它的優先順序低於任何作者樣式,並在特異性發揮作用之前就被演算法的來源和重要性步驟移除。

作者樣式表中有三個宣告

css
li {
  margin-left: 0;
} /* from author css 1 */
css
@media screen {
  li {
    margin-left: 3px;
  }
}
css
@layer namedLayer {
  li {
    margin-left: 5px;
  }
}

最後一個5px是層疊層的一部分。層中的普通宣告優先順序低於同一來源型別中不在層中的普通樣式。這也由演算法的第 2 步(來源和重要性)移除。

這留下了03px,它們都具有相同的選擇器,因此具有相同的特異性。它們都不在@scope塊內,因此作用域臨近性在此示例中也不起作用。

然後我們檢視出現順序。第二個,這兩個未分層作者樣式中的最後一個,獲勝。

css
margin-left: 3px;

注意:使用者 CSS 中定義的宣告,雖然可能具有更高的特異性,但不會被選中,因為層疊演算法的來源和重要性特異性演算法之前應用。即使層疊層中定義的宣告在程式碼中可能出現在後面,也不會具有優先順序,因為層疊層中的普通樣式優先順序低於未分層的普通樣式。出現順序僅在來源、重要性和特異性都相同時才重要。

作者樣式:內聯樣式、層級和優先順序

層疊順序中的表格中提供了優先順序順序概述。該表在兩行中分別總結了使用者代理、使用者和作者來源型別的樣式,分別為“來源型別 - 普通”和“來源型別 - !important”。每個來源型別內的優先順序更加細緻。樣式可以包含在其來源型別內的層中,並且對於作者樣式,內聯樣式在層疊順序中的位置也是一個問題。

宣告層的順序對於確定優先順序很重要。層中的普通樣式優先於在先前層中宣告的樣式;在任何層之外宣告的普通樣式優先於普通分層樣式,無論特異性如何。

在此示例中,作者使用 CSS 的@import規則在<style>資訊元素中匯入五個外部樣式表。

html
<style>
  @import unlayeredStyles.css;
  @import AStyles.css layer(A);
  @import moreUnlayeredStyles.css;
  @import BStyles.css layer(B);
  @import CStyles.css layer(C);
  p {
    color: red;
    padding: 1em !important;
  }
</style>

然後在文件主體中,我們有內聯樣式

html
<p style="line-height: 1.6em; text-decoration: overline !important;">Hello</p>

在上面的 CSS 程式碼塊中,建立了三個名為“A”、“B”和“C”的層疊層,按此順序排列。三個樣式表直接匯入到層中,兩個樣式表在沒有建立或分配到層的情況下匯入。“所有未分層樣式”(在下表中為普通作者樣式優先順序 - 順序 4)包括來自這兩個樣式表和其他未分層 CSS 樣式塊的樣式。此外,還有兩個內聯樣式,一個普通的line-height宣告和一個重要的text-decoration宣告

順序(從低到高) 作者樣式 重要性
1 A - 第一個層 普通
2 B - 第二個層 普通
3 C - 最後一個層 普通
4 所有未分層樣式 普通
5 內聯style 普通
6 動畫
7 所有未分層樣式 !important
8 C - 最後一個層 !important
9 B - 第二個層 !important
10 A - 第一個層 !important
11 內聯style !important
12 過渡

在所有來源型別中,層中包含的非重要樣式優先順序最低。在我們的示例中,與第一個宣告的層(A)關聯的普通樣式優先順序低於第二個宣告的層(B)中的普通樣式,後者優先順序低於第三個宣告的層(C)中的普通樣式。這些分層樣式的優先順序低於所有普通未分層樣式,其中包括來自unlayeredStyles.cssmoreUnlayeredStyles.css以及<style>本身中pcolor的普通樣式。

如果 A、B 或 C 中的任何分層樣式的選擇器具有匹配元素的更高特異性,類似於:root body p { color: black;},則無關緊要。這些宣告將被移除考慮,因為來源;普通分層樣式優先順序低於普通未分層樣式。但是,如果在unlayeredStyles.css中找到了更具體的選擇器:root body p { color: black;},由於來源和重要性具有相同的優先順序,因此特異性將意味著更具體的黑色宣告獲勝。

對於宣告為!important的樣式,層級優先順序順序相反。在層中宣告的重要樣式優先於在層之外宣告的重要樣式。在早期層中出現的重要值優先於在後續層疊層中宣告的重要樣式。

內聯樣式

僅與作者樣式相關的是內聯樣式,使用style屬性宣告。普通內聯樣式優先於任何其他普通作者樣式,無論選擇器的特異性如何。如果在五個匯入的樣式表中的任何一個的:root body p選擇器塊中聲明瞭line-height: 2;,則行高仍將為1.6

普通內聯樣式優先於任何其他普通作者樣式,除非屬性正在被 CSS 動畫更改。

所有重要的內聯樣式優先於所有作者樣式,無論是重要的還是不重要的,內聯的還是不內聯的,分層的還是不分層的。重要樣式也優先於動畫屬性,但不優先於過渡屬性。有三件事可以覆蓋重要的內聯樣式:1) 重要的使用者樣式,2) 重要的使用者代理樣式,或 3) 正在過渡的屬性值。

重要性和層

對於重要樣式,來源型別優先順序順序相反。在任何層疊層之外宣告的重要樣式優先順序低於那些作為層的一部分宣告的樣式。出現在早期層中的重要值的優先順序高於在後續層疊層中宣告的重要樣式。

例如以下 CSS

css
p {
  color: red;
}
@layer B {
  :root p {
    color: blue;
  }
}

即使紅色是首先宣告的並且具有較不特定的選擇器,但由於未分層 CSS 優先於分層 CSS,因此段落將為紅色。如果我們在段落上包含了一個將其設定為不同顏色的內聯樣式,例如<p style="color: black">,則段落將為黑色。

當我們在 CSS 中新增!important時,樣式表的優先順序順序將反轉

css
p {
  color: red !important;
}
@layer B {
  :root p {
    color: blue !important;
  }
}

現在段落將為藍色。在最早宣告的層中!important優先於後續層和未分層的重要宣告。如果內聯樣式包含!important,例如<p style="color: black !important">,則段落將再次為黑色。內聯重要性確實優先於所有其他作者宣告的!important宣告,無論特異性如何。

注意:!important標誌反轉了層疊層的優先順序。因此,儘量不要使用!important來覆蓋外部樣式。相反,使用@import以及layer關鍵字或layer()函式將外部樣式表(來自框架、視窗小部件樣式表、庫等)匯入到層中。將樣式表作為 CSS 中的第一個宣告匯入到層中會降低其優先順序,並且稍後在 CSS 中定義的作者定義的層將具有更高的優先順序。!important標誌應謹慎使用,如果使用,則僅用於在第一個宣告的層中保護所需樣式免受後續覆蓋。

正在過渡的樣式優先於所有重要樣式,無論它們是誰或如何宣告的。

完整的層疊順序

現在我們對來源型別和級聯層優先順序有了更好的理解,我們意識到級聯順序中的表格可以用以下表格更準確地表示。

優先順序順序
(從低到高)
樣式來源 重要性
1 使用者代理 - 首次宣告的層 普通
使用者代理 - 最後宣告的層
使用者代理 - 未分層的樣式
2 使用者 - 首次宣告的層 普通
使用者 - 最後宣告的層
使用者 - 未分層的樣式
3 作者 - 首次宣告的層 普通
作者 - 最後宣告的層
作者 - 未分層的樣式
內聯style
4 動畫
5 作者 - 未分層的樣式 !important
作者 - 最後宣告的層
作者 - 首次宣告的層
內聯style
6 使用者 - 未分層的樣式 !important
使用者 - 最後宣告的層
使用者 - 首次宣告的層
7 使用者代理 - 未分層的樣式 !important
使用者代理 - 最後宣告的層
使用者代理 - 首次宣告的層
8 過渡

哪些 CSS 實體參與層疊

只有 CSS 屬性/值對宣告參與級聯。CSS @規則描述符不參與級聯,HTML 表現屬性也不屬於級聯的一部分。

At規則

包含宣告之外的實體的 CSS @規則,例如包含描述符@font-face規則,不參與級聯。

在大多數情況下,@規則中定義的屬性和描述符不參與級聯。只有@規則作為一個整體參與級聯。例如,在@font-face規則中,字型名稱由font-family描述符標識。如果定義了幾個具有相同描述符的@font-face規則,則僅考慮最合適的@font-face(作為一個整體)。如果多個規則都同樣適用,則使用演算法的步驟 1、2 和 4 對整個@font-face宣告進行比較(@規則沒有特異性)。

雖然大多數@規則(例如@media@document@supports中的@規則)中包含的宣告參與級聯,但@規則可能會使整個選擇器變得不相關,就像我們在基本示例中看到的列印樣式一樣。

@keyframes中的宣告不參與級聯。與@font-face一樣,只有@keyframes作為一個整體透過級聯演算法選擇。動畫的優先順序順序將在下面描述

對於@import@import本身不參與級聯,但所有匯入的樣式都參與級聯。如果@import定義了一個命名或匿名層,則匯入樣式表的內容將放置到指定的層中。使用@import匯入的所有其他 CSS 都被視為最後宣告的層。這在上面已經討論過了。

最後,@charset遵循特定的演算法,不受級聯演算法的影響。

表現屬性

表現屬性是源文件中可以影響樣式的屬性。例如,當包含時,已棄用的align屬性設定多個 HTML 元素的對齊方式,而fill屬性定義用於繪製 SVG 形狀和文字的顏色,並定義 SVG 動畫的最終狀態。雖然它們是作者樣式,但表現屬性不參與級聯。

如果使用者代理支援 HTML 表現屬性,則包含在 HTML 和 SVG 中的有效表現屬性(例如alignfill屬性)將轉換為相應的 CSS 規則(所有 SVG 表現屬性都支援為 CSS 屬性),並在具有等於0的特異性的任何其他樣式之前插入到作者樣式表中。

表現屬性不能宣告為!important

CSS 動畫和層疊

CSS 動畫使用@keyframes@規則定義狀態之間的動畫。關鍵幀不級聯,這意味著在任何給定時間,CSS 僅從一個@keyframes中獲取值,並且永遠不會混合多個關鍵幀。

如果使用相同的動畫名稱定義了多個關鍵幀動畫,則具有最高優先順序的來源和層中的最後一個定義的@keyframes將被使用。即使@keyframes動畫化不同的屬性,也只使用一個@keyframes定義。具有相同名稱的@keyframes永遠不會合並。

css
p {
  animation: infinite 5s alternate repeatedName;
}
@keyframes repeatedName {
  from {
    font-size: 1rem;
  }
  to {
    font-size: 3rem;
  }
}

@layer A {
  @keyframes repeatedName {
    from {
      background-color: yellow;
    }
    to {
      background-color: orange;
    }
  }
}
@layer B {
  @keyframes repeatedName {
    from {
      color: white;
    }
    to {
      color: black;
    }
  }
}

在此示例中,有三個名為repeatedName的單獨動畫宣告。當animation: infinite 5s alternate repeatedName應用於段落時,只會應用一個動畫:基於來源和級聯層優先順序順序,未分層 CSS 中定義的關鍵幀動畫優先於分層關鍵幀動畫宣告。在此示例中,只會對元素的字型大小進行動畫處理。

注意:沒有重要的動畫,因為@keyframes塊中包含!important作為值一部分的屬性宣告將被忽略。

重置樣式

在內容完成更改樣式後,它可能會發現自己需要將其恢復到已知狀態。這可能發生在動畫、主題更改等情況下。CSS 屬性all允許您快速將 CSS 中(幾乎)所有內容恢復到已知狀態。

all允許您選擇立即將所有屬性恢復到其初始(預設)狀態、從級聯的上一級繼承的狀態、特定來源(使用者代理樣式表、作者樣式表或使用者樣式表)的狀態,甚至完全清除屬性的值。

規範

規範
CSS 級聯和繼承級別 4

另請參閱