---
# System prepended metadata

title: CSS Specificity

---

# CSS Specificity

> :bookmark:**CSS Specificity( CSS 的權重/優先權)：套用在 DOM 上的樣式，互相覆蓋的權力。**
> 瀏覽器會通過權重來判斷哪些屬性值與一個元素最為相關，並在該元素上應用這些屬性值。
> 權重是基於不同種類的選擇器 (CSS selectors) 組成的比較規則。
> [color=#f24]

**結論**

![](https://i.imgur.com/8nC3g8G.png)

## 權重的計算

![](https://i.imgur.com/wDMjYjs.png)
簡單來說，可以分成四個區塊，從左而右分別是
- inline CSS (例 **style="font-weight:bold"**)
- ID selectors （例 **#example**）
- Class selectors(例 **.example**)、屬性選擇器（例 **[type="radio"]**）和偽類（例 **:hover**）
- Type selectors(例 **h1**)、偽元素(例 **::before**)


這裡可以像 ip 一樣表示成四個數字：0-0-0-0。
這些數字是 CSS 的規則，越往左邊權重越大，而數字越大權重也越大。

> 舉例來說，如果今天樣式表只有寫：div { color:red; }，所產生的數字就是：0-0-0-1；
> 如果今天寫成：body div div { color:red; }，那麼數字就可以表示為：0-0-0-3；
> 如果今天寫成：#header div { color:red }，那麼數字就要表示為：0-1-0-1；
> 而 0-0-0-3 一定會覆蓋過 0-0-0-1 ，0-1-0-1 會覆蓋過 0-0-0-3；
> 當然，如果兩個數字完全相同的，就是樣式擺放的比先後順序了。
> [color=#f24]

若還是有點不清楚，可以參考下圖：
![](https://i.imgur.com/fP5w88p.png)

![](https://i.imgur.com/ADHfzNz.png)

==**權重計算工具**==
[Specificity Calculator](https://specificity.keegan.st/)
(要注意有些規則會不適用)

## 例外規則 !important
``` css
/* 範例： */
table td { height: 50px !important; }
```
> 使用 !important 規則時，將會覆蓋其他的宣告。
> 雖然 !important 與權重無關，但它與最終的結果直接相關。
> 使用 !important 是一個壞習慣，應該盡量避免，因為這破壞了樣式表中的固有的規則，也使得debug變得困難了。
> [color=#f24]


經驗法則：
* 一定要優先考慮使用權重來解決問題而不是 !important
* 永遠不要在你的套件中使用 !important
* 永遠不要在全域的 CSS 代碼中使用 !important


與其使用 !important，你可以：

==1. 更好地利用 CSS 階層屬性==

``` html=
<!-- 範例 -->
<div id="test">
  <span>Text</span>
</div>
```

``` css=
/* 範例 */
div#test span { color: green; }
div span { color: blue; }
span { color: red; }
/* 無論 CSS 語句的順序如何，Text 都會是綠色（green），
  因為第1行的規則最有針對性、權重最高。
  藍色 blue 的規則會覆蓋紅色 red 的規則。 */
```
==2. 增加一個或多個其他元素，使 CSS selectors 變得更加具體，並獲得更高的權重。==
``` css=
/* 範例 */
#myId#myId span { color: yellow; }
.myClass.myClass span { color: orange; }
```
### 什麼的情況下可以使用 !important：
**1. 覆蓋 inline CSS**
假設你的網站上有一個設定了全域的 CSS 文件，同時你（或是你同事）寫了一些很差的 inline CSS。
inline CSS 和 !important 都被認為是非常不好的做法，但是有時你可以在CSS文件裡用!important去覆蓋 inline CSS。
在這種情況下，你就可以在你全域的 CSS 文件中寫一些 !important 的樣式來覆蓋掉那些inline CSS。
``` html=
<div class="foo" style="color: red;">What color am I?</div>
```
``` css=
.foo[style*="color: red"] {
  color: firebrick !important;
}
```

**2. 覆蓋權重高的選擇器**
``` css=
/* 如果不使用 !important ，第一條規則永遠比第二條的權重更高 */
#someElement p {
  color: blue;
}

p.awesome {
  color: red;
}
```
讓上述例子的第二條規則 權重變高的方法 :arrow_down: 
``` css=
/* 直接用 !important */
#someElement p {
  color: blue;
}
p.awesome {
  color: red !important;
}
/*////////////////////////*/
/* 也可以改寫原來的規則，以避免使用 !important */
[id="someElement"] p {
  color: blue;
}

p.awesome {
  color: red;
}
```

## 一些特別的偽類的計算方式
### :is() 和 :not() 有例外規則
:is() 和 :not() 在權重計算中不會被看作是偽類( pseudo-class )。
事實上，在計算選擇器數量時還是會把其中的選擇器當做 Type selectors 進行計數。


``` html=
<div class="outer">
  <p>This is in the outer div.</p>
  <div class="inner">
    <p>This text is in the inner div.</p>
  </div>
</div>
```
``` css=
div.outer p {
  color: orange;
}

div:not(.outer) p {
  color: blueviolet;
}
```
結果：
![](https://i.imgur.com/elPXfMh.png =200x90)

### :where() 有例外規則
:where()會將自己的權重替換成 0。
``` html=
<div class="outer">
  <p>This is in the outer div.</p>
  <div class="inner">
    <p>This text is in the inner div.</p>
  </div>
</div>
```

``` css=
div:where(.outer) p {
  color: orange;
}

div p {
  color: blueviolet;
}
```
結果：
![](https://i.imgur.com/QikctiQ.png =200x90)


## 基於形式的權重（Form-based specificity）
權重是基於選擇器的形式進行計算的。
在下面的例子中，儘管選擇器*[id="foo"]選擇了一個ID，
但它還是作為一個屬性選擇器來計算自身的權重。
``` html=
<p id="foo">I am a sample text.</p>
```
``` css=
*#foo {
  color: green;
}

*[id="foo"] {
  color: purple;
}
```
結果：
![](https://i.imgur.com/tpURlS0.png =150x50)

## 無視 DOM 樹中的距離
``` html=
<html>
  <body>
    <h1>Here is a title!</h1>
  </body>
</html>
```
``` css=
body h1 {
  color: green;
}

html h1 {
  color: purple;
}
```
結果：
![](https://i.imgur.com/uEOeojE.png =150x50)

## 直接添加樣式 vs. 繼承樣式
為目標元素直接添加樣式，永遠比繼承樣式的權重高，無視權重的遺傳規則。
``` html=
<html>
  <body id="parent">
    <h1>Here is a title!</h1>
  </body>
</html>
```
``` css=
#parent {
  color: green;
}

h1 {
  color: purple;
}
```
結果：
![](https://i.imgur.com/uEOeojE.png =150x50)
因為 h1 選擇器明確的定位到了元素，但綠色選擇器的僅僅繼承自其父級。


---


## [補充] ::before 和 ::after
::: info
**偽元素 (pseudo-element)**
什麼是「偽元素」？
1. 「偽元素」之所以稱作「偽」，除了英文從「Pseudo」翻譯過來之外，就是因為它並不是真正網頁裡的元素，但行為與表現又和真正網頁元素一樣，也可以對其使用 CSS 操控。
2. 跟偽元素類似的還有「偽類」( Pseudo classes )，在 W3C 的定義裡總共有五個偽元素 ( 其他仍在測試階段 )分別是 ::before、::after、::first-line、::first-letter和::selection。
3. 為了和偽類區分，偽元素使用兩個冒號「::」開頭，而偽類使用一個冒號「:」開頭 ( 像是 :hover、:target...等 )。
4. 雖然現在的瀏覽器就算寫一個冒號也可以正常運作，為了方便區分，用兩個冒號還是比較好， 且不論瀏覽器是什麼，::selection必須是兩個冒號才能正常運作。
:::
### ::before (:before)
* ::before 創建一個偽元素，作為已選中元素的元素的第一個子元素。
* ==必須用 content 屬性來為一個元素添加修飾性的內容。==
* 此元素默認為行內元素 (display:inline-block) 的屬性存在。

### ::after(:after)
* ::after 用來創建一個偽元素，作為已選中元素的最後一個子元素。
* 同 ::before 的特點。

語法範例：
``` css
/* CSS3 語法 */
/* Add a heart before links */
a::before {
  content: "♥";

/* （單冒號）CSS2 過時語法 (僅用來支持 IE8) */
element:before  { 樣式 }

/* 在每一個p元素前插入內容 */
p::before { content: "Hello world!"; }
}
```

實例：
```html=
<div>大家好，我是 div</div>
```
```css=
div::before{
    content:"我是 before";
    color:red;
}
div::after{
    content:"我是 after";
    color:red;
}
```
結果：
![](https://i.imgur.com/8Bm2l00.png =300x30)

### 實用的 content
1. 需要注意使用上一定要具備 content 的屬性，就算是只有 content:""。
2. 沒有 content 的偽元素不會出現在畫面上。
3. content 是個很特別的屬性，它可以使用 attr 直接獲取內容元素的屬性值 ( attribute )。

* 舉例來說，在 HTML 裡有一個超連結，點擊後會彈出新視窗並連結至 Google：
``` html
<a href="https://www.google.com" target="_blank">google</a>
```

* 使用下列的用法，將會把超連結的 href 內容與 target 內容，透過偽元素一前一後的顯示出來。
``` css=
a::before{
  content: attr(href);
  color:red;
}
a::after{
  content: attr(target);
  color:green;
}
```
![](https://i.imgur.com/EoaBVJe.png =310x30)

* content 內容是可以「相加」的，不過用法不像 JavaScript 使用 + 號來相連，而是直接用一個空白鍵就可以不斷的累加下去。
``` css=
a::before{
  content: "( " attr(href) " ) < ";
  color:red;
}
a::after{
  content: " > ( " attr(target) " ) ";
  color:green;
}
```
![](https://i.imgur.com/wy5sqh0.png =360x30)

* content 也可以使用 url 放入圖片的功能。
```css=
div::before{
  content:url(圖片網址) url(圖片網址) url(圖片網址);
}
```
![](https://i.imgur.com/RaQeK1r.png)


**總結：**
雖然偽元素很好用，但偽元素的內容實際上不存在網頁裡 ( 如果打開瀏覽器的開發者工具，會看不到內容 )，所以如果在裡頭塞了太多的重要的內容，反而會影響到 SEO 的成效。
因此對於使用偽元素的定位，還是當作「輔助」性質會比較恰當。



---
**筆記引用資料：**
[CSS Specificity MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity)
[CSS Specificity - OXXO.STUDIO](https://www.oxxostudio.tw/articles/201405/css-specificity.html)
[CSS 偽元素 ( before 與 after ) - OXXO.STUDIO](https://www.oxxostudio.tw/articles/201706/pseudo-element-1.html)