iT邦幫忙

2025 iThome 鐵人賽

DAY 26
0
Modern Web

前端迷走中:從零開始的新手之路系列 第 26

[Day 26] 續談Cascading──行內樣式、分層順序

  • 分享至 

  • xImage
  •  

昨天我們提到CSS判斷宣告優先順序的流程Cascading,總共會考量七個因素。

考量第一個因素Origin and Importance時,會依據宣告的來源與重要性,分出優先順序。

考量第二個因素Context時,會依據宣告所在的context,判斷他們的內外關係。

接下來,我們要繼續介紹第三個因素The Style Attribute,跟第四個因素Layers。

行内樣式The Style Attribute

如果第二個因素也分不出宣告之間的優先順序,會繼續考量第三個因素The Style Attribute,判斷宣告是否為行内樣式。

原本在CSS 2.1,是在考量選擇器具體度這個因素時,才會判斷宣告是否為行内樣式。當時的規範將行内樣式的具體度定為1,0,0,0,大於所有選擇器。

到了CSS Cascading and Inheritance Level 5(目前是Candidate Recommendation),因為多了Cascade Layer的概念,就將行内樣式變成要另外考量的一個獨立的因素。

由於是直接對元素添加添加style屬性,行内樣式只會出現在作者來源的樣式表。如果衝突的宣告都來自作者樣式表,才會判斷這些宣告是否為行内樣式。

行内樣式的優先順序高於任何選擇器,所以在沒有加上!important的情況下,會優先於<link><style>元素中透過選擇器宣告的樣式,不論他們是否分層;
如果都加上!important,由於優先順序高於任何選擇器,行内樣式同樣也會優先於所有透過選擇器宣告的樣式。

不過,即使優先順序高於任何選擇器,如果其他來自使用者樣式表或使用者代理樣式表的宣告加上了!important,行内樣式仍然會被蓋過,因為它來自於作者來源的樣式表;
即使行内樣式加了!important,也因為優先順序反轉,仍然無法蓋過其他樣式表的衝突宣告。

如果第三個因素也無法決定優先順序,就會繼續考量第四個因素Layers。

分層Layers

CSS Cascading and Inheritance Level 5(目前是Candidate Recommendation)開始,CSS多了Cascade Layer的概念,能夠將樣式表中的規則分成不同層。

於是Cascading時,會多考量Layers這個因素,判斷衝突的宣告值來自哪個分層。

Cascade Layer語法簡介

不論是哪種樣式表或在什麼context,宣告樣式時都可以使用@layer的語法將規則分層;
使用@import匯入外部樣式表的時候,也可以搭配關鍵字layer或函式layer(),將匯入的樣式表分層。

使用這些語法將規則分層時,可以設定名稱,建立命名層。這麼一來,其他地方的規則也可以透過名稱,指定要分入哪一層;
或是也可以不設定名稱,將規則分入匿名層。但在不同地方宣告的匿名層會被視作不同分層;
其他所有未經分層的規則,則會被分入樣式表隱含的最後一層。[^1]

例如以下例子,匯入四個外部樣式表時一併分層。由於最後兩個樣式表都匯入social分層,所以總共只有三層。

@import "components-lib.css"  layer(components);
@import "marketing.css" layer();
@import "comments.css" layer(social);
@import "sm-icons.css" layer(social);

Cascade Layer的優先順序

位於同一層的規則,優先順序相同;
位於不同層的規則,優先順序則取決於所在的分層首次出現於樣式表的先後順序。

如果某個命名層在前面就已經被宣告過,即使它在後面其他地點又被重複宣告,判斷它的出現順序時,仍然會依據它最早被宣告的那個地點。

一般情況下,後面出現的分層會優先於前面的分層;
未經分層的規則,會被分入隱含的最後一層,出現順序最晚,會優先於其他經過分層的規則。

不過如果宣告樣式時加上了!important,同樣會反轉原本的優先順序,讓前面的分層優先於後面的分層;
未經分層的規則,會被分入隱含的最後一層,出現順序最晚,優先順序就會比其他經過分層的規則還低。

範例

例如以下MDN提供的例子,分別在page跟site層內宣告<h1>元素的樣式,將規則分層。

@layer page {
  h1 {
    text-decoration: overline;
    color: red;
  }
}

@layer site {
  h1 {
    text-decoration: underline;
    color: green;
  }
}

/* this does nothing */
@layer site, page;

由於首次宣告page層的地點比首次宣告site層的地點還前面,在site層內宣告的樣式會比page內的樣式還優先,所以標題的顏色會是綠色且帶有底線。

雖然之後又以site、page的順序重複宣告這兩個分層,仍然不會改變宣告的優先順序,因為分層的順序是由首次宣告分層的地點決定。

巢狀分層

每一個分層內,還可以繼續細分出其他子層。

要形成巢狀結構的分層,可以將一個分層包在另外一個分層內;
也可以在命名時,直接以點.將分層名稱相接,在.前的分層內細分出.後的子層。
也可能是在匯入分層的外部樣式表中,宣告樣式時另外以@layer分層;
或是以@import另外將匯入的其他外部樣式表分層。

子層的優先順序,同樣是依據子層首次宣告的地點,以後面宣告的優先;
沒有另外分層的樣式,也會優先於子層內的樣式。

如果加上!important同樣會反轉順序,變成前面宣告的子層優先於後面的;
位於子層的樣式,優先於沒有另外分層的樣式。

範例

以下由MDN提供的例子,對<div>重複宣告了四個規則。其中三個規則,在宣告樣式時分入components層內。不過後面兩個規則是直接以.分別分入components的narrow跟wide子層。

<div>的背景顏色,最終會採用background-color: wheat的宣告,因為沒有分層的規則優先。

邊框最終會採用border: 1rem dashed red的宣告,因為沒有另外分入子層的規則,會優先於位於子層的。

文字顏色最終會採用color: purple !important的宣告。由於加上重要性,會優先考量分入子層的規則,並以越早宣告的子層為優先。

邊框的圓角,最終會採用border-radius: 20%;的宣告。因為只在子層內宣告,會以越晚宣告的子層為優先。

div {
  background-color: wheat;
  color: pink !important;
}

@layer components {
  div {
    background-color: yellow;
    border: 1rem dashed red;
    color: orange !important;
  }
}

@layer components.narrow {
  div {
    background-color: skyblue;
    border: 1rem dashed blue;
    color: purple !important;
     ㄒ: 50%;
  }
}

@layer components.wide {
  div {
    background-color: limegreen;
    border: 1rem dashed green;
    color: seagreen !important;
    border-radius: 20%;
  }
}

小結

今天我們介紹了Cascading的過程中,會考量的第三個因素與第四個因素。

考量第三個因素時,會判斷樣式是不是直接透過元素style屬性宣告的行内樣式。

由於行内樣式是直接針對元素宣告,會優先於其他透過選擇器宣告的樣式,不論有沒有加上!important

如果第三個因素也分不出優先順序,會繼續考量第四因素,判斷規則是否在分層內,如果有的話是哪個分層。

一般情況下,未經分層的規則優先於經過分層的規則;
如果規則位於分層,優先順序會取決於分層的宣告順序。後面宣告的分層,優先於前面宣告的分層。

不過如果加上!important,同樣會反轉順序。變成前面宣告的分層優先於後面宣告的分層,經過分層的規則優先於未經分層的規則。

如果第四個因素也分不出勝負,就會繼續考量第五個因素Specificity。

不過時間不多了,讓我們下集待續~

參考資料

[^1]: W3C, CSS Cascading and Inheritance Level 5


上一篇
[Day 25] 選擇不障礙,讓 CSS 幫你──Cascading 決定最終宣告值
下一篇
[Day 27] 續談Cascading──具體度、範疇鄰近度、出現順序
系列文
前端迷走中:從零開始的新手之路27
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言