優先權

CSS Podcast - 003:特異性

假設您使用下列 HTML 和 CSS:

<button class="branding">Hello, Specificity!</button>
.branding {
  color: blue;
}

button {
  color: red;
}

這裡有兩項規則都以相同元素為目標。 每項規則都包含要設定按鈕顏色的宣告: 一個嘗試將按鈕塗成紅色,另一個則嘗試塗成藍色。 哪個宣告會套用至元素?

瞭解 CSS 權重演算法,是瞭解 CSS 如何在相互競爭的宣告之間做出決定的關鍵。

特異性是層疊樣式表中的一個獨立階段,我們在上一個單元中介紹了層疊樣式表。

優先權評分

來源中的每個選取器規則都會獲得分數。您可以將特異性視為總分,每個選取器類型都會為該分數加分。 系統會採用最明確規則的宣告。

在實際專案中,特異性是確保您預期套用的 CSS 規則確實套用,同時一般會保持低分,以避免複雜性。特異性應盡量符合需求,而非追求最高特異性。日後可能需要套用一些真正更重要的 CSS。如果選擇最高精確度,這項工作就會變得非常困難。

優先權不是十進位數字,而是由三個元件組成的三元組:ABC

  • A:類似 ID 的具體性
  • B:類似類別的特異性
  • C:類似元素的特殊性

這通常以 (A,B,C) 符號表示。例如:(1,0,2)。替代的 A-B-C 標記也很常用。

圖表:顯示專一性的三個要素 (A、B、C)。圖表會顯示每個元件代表的內容,以及影響元件的部分選取器範例。
這張圖表說明各種選取器會影響特異性的哪個元件。

如何比較特異性

比較特定性時,會依序比較三個元件:A 值較大的特定性較明確;如果兩個 A 值相同,則 B 值較大的特定性較明確;如果兩個 B 值也相同,則 C 值較大的特定性較明確;如果所有值都相同,則兩個特定性相等。

舉例來說,(1,0,0) 的具體程度高於 (0,4,3),因為 (1,0,0) 中的 A 值 (即 1) 大於 (0,4,3) 中的 A 值 (即 0)。

選取器會影響明確性

特異性三元組的每個部分都以 0 開頭,因此預設特異性為 (0,0,0)。選取器的每個部分都會提高特定性,並根據選取器類型,遞增 ABC 的值。

通用選取器

通用選取器 (*) 不會增加明確性,因此其值會維持 (0,0,0) 的初始明確性。

* {
  color: red;
}

元素或虛擬元素選取器

元素 (類型) 或虛擬元素選取器會新增類似元素的特殊性,並將 C 元件遞增 1

下列範例的整體特殊性為 (0,0,1)

類型選取器

div {
  color: red;
}

虛擬元素選取器

::selection {
  color: red;
}

類別、虛擬類別或屬性選取器

類別虛擬類別屬性選取器會新增類似類別的特殊性,將 B 元件遞增 1

以下範例的特異性為 (0,1,0)

類別選取器

.my-class {
  color: red;
}

虛擬類別選取器

:hover {
  color: red;
}

屬性選取器

[href='#'] {
  color: red;
}

ID 選取器

ID 選取器會新增類似 ID 的特殊性,只要使用 ID 選取器 (#myID) 而非屬性選取器 ([id="myID"]),A 元件就會遞增 1。

在以下範例中,特異性為 (1,0,0)

#myID {
  color: red;
}

其他選取器

CSS 有許多選取器。並非所有屬性都會增加明確性。 舉例來說,:not() 虛擬類別本身不會為特殊性計算新增任何內容。

:not() 虛擬類別的特殊性會由以半形逗號分隔的選取器引數中,最特定選取器的特殊性取代;提供的特殊性與寫入 :not(:is(argument)) 時相同。

div:not(.my-class) {
  color: red;
}

:is() 虛擬類別本身也不會加入特異性計算。與 :not() 類似,它會採用最明確引數的明確性。

:is(h1, h2, h3) {
  color: blue;
}

這個範例只有一種選取器 (type),因此特異性為 (0,0,1)

新增 id 後,具體程度會提高至 (1,0,0)

:is(h1, h2, h3, #my-heading) {
  color: blue;
}

:where() 虛擬類別則不同。無論任何引數的具體程度為何,一律具有 (0,0,0) 的具體程度。

:where(h1, h2, h3, #my-heading) {
  color: blue;
}

使用 :where() 套用的低特異性樣式可讓您稍後在 CSS 樣式表中使用基本選取器覆寫這些樣式:

:where(#my-content) {
   color: red;
}

p {
   color: blue;
}

即使 :where() 的引數清單中有 id,但其特殊性仍只有 (0, 0, 0),因此基本 p 選擇器會覆寫該引數,而文字會是 blue

隨堂測驗

測試您對權重分數的瞭解程度

a[href="#"]」的具體程度為何?

(0,0,1)
a 價值 (0,0,1),但 [href="#"] 價值 (0,1,0)
(0,1,0)
再試一次!a 價值 (0,0,1),但 [href="#"] 價值 (0,1,0)
(0,1,1)
a 的值為 (0,0,1)[href="#"] 的值為 (0,1,0),因此總特異性為 (0,1,1)

不會影響特異性的因素

以下列出幾個常見的誤解,有助於瞭解影響具體程度的因素。

內嵌樣式屬性

直接套用至元素 style 屬性的 CSS 不會影響特殊性,因為這是層疊中的不同步驟,會在評估特殊性之前進行。

<div style="color: red"></div>

如要從樣式表內覆寫這項宣告,您必須在層疊的先前步驟中,取得宣告的優先權。

舉例來說,您可以將 !important 加入其中,讓它成為「Authored !important」來源的一部分。

!important 項聲明

CSS 宣告結尾的 !important 不會影響特異性 但會將宣告放在不同的來源, 也就是「撰寫的 !important

在下列範例中,.my-class 的特異性與 !important 宣告是否勝出無關。

.my-class {
  color: red !important;
  color: white;
}

如果兩個宣告都是 !important,則特異性會再次發揮作用,因為來自層疊的原始步驟尚未判斷出勝出者。

.branding {
  color: blue !important;
}

button {
  color: red !important;
}

情境中的優先權

使用複雜或複合選取器時,選取器的每個部分都會加總為特定性。請參考以下 HTML 範例:

<a class="my-class another-class" href="#">A link</a>

這個連結有兩個類別。 下列 CSS 中的規則明確度為 (0,0,1)

a {
  color: red;
}

如果您在選取器中參照其中一個類別,現在的明確度為 (0,1,1)

a.my-class {
  color: green;
}

將其他類別新增至選取器,現在的明確度為 (0,2,1)

a.my-class.another-class {
  color: rebeccapurple;
}

href 屬性新增至選取器,現在的明確度為 (0,3,1)

a.my-class.another-class[href] {
  color: goldenrod;
}

最後,在所有這些項目中加入 :hover 虛擬類別,選取器最終會具有 (0,4,1) 的特異性

a.my-class.another-class[href]:hover {
  color: lightgrey;
}

隨堂測驗

測試您對權重分數的瞭解程度

下列哪個選取器的特殊性為 (0,2,1)

article > section
元素會新增類似元素的特異性 (`C` 元件)。選取器中有 2 個元素,因此特異性為 (0,0,2)
article.card.dark
元素會新增類似元素的特異性 (<code>C</code> 元件),類別則會新增類似類別的特異性 (<code>B</code> 元件)。有 2 個類別和 1 個元素,因此這個選取器的特異性為 (0,2,1)
article:hover a[href]
元素會新增類似元素的特異性 (<code>C</code> 元件),偽類別和屬性則會新增類似類別的特異性 (<code>B</code> 元件)。有 2 個元素選取器 (2 × (0,0,1))、1 個屬性選取器 (值為 (0,1,0)) 和 1 個類別選取器 (值為 (0,1,0))。因此,這個選取器的總特異性為 (0,2,2)

以實用方式提高精確度

假設您有類似這樣的 CSS:

.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

HTML 如下所示:

<button class="my-button" onclick="alert('hello')">Click me</button>

按鈕的背景為灰色,因為第二個選取器具有(0,1,1) 的特異性。這是因為它有一個類型選取器 (button),即 (0,0,1),以及一個屬性選取器 ([onclick]),即 (0,1,0)

先前的規則 .my-button 等於 (0,1,0),因為它有一個類別選取器,特異性比 (0,1,1) 低。

如要提升這項規則的優先順序,可以重複類別選取器,如下所示:

.my-button.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

現在按鈕會加上藍色背景,因為新的選取器會取得 (0,2,0) 的特定性

如果特異性相同,則會回溯至層疊的下一個步驟

現在繼續使用按鈕範例,將 CSS 切換為以下內容:

.my-button {
  background: blue;
}

[onclick] {
  background: grey;
}

按鈕的背景為灰色,因為兩個選取器的特異性相同,都是 (0,1,0)

如果切換來源順序中的規則,按鈕就會變成藍色。

[onclick] {
  background: grey;
}

.my-button {
  background: blue;
}

這是因為兩個選取器的特異性相同。 在此情況下,層疊會回溯至顯示順序步驟

資源