昨天我們提到CSS判斷宣告優先順序的流程Cascading,總共會考量七個因素。
考量第一個因素Origin and Importance時,會依據宣告的來源與重要性,分出優先順序。
考量第二個因素Context時,會依據宣告所在的context,判斷他們的內外關係。
接下來,我們要繼續介紹第三個因素The Style Attribute,跟第四個因素Layers。
如果第二個因素也分不出宣告之間的優先順序,會繼續考量第三個因素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。
CSS Cascading and Inheritance Level 5(目前是Candidate Recommendation)開始,CSS多了Cascade Layer的概念,能夠將樣式表中的規則分成不同層。
於是Cascading時,會多考量Layers這個因素,判斷衝突的宣告值來自哪個分層。
不論是哪種樣式表或在什麼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);
位於同一層的規則,優先順序相同;
位於不同層的規則,優先順序則取決於所在的分層首次出現於樣式表的先後順序。
如果某個命名層在前面就已經被宣告過,即使它在後面其他地點又被重複宣告,判斷它的出現順序時,仍然會依據它最早被宣告的那個地點。
一般情況下,後面出現的分層會優先於前面的分層;
未經分層的規則,會被分入隱含的最後一層,出現順序最晚,會優先於其他經過分層的規則。
不過如果宣告樣式時加上了!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