Cover image for CSS 特異性
Profile picture of Canis

Canis

2024-03-29

約 10 分鐘

CSS 特異性

前言

本來打算直接開始講 CSS 屬性的,但是我覺得既然前面已經提到了優先級這個東西,那就先來說一下好了。不過特異性的計算其實只是在確定具體樣式的其中一步,所以我這篇文不會只有講特異性。

什麼是特異性

特異性(Specificity)就是所謂的優先級。這個是瀏覽器用來確定 CSS 與元素相關度(權重)的演算法,並且決定具體要應用在元素上的屬性值。

這個演算法用來計算 CSS 聲明(CSS Declaration)的權重(Weight),而權重是由「與元素匹配的選擇器(含偽元素)」中每個權重類別的選擇器的數量決定。如果有超過一個聲明匹配同一個元素,在經過計算之後會套用權重最大的聲明。

選擇器權重類型

下面會說到各種選擇器,不知道這些選擇器在幹嘛的可以看一下認識 CSS 這篇文。

  • ID

    只有「ID 選擇器」。

  • Class

    包含「類選擇器」、「屬性選擇器」、「偽類」。

  • Type

    包含「類型選擇器」、「偽元素」。

在計算權重時,有一些特殊的情況:

  1. 「通用選擇器」和「:where()」偽類(包含它的參數)不會在計算範圍內。雖然不在計算範圍裡面,但是還是會起作用。
  2. 「關係選擇器」可能會使選擇器在所選內容上更加具體,但它們不會讓權重的值有什麼變化。
  3. 「嵌套關係選擇器」(&1、「:is()」偽類,它們不會讓權重的值有什麼變化,因為它們的作用只是讓程式更加簡潔。
  4. 「內嵌樣式」、!important 這兩個是例外中的例外。前者會覆蓋內部和外部樣式的任何聲明,所以優先級最高。而 !important 則是不管優先級的結果怎麼樣,它偏愛謀權篡位,它會直接破壞並應用,所以盡量不要使用。

在特殊情況的第 4 點有說到內嵌樣式的優先級最高(不說 !important 的話),所以權重類型其實應該是四個,下面我會用四個權重來做講解。

特異性是怎麼計算的

在開始計算前要明白,之所以要計算特異性,是為了解決樣式(聲明)的「衝突」。

特異性比較圖
特異性比較圖

上面有說到,權重會分成四個類別:內嵌樣式、ID、Class、Type(元素標籤或偽元素),它會組成這樣的格式:(內嵌樣式, ID, Class, Type),這樣可以方便判斷要使用什麼聲明。

我們使用下面的例子來看一下:

HTML:

<h1 class="title">Title</h1>

使用者代理程式樣式2(Chrome 預設樣式):

h1 {
    display: block;
    font-size: 2em;
    margin-block-start: 0.67em;
    margin-block-end: 0.67em;
    margin-inline-start: 0px;
    margin-inline-end: 0px;
    font-weight: bold;
    unicode-bidi: isolate;
}

自訂樣式:

.title {
    color: red;
    font-size: 40px;
}
 
h1 {
    font-size: 26px;
}
 
div h1.title {
    font-size: 3em;
    font-size: 30px;
}

第一步:找出衝突

根據觀察,衝突的樣式是 font-size

第二步:比較重要性

重要性從高到低,依次是:

  1. !important 的自訂樣式
  2. !important 的使用者代理程式樣式
  3. 自訂樣式
  4. 使用者代理程式樣式

所以至此,可以淘汰使用者代理程式樣式的 font-size

第三步:比較特殊性

就是上面提到的「選擇器權重類型」,這邊再簡單統整一下:

格式:(內嵌樣式, ID, Class, Type)

項目說明
內嵌樣式有就是 1,沒有就是 0
IDID 選擇器數量
Class類選擇器、屬性選擇器、偽類數量
Type類型選擇器、偽元素數量

因為使用者代理程式樣式已經淘汰,所以不考慮了。這是結果:

項次項目結果
1.title(0, 0, 1, 0)
2h1(0, 0, 0, 1)
3div h1.title(0, 0, 1, 2)

到此已經算出來了,那麼具體比較的方式就是由左至右比較,誰大就用誰。

第一步:比較內嵌樣式

三個都為 0,所以下一步。

第二步:比較 ID

三個都為 0,所以下一步。

第三步:比較 Class

項次 1 = 項次 3 > 項次 2,所以項次 2 淘汰,然後進到下一步。

第四步:比較 Type

項次 3 > 項次 1,所以項次 1 淘汰,所以最後 font-size 的值應該為 3em30px

因為無法確認究竟是 3em 還是 30px,所以會進入下一步。

第四步:比較源次序(撰寫順序)

較後面的會覆蓋前面的,所以結果 font-size 的值應該為 30px

第五步:繼承

繼承其實就是字面上的意思。那有些屬性可以被繼承,有些不行,通常和文字有關的(如字體大小、行高)可以被繼承,像是寬高、位置等就不能被繼承。具體可不可以最好是看官方的說明,例如說 width 這個屬性,它是不能被繼承的,它會在 width - CSS: Cascading Style Sheets | MDN Formal definition 部分的 Inherited 欄位註記。

MDN Inherited
MDN Inherited

繼承需要滿足兩個條件:

  1. 目前沒有值(不考慮使用者代理程式樣式)
  2. 可以被繼承

當兩個條件都滿足,那麼就會繼承父元素或是更上層的元素的值。如果都沒有,就繼承使用者代理程式樣式的值,例如 width 的使用者代理程式樣式值是 auto。如此一來,就可以確保每一個屬性都有值了。

結語

樣式計算大概就這樣了,有想到要補充的會再更新或寫一篇新的。推薦一個計算器——Specificity Calculator,可以讓你更好地了解特異性。

參考資料

  1. Specificity - CSS: Cascading Style Sheets | MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity
  2. CSS的優先順位. 載入位置&撰寫順序&選擇器類型都會影響 css 的優先順位 | by Sandy | UI/UX練功坊 | Medium:https://medium.com/ui-ux練功坊/css的優先順位-cfb859e988bd
  3. Specifishity :: Specificity with Fish: https://specifishity.com/
  4. Introducing the CSS Cascade - CSS: Cascading Style Sheets | MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade
  5. CSS 属性计算的总流程:https://v.douyin.com/iFwSBCqx/
  6. CSS 的层叠规则:https://v.douyin.com/iFwSkaTX/
  7. CSS 的继承规则:https://v.douyin.com/iFwSB4FD/

Footnotes

  1. 嵌套關係選擇器或者說嵌套關係,它其實就是讓 CSS 變得簡潔的一種寫法,具體範例可以看 MDN

  2. 我在 HTML 結構與元素這篇文有提到區塊級元素或行內元素,但是其實說法不太準確,因為官方有說過不再使用這種說法。那麼我為什麼還這樣解釋呢?不是因為我不聽勸喔,是因爲瀏覽器有所謂的使用者代理程式樣式。這個東西是用來定義預設樣式的,比如說 div 之所以是區塊級元素,是因爲它的預設樣式有 display: block;,所以它才是區塊級元素。

網站開發學習前端入門CSS
Canis

Canis

一個能設計 UI/UX 的前端工程師。平時有想法的時候,會坐到電腦前開始把想法慢慢地實現出來。白話文就是「喜歡沒事找事來折磨自己」。