CSS選擇器的優先順序(精講版)

2020-07-16 10:05:18
幾乎所有的 CSS 樣式衝突、樣式覆蓋等問題都與 CSS 宣告的優先順序錯位有關。因此,在詳細闡述 CSS 選擇器的優先順序規則之前,我們先快速了解一下 CSS 全部的優先順序規則。

CSS 優先順序規則概覽

CSS 優先順序有著明顯的不可逾越的等級制度,我將其劃分為 0~5 這 6 個等級,其中前 4 個等級由 CSS 選擇器決定,後 2 個等級由書寫形式和特定語法決定。下面我將對這 6 個等級分別進行講解。

0 級

通配選擇器、選擇符和邏輯組合偽類的優先順序都是 0。

通配選擇器寫作星號*。範例如下:

* { color: #000; }


選擇符指+>~、空格和||

邏輯組合偽類有:not():is():where等,這些偽類本身並不影響 CSS 優先順序,影響優先順序的是括號裡面的選擇器。例如:

:not() {}

需要注意的是,只有邏輯組合偽類的優先順序是 0,其他偽類的優先順序並不是這樣的。

1 級

標籤選擇器的優先順序是 1。範例如下:

body { color: #333; }

2 級

類選擇器、屬性選擇器和偽類的優先順序是 2。範例如下:

.foo { color: #666; }
[foo] { color: #666; }
:hover { color: #333; }

3 級

ID 選擇器的優先順序是 3。範例如下:

#foo { color: #999; }

4 級

style屬性內聯樣式的優先順序是 4。範例如下:

<span style="color: #ccc;">http://c.biancheng.net/</span>

5級

!important優先順序是最高的,也就是 5。範例如下:

.foo { color: #fff !important; }

!important是頂級優先順序,可以重置 JavaScript 設定的樣式,唯一推薦使用的場景就是使 JavaScript 設定無效。例如:

.foo[style*="color: #ccc"] {
  color: #fff !important;
}

對於其他場景,沒有任何使用它的理由,切勿濫用。

不難看出,CSS 選擇器的優先順序(0 級至 3 級)屬於 CSS 優先順序的一部分,也是最重要、最複雜的部分,學會 CSS 選擇器的優先順序等同於學會了完整的 CSS 優先順序規則。

CSS 選擇器優先順序的計算規則

對於 CSS 選擇器優先順序的計算,業界流傳甚廣的是數值計數法。具體如下:每一段 CSS 語句的選擇器都可以對應一個具體的數值,數值越大優先順序越高,其中的 CSS 語句將被優先渲染。具體規則為:
  • 出現一個 0 級選擇器,優先順序數值+0
  • 出現一個 1 級選擇器,優先順序數值+1
  • 出現一個 2 級選擇器,優先順序數值+10
  • 出現一個 3 級選擇器,優先順序數值+100

於是,有下表所示的計算結果。

表1:選擇器優先順序計算值

選擇器

計算值

計算細則

* {}

0

1 個 0 級通配選擇器,優先順序數值為 0

dialog {}

1

1 個 1 級標籤選擇器,優先順序數值為 1

ul > li {}

2

2 個 1 級標籤選擇器,1 個 0 級選擇符,優先順序數值為 1+0+1

li > ol + ol {}

3

3 個 1 級標籤選擇器,2 個 0 級選擇符,優先順序數值為 1+0+1+0+1

.foo {}

10

1 個 2 級類名選擇器,優先順序數值為 10

a:not([rel=nofollow]) {}

11

1 個 1 級標籤選擇器,1 個 0 級否定偽類,1 個 2 級屬性選擇器,優先順序數值為 1+0+10

a:hover {}

11

1 個 1 級標籤選擇器,1 個 2 級偽類,優先順序數值為 1+10

ol li.foo {}

12

1 個 2 級類名選擇器,2 個 1 級標籤選擇器,1 個 0 級空格選擇符,優先順序數值為 1+0+1+10

li.foo.bar {}

21

2 個 2 級類名選擇器,1 個 1 級標籤選擇器,優先順序數值為 10×2+1

#foo {}

100

1 個 3 級 ID 選擇器,優先順序數值為 100

#foo .bar p {}

111

1 個 3 級 ID 選擇器,1 個 2 級類名選擇器,1 個 1 級標籤選擇器,優先順序數值為 100+10+11

測試題

趁熱打鐵,我出一個小題考考大家,<body> 元素的顏色是紅色還是藍色?

HTML 程式碼:
<html lang="zh-CN">
   <body class="foo">顏色是?</body>
</html>
CSS 程式碼:
body.foo:not([dir]) { color: red; }
html[lang] > .foo { color: blue; }
我們先來計算一下各自的優先順序數值。

首先是body.foo:not([dir]),出現了 1 個標籤選擇器 body,1 個類名選擇器.foo和 1 個否定偽類:not,以及屬性選擇器[dir],計算結果是 1+10+0+10,也就是 21。

接下來是html[lang] > body.foo,出現了 1 個標籤選擇器 html,1 個屬性選擇器[lang]和 1 個類名選擇器.foo,計算結果是 1+10+10,也就是 21。

這兩個選擇器的計算值居然是一樣的,那該怎麼渲染呢?

這就引出了另外一個重要的規則——“後來居上”。也就是說,當 CSS 選擇器的優先順序數值一樣的時候,後渲染的選擇器的優先順序更高。因此,上題的最終顏色是藍色(blue)。

後渲染優先順序更高的規則是相對於整個頁面文件而言的,而不僅僅是在一個單獨的 CSS 檔案中。例如:

<style>body { color: red; }</style>
<link rel="stylesheet" href="a.css">
<link rel="stylesheet" href="b.css">

其中在 a.css 中有:

body { color: yellow; }

在 b.css 中有:

body { color: blue; }

此時,body 的顏色是藍色,如下圖所示,因為 blue 這段 CSS 語句在文件中是最後出現的。

瀏覽器中body顏色的優先級
圖1:瀏覽器中 body 顏色的優先順序