Learning Objectives
- 說明同一個元素被多條規則選到、設定互相衝突時,瀏覽器比較的順序:來源與 !important、權重、出現順序
- 算出選擇器的權重 (a, b, c),並逐欄比較誰比較高
- 說明 style 屬性與 !important 為什麼會讓樣式難以維護,改用提高權重或調整順序解決
- 分辨會繼承與不會繼承的屬性,用 inherit、initial 控制繼承
- 在開發者工具裡看懂被劃掉的規則
兩條規則打架的時候
寫了一陣子 CSS 之後,你一定會遇到這種情況:明明寫了 .current { color: white; },連結卻還是灰色。
CSS 沒有報錯,因為它沒有錯:另一條規則也選中了這個連結,而且那一條比較「強」。
CSS 的 C 是 Cascading(層疊):同一個元素常常同時被好幾條規則選中,每條規則都想設定它的 color。 瀏覽器依一套固定的順序決定聽誰的,這套順序就叫層疊。另外,有些值沒有任何規則設定,元素會向父元素拿,這叫繼承(inheritance)。
搞懂這兩件事,你就不需要靠「多加幾個 !important 試試看」來修樣式。
層疊的比較順序
瀏覽器只比較「選中這個元素、而且設定同一個屬性」的宣告。比較的順序是:
| 順序 | 比什麼 | 說明 |
|---|---|---|
| 1 | 來源與重要性 | 瀏覽器預設樣式(user-agent stylesheet)最弱,你寫的作者樣式(author)比它強;加了 !important 的宣告勝過一般宣告 |
| 2 | style 屬性 | 寫在標籤上的 style="…" 勝過樣式表裡的一般規則,不論權重多高 |
| 3 | 權重(specificity) | 選擇器越「精準」越強,下一段細講 |
| 4 | 出現順序 | 以上都一樣時,寫在後面的勝出(後面的檔案、同一檔案裡比較下面的規則) |
第 1 步的「瀏覽器預設樣式」就是標題變大、連結變藍、清單有縮排的來源。你寫的任何規則都比它強,所以只要寫 a { color: #52606d; } 就能蓋過連結的藍色。
還有使用者自訂樣式、動畫、
@layer圖層等來源,進階課程才會遇到;以上四步已經能解釋你平常遇到的絕大多數情況。
權重 (a, b, c) 怎麼算
把選擇器拆開,數三種東西:
| 欄 | 數什麼 | 例子 |
|---|---|---|
| a | id 選擇器 | #signup |
| b | class、屬性選擇器、虛擬類別 | .btn、[target]、:hover、:nth-child(2) |
| c | 元素選擇器、虛擬元素 | a、li、::before |
全域選擇器 * 和組合器(空白、>、+、~)不算。:is()、:not()、:has() 本身不算,算括號裡最高的那一個;:where() 一律是 0。
| 選擇器 | 權重 |
|---|---|
a |
(0, 0, 1) |
.btn |
(0, 1, 0) |
.event-card a |
(0, 1, 1) |
a.btn.primary |
(0, 2, 1) |
#signup |
(1, 0, 0) |
#main-nav a:hover |
(1, 1, 1) |
由左到右逐欄比:先比 a,a 大的直接勝出,後面不用看;a 一樣才比 b,再比 c。
所以 #signup(1, 0, 0)勝過 .a.b.c.d.e.f.g.h.i.j.k(0, 11, 0):欄和欄之間不會進位,11 個 class 也贏不了一個 id。
這也是上一課說「樣式用 class、不要用 id」的原因:id 的權重太高,之後想用 class 規則改它,只能寫出更長的選擇器或動用 !important。
style 屬性與 !important
style 屬性直接寫在標籤上,勝過樣式表裡的任何一般規則。想用 CSS 改它,只剩 !important 一條路,這就是第 9 課建議把樣式搬進 CSS 的原因。
!important 加在值的後面,讓這條宣告跳過權重比較:
.current { color: #ffffff !important; }
它看起來很方便,但會讓樣式越來越難改:
- 下次要蓋過它,只能再寫一個權重更高的
!important,整份 CSS 開始比誰的!important多。 - 讀程式的人看不出真正的原因:是選擇器寫錯?順序不對?還是被別的規則蓋掉?
遇到樣式被蓋掉時,先依層疊順序找原因,再用正常的方法解決:
| 原因 | 解法 |
|---|---|
| 對方的權重比較高 | 讓你的選擇器多一個 class(.nav .current),或把對方的 id 選擇器改成 class |
| 權重一樣但你寫在前面 | 把規則移到後面 |
| 被 style 屬性蓋掉 | 刪掉 HTML 上的 style 屬性 |
!important 只留給少數真正需要的地方,例如覆蓋你改不到的第三方樣式;用的時候加註解說明原因。
小測驗:層疊與權重
#nav .item a 的權重是多少?
一條規則用了 11 個 class 選中按鈕(0, 11, 0),另一條用 #signup(1, 0, 0)。都設定 color,誰勝出?
樣式表裡先寫 .btn { color: red; },後面再寫 .btn { color: blue; },按鈕是什麼顏色?
<p id="intro" style="color: green;">,樣式表裡有 #intro { color: red; }。段落是什麼顏色?
繼承:沒設定的值向父元素拿
在 <article> 設定 color 和 font-family,裡面的標題、段落都會跟著變,不用一個一個寫,這是繼承。
但 border 不會:article 有框線,裡面的每個段落並不會各自多一圈框線。
| 通常會繼承(和文字有關) | 通常不會繼承(和盒子有關) |
|---|---|
color、font-family、font-size、font-weight、line-height |
margin、padding、border |
text-align、letter-spacing、list-style、visibility |
background-color、width、height、display |
每個屬性會不會繼承,MDN 的屬性頁面都有寫(「Inherited: yes/no」)。
繼承的值最弱:只要有任何規則直接選中這個元素,即使只是瀏覽器預設樣式,都會蓋過繼承來的值。
所以文章的 color 傳不到 <a>(瀏覽器預設讓連結變藍),字型也傳不到 <button>(瀏覽器預設給按鈕自己的字型)。
用關鍵字可以手動控制:
| 關鍵字 | 意思 |
|---|---|
inherit |
用父元素的值(不會繼承的屬性也能強制繼承) |
initial |
用這個屬性在規格裡的初始值(例如 color 的初始值通常是黑色) |
unset |
會繼承的屬性當成 inherit,不會繼承的當成 initial |
button, input, select { font: inherit; } /* 表單元件跟著頁面的字型,常見的重設寫法 */
.post a { color: inherit; } /* 連結跟著文章的顏色,再用底線區分 */
在開發者工具裡看層疊
在網頁上按右鍵選「檢查」(或按 F12),選一個元素,右側的「樣式」(Styles)面板就是這一課的內容:
- 所有選中這個元素的規則,由強到弱從上往下列出;最上面是
element.style,也就是 style 屬性。 - 被更強的規則蓋掉的宣告會被劃掉,就像權重實驗室裡的灰色規則。
- 往下捲會看到「繼承自 article」這種分組,列出從祖先繼承來的屬性;最下面通常是
user agent stylesheet,也就是瀏覽器預設樣式。 - 「計算後」(Computed)分頁列出每個屬性最後的值,點開可以看到是哪一條規則決定的。
- 滑鼠移到選擇器上,會顯示它的權重(Specificity)。
樣式沒有照預期出現時:先選中那個元素,看你的宣告是沒出現(選擇器沒選中、或語法錯了)還是被劃掉(被更強的規則蓋掉),再依層疊順序找原因。
小測驗:繼承與 !important
在 <section> 設定下面哪一個屬性,裡面的段落會跟著繼承?
為什麼不建議用 !important 解決「樣式被蓋掉」?
body { font-family: "Noto Sans TC", sans-serif; },但頁面上的按鈕仍是另一種字型。最常見的修法是?
重點整理
- 只有「選中同一個元素、設定同一個屬性」的宣告才會互相比較。
- 比較順序:來源與
!important→ style 屬性 → 權重 → 出現順序(後面的勝出)。你寫的樣式勝過瀏覽器預設樣式。 - 權重 (a, b, c):a 數 id,b 數 class/屬性/虛擬類別,c 數元素/虛擬元素;由左到右逐欄比,不會進位。
- 樣式被蓋掉時先找原因,用提高權重、調整順序、刪掉 style 屬性解決,不要用
!important硬壓。 - 文字類屬性會繼承、盒子類屬性不會;繼承的值最弱;用
inherit、initial、unset手動控制。 - 開發者工具的「樣式」面板由強到弱列出規則,被蓋掉的宣告會劃掉。
下一課講文字與字型:字型堆疊、自己提供字型檔、行高,以及中文排版的習慣。