圖解CSS:CSS層疊和繼承
CSS中有三個概念是學習CSS必須要掌握的: 層疊 、 繼承 和 權重 。今天我們主要來了解CSS中的層疊和繼承,對於CSS權重這一部分將放到CSS的選擇器中來介紹,因為這一部分和CSS的選擇器耦合的更為緊密。不管是初學者還是有一定工作經驗的同學,花點時間閱讀這篇文章都是很有必要的,這樣有利於你對CSS更清楚的瞭解和理解。感興趣的同學請繼續往下閱讀。
在很多Web開發人員眼中,CSS不是一門程式語言,但它真真切切的是一門 ofollow,noindex" target="_blank">計算機語言 。主要用來為結構化文件,比如HTML、XML等新增樣式,其主要由W3C定義和維護。而CSS是由 Cascading Style Sheets 三個詞的首字母縮寫,很多人將其稱為 層疊樣式表 或者 級聯樣式表 。而我們今天要講的第一個概念就是CSS中的層疊,也對應的是CSS中的第一個字母 C 。看到這裡,或許你就知道為什麼會說層疊是CSS的重要概念之一了。
層疊
層疊是CSS固有的一個東東,它賦予了 層疊樣式表 層疊性。層疊可能是一個很強大的工具,但如果錯誤的使用它可能會導致樣式表的脆弱性,使用Web開發人員在任何時候不得不進行更改時都感到頭痛。比如說:
初學CSS的同學,特別是Web其他端的程式設計師(比如服務端、客戶端)在獨立編寫CSS的時候,經常會碰到這樣的一個現象: 為什麼寫的樣式不起作用呢 ?面對這樣的場景很多人會採用非常暴力的手段來處理,比如通過新增 !important
或直接在HTML的元素上新增內聯CSS。這就是令很多同學感到疑惑的一點?為什麼要這樣做呢?其實這和接下來要介紹的層疊(也有很多人稱之為 級聯 )有很大的關係。
層疊的定義
因為我們將要深入的學習和討論CSS層疊是如何工作的相關細節,因此我們有必要的瞭解 W3C規範 是如何定義它的:
The cascade takes a unordered list of declared values for a given property on a given element, sorts them by their declaration’s precedence, and outputs a single cascaded value. —— W3C規範
大致意思是: 該層疊將獲取給定元素上給定屬性的宣告值的無序列表,按宣告的優先順序對它們進行排序,並輸出單個層疊值 。
CSS層疊是一種演算法,瀏覽器通過它來決定將哪些CSS樣式規則應用到一個元素上。很多人喜歡把它看作是“獲勝”的樣式,按照CSS中的術語來說,它的權重更高(後面我們會深入介紹)。
為了更好的理解CSS層疊,將CSS宣告看作具體的”屬性“(attributes)。這些屬性可以是宣告的各個部分比如說CSS選擇器或CSS屬性,甚至是CSS宣告的位置相關(比如它的原始或原始碼中的位置)。
CSS層疊將會根據自己的演算法,採用這些屬性中的一些,併為每個屬性分配一個權重。如果CSS規則在高優先順序上獲勝(選擇器權重高),那麼這個樣式規則就會獲勝,即生效。但是,如果在給定的權重下極然有兩個規則衝突,演算法將繼續” 向下層疊 ”,並且會檢查低優先順序屬性,直到找到一個勝出的規則。
簡單的說,當多個相互衝突的CSS宣告應用於同一個元素時,CSS層疊演算法會根據一定的機制,從最高權重到最低權重的順序列出:
- 來源和重要性
- 選擇器權重
- 出現的順序
- 初始和繼承屬性(預設值)
接下來圍繞這幾點來進行展開。
來源和重要性
層疊檢查的最高加權屬性是給定規則的重要性和來源的組合。就CSS規則的來源而言,規則主要來自三個地方:
- 編寫者規則(Author) :這是HTML文件宣告的CSS。也就是我們前端開發人員編寫的,根據文件語言(比如HTML)約定給源文件指定樣式表。這也是我們能夠控制的唯一來源
- 使用者(User) :這是由瀏覽器的使用者定義和控制的。不是每個人都會有一個,但是當人們新增一個時,通常是為了覆蓋樣式和增加網站的可訪問性。比如,使用者可以指定一個售有樣式表的檔案,或者使用者代理可能會提供一個用來生成使用者樣式(或者表現得像這樣做了一樣)的介面
- 使用者代理(User-Agent) :這些是瀏覽器為元素提供的預設樣式。這就是為什麼
input
在不同的瀏覽器上看起來略有不同,這也是人們喜歡使用CSS重置樣式,以確保重寫使用者代理樣式的原因之一
注意,使用者可能會修改系統設定(例如,系統配色),這會影響預設樣式表。然而,有些使用者代理實現讓預設樣式表中的值不可改變
這三種樣式表將在一定範圍內重疊,並且它們按照層疊互相影響。
CSS層疊給每個樣式規則賦予了權重,應用幾條規則時,權重最大的優先。預設情況下,編寫者樣式表中的規則比使用者樣式表中的規則權重高。CSS宣告的重要性由適當命名的 !important
語法決定。 !important
的CSS規則自動將它跳到層疊演算法的前面,這也是為什麼不鼓勵在樣式中使用 !important
的原因之一。覆蓋使用 !important
的樣式只能使用其他的 !important
的規則來完成,如果你的專案足夠大,用的 !important
又足夠地多,那麼你的CSS就會變得更為脆弱,更難於維護。對於 !important
的使用,建議你只在其他所有方法都失效的情況之下使用。
那麼CSS中層疊演算法又是如何判斷哪個宣告獲勝:
CSS層疊在判斷哪個宣告獲勝時考慮這兩個屬性的組合。每個組合都有一個權重(類似宣告的部分權重),權重最高的規則獲勝。以下是瀏覽器考慮的各種來源和重要性的組合,按從最高權重到最低權重的順序列出:
!important !important !important @keyframes
當瀏覽器遇到兩個或更多衝突的CSS宣告,其中一個在來源和重要性級別獲勝時,CSS層疊就會解決這個規則。但是,如果相互衝突的宣告具有相同的重要性和來源級別,則層疊將繼續考慮選擇器的權重。
選擇器權重
CSS層疊中的第二個權重是 選擇器的權重 。在這個層中,瀏覽器檢視CSS宣告中使用的選擇器。作為前端開發人員,我們只能控制編寫者樣式規則。因為我們無法對來源的規則做太多的更改。但是,你會發現,只要在程式碼中不使用 !important
,對於CSS層疊中的選擇器權重這一層,還是有較多的方式可以控制。
對於一個選擇器的權重,將會按下面這樣的規則進行計算:
- 如果宣告來自一個行內樣式(
style
屬性)而不是一條選擇器樣式規則,算1
,否則就是0
(=a
)。HTML中,一個元素的style
屬性值是樣式規則,這些屬性沒有選擇器,所以a=1, b = 0, c = 0, d = 0
,即1, 0, 0, 0
- 計算選擇器中
ID
屬性的數量 (= b
) - 計算選擇器中其它屬性和偽類的數量 (
= c
) - 計算選擇器中元素名和偽元素的數量 (
= d
)
4
個數連起來 a-b-c-d
(在一個基數很大的數字系統中)表示特殊性,比如下面這樣的示例:
*{}/* a=0 b=0 c=0 d=0 -> 選擇器權重 = 0,0,0,0 */ li{}/* a=0 b=0 c=0 d=1 -> 選擇器權重 = 0,0,0,1 */ li:first-line {}/* a=0 b=0 c=0 d=2 -> 選擇器權重 = 0,0,0,2 */ ul li{}/* a=0 b=0 c=0 d=2 -> 選擇器權重 = 0,0,0,2 */ ul ol+li{}/* a=0 b=0 c=0 d=3 -> 選擇器權重 = 0,0,0,3 */ h1 + *[rel=up]{}/* a=0 b=0 c=1 d=1 -> 選擇器權重 = 0,0,1,1 */ ul ol li.red{}/* a=0 b=0 c=1 d=3 -> 選擇器權重 = 0,0,1,3 */ li.red.level{}/* a=0 b=0 c=2 d=1 -> 選擇器權重 = 0,0,2,1 */ #x34y{}/* a=0 b=1 c=0 d=0 -> 選擇器權重 = 0,1,0,0 */ style=""/* a=1 b=0 c=0 d=0 -> 選擇器權重 = 1,0,0,0 */
在網際網路上也有很多很形象的圖來解釋CSS選擇器權重的,比如下圖:
上圖來自於 @Estelle Weyl 的《 CSS Specificity 》一文 。
有關於CSS選擇器權重更詳細的介紹,我們將在CSS選擇器一文中進行闡述,如果你想先進行了解,可以閱讀 @Vitaly Friedman 的《 CSS Specificity: Things You Should Know 》一文。譯文可以點選這裡。也可藉助 @Keegan Street 的 選擇器權重計算工具 更形象的瞭解CSS的選擇器權重的計算。
出現的順序
CSS層疊演算法的最後一個主要層是按原始碼中出現的順序來計算。當兩個選擇器具有相同的權重時,在原始碼中最後的宣告獲勝。
因為CSS在層疊中考慮源順序,所以載入樣式表的順序就顯得尤為重要。如果在HTML文件的 head
引入了兩個樣式表,那麼第二個樣式表將覆蓋第一個樣式表中的規則。
初始和繼承屬性
雖然初始值 initial
和繼承值 inherit
並不是CSS層疊中真正組成部分,但是如果沒有針對元素的CSS宣告,它們將確定發生什麼。通過這種方式,它們確定元素的預設樣式值。
有關於初始和繼承更詳細的介紹,將在下一節中進行詳細地闡述。感興趣的同學,請繼續往下閱讀。
參與層疊計算的CSS實體
只有CSS宣告,就是屬性名值對,會參與層疊計算。這表示包含CSS宣告以外實體的 @
規則不參與層疊計算,比如包含描述符的 @font-face
。對於這些情形, @
規則是做為一個整體參與層疊計算,比如 @font-face
的層疊是由其描述符 font-family
確定的。如果對同一個描述符定義了多次 @font-face
,則最適合的 @font-face
是做為一個整體而被考慮的。
包含在大多數 @
規則的CSS宣告是參與層疊計算的,比如包含於 @media
、 @documents
或者 @supports
的CSS宣告,但是包含於 @keyframes
的宣告不參與計算,正如 @font-face
,它是作為一個整體參與層疊演算法的篩選。
注意 @import和@charset遵循特定的演算法,並且不受層疊演算法影響。
繼承
在CSS中,每個CSS屬性定義的概述都指出了這個屬性是預設繼承的還是預設不繼承的。這決定了當你沒有為元素的屬性指定值時該如何計算值。當元素的一個 繼承屬性沒有指定值時,則取父元素的同屬性的計算值。只有文件根元素取該屬性的概述中給定的初始值;當元素的一個非繼承屬性沒有指定值時,則取屬性的初始值。比如:
html { font-size: small; }
這個規則在HTML文件的根元素 html
設定了一個 font-size
屬性,而這個屬性是會被繼承的(在CSS中有些屬性天性就會被繼承)。正如上面所示, html
元素的所有找後元素都將被繼承這個屬性,比如下釁中藍框中顯示的一樣:
上圖藍框中告訴 body
元素繼承 html
元素中的 font-size: small;
。開發者工具中會提示我們“ Inherited from html ”。那麼問題來了,在CSS中哪些屬性是會被繼承的?其實在W3C規範中各個屬性的描述已經很清楚的告訴我們了。比如說 border
屬性,在描述其語法時,在列表中有一個 Inherited 描述項的值為 no 。這也就告訴我們 border
屬性是不能被繼承的。反之,再看 font-size
屬性,語法描述的列表中同樣有一個 Inherited 描述項,只不過它的值不是 no ,而是 yes ,也就是說 font-size
屬性是會被繼承的。
這決定了當你沒有為元素的屬性指定值時該如何計算值。
如果你平時閱讀規範仔細的話,不難發現,在介紹每個屬性的語法引數的時候,都會有一個 Initial 引數,該引數主要指定每個屬性的初始值。CSS屬性已經給出的初始值針對不同的繼承和非繼承屬性有不同的含義:
- 對於繼承屬性,初始值只能被用於沒有指定值的根元素上
- 對於非繼承屬性,初始值可以被用於任意沒有指定的元素上
這樣我們引出兩個概念: 初始值 和 繼承值 ,除了這兩個概念之外,在CSS屬性中還有一個 計算值 ,該值由指定的值計算而來:
- 處理特殊的值
inherit
和initial
- 根據屬性的摘要中關於計算值描述的方法計算出值
計算屬性的計算值通常包括將相對值轉換成絕對值,比如 em
和 %
這樣的單位。例如:
font-size: 16px; padding-top: 2em;
其中 padding-top: 2em
就是一個計算值,其計算出來的值將根據 font-size
做為基數計算(在此示例中),在此計算出來的值是 32px
。然而,有些屬性的百分比值會轉換成百分比的計算值(比如 width
)。另外, line-height
屬性值是沒有單位的數字,其值也是一個計算值。
對於CSS的計算值,在不同的瀏覽器中其計算出來的值有時候會略有偏差。
如果你感興趣的話,可以開啟瀏覽器的開發者工具,檢視對應的計算值(比如,Chrome開發者工具,有一個 Computed選項 ,該選項展示的就是對應的CSS計算值),如下圖所示:
其中計算值的最主要用處是繼承,包括 inherit
關鍵詞。
最後總結兩點:
- 當元素的一個繼承屬性沒有指定值時,則取父元素的同屬性的計算值,只有文件根元素取該屬性的概術中給定的初始值
- 當元素的一個非繼承屬性沒有指定值時,則取屬性的初始值
看到這裡,或許你知道了什麼叫繼承和非繼承,以及他們取值方式。但你可能還在糾結,在CSS中到底哪些屬性是繼承屬性,哪些不是繼承屬性?其實這個問題我也沒辦法準確的回答您,因為我也沒有做過這方面的統計。不過我可以告訴大家兩個小經驗:
- 在CSS中一些關於字型、文字和顏色等屬性都是可繼承屬性
- 在CSS中一些跟佈局和盒子模型的屬性都是非繼承屬性
如果你想準確的知道答案,可以通過 這裡整理的屬性表格 進行統計。只要 Inherited 選項是 Yes 的都表示是繼承屬性,否則都是非繼承屬性。
處理CSS繼承的機制
在CSS中提供了處理CSS繼承的機制,簡單地講就是CSS提供了幾個屬性值,可以用來處理屬性的繼承。這幾個屬性值就是 initial
、 inherit
、 unset
和 revert
。其實除了這四個屬性值之外,還有一個 all
屬性值。雖然這幾個屬性值主要用來幫助大家處理CSS屬性繼承的,但他們之間的使用,還是有一定的差異化。接下來我們一看看這幾個屬性值的實際使用以及對應的差異化。
initial
在CSS中,每個屬性都具有一個初始值,其實也就是CSS屬性的預設值。在CSS規範中,都對每個屬性的初始值做出了相關的定義。比如 text-align
的初始值是 left
, display
的初始值是 inline
。
而這裡,我們要說的是CSS的關鍵詞 initial
:
If the cascaded value is the initial keyword, the property’s initial value becomes its specified value.
大致的意思是:“如果層疊值是 initial
關鍵詞,則屬性的初始值將成為其指定值”。換句話說,如果你在元素樣式的設定中顯式的設定某個屬性的值為 initial
時,其實就表示設定了該屬性的預設值。
從文字上理解可能有點困惑,我們通過一個小例來幫助大家理解。假設我們有一個 <p>
元素,接觸過CSS的同學都知道,它是一個塊元素,為了好看,咱們新增一點修飾的樣式程式碼:
p { background: #f36; padding: 2rem; font-size: 2rem; color: #fff; }
效果看起來像下面這樣:
如果我們希望 p
元素變成行內元素時,按照我們以前的處理方式,需要手動處理瀏覽器預設樣式(User-Agent 使用者代理樣式),也就是顯示的重置:
p{ dispaly: inline; }
block
和 inline
效果對比如下:
前面提到過 inline
是 display
的初始值(也就是預設值),而在規範中也提到過:
你在元素樣式的設定中顯示的設定某個屬性的值為initial時,其實就表示設定了該屬性的預設值。
也就是說,我們可以給 display
設定 initial
關鍵詞:
p { display: initial; }
這個時候得到的效果其實和使用 display:inline
是一樣的:
如果我們通過瀏覽器檢查器中的計算值(Computed)一項可以看出來, display
設定為 initial
時,會覆蓋使用者代理的樣式值 block
:
接下來,我們再來看一個繼承屬性 color
。所以 <p>
元素的後代元素 <strong>
也會繼承 <p>
元素中設定的 color: #fff
值。如果我們顯式的在 strong
中設定 color
的值為 initial
時,那麼 strong
的 color
將重置為預設值。由於我們沒有設定預設的 color
顏色,那麼這個時候,瀏覽器將會把一個計算值賦予成 color
的初始值:
inherit
CSS添加了一個 inherit
關鍵詞屬性值,可以讓元素強制繼承父元素的某個屬性的值。前面也說過,CSS中有些屬性自動就是可繼承屬性,比如 font-size
、 color
之類,但也有很多屬性又是非繼承屬性,比如 border
、 border-radius
之類的。在這裡,如果在非常繼承的屬性上顯示的設定了 inherit
關鍵詞,表示該元素將繼承父元素指定的屬性值或者計算值。
為了同樣的能更好理解 inherit
,來看一個示例,在這個示你中,我們用 border
來做例子:
<div class="wrapper" style="border:5px solid blue;"> <div>...</div> </div>
從效果上可以看出來, div.wrapper
上的 border
樣式並沒有繼承其子元素 div
上。如果我們想讓 .wrapper
的子元素 div
也要具有與 .wrapper
元素的 border
樣式,就需要在該元素 div
上顯式設定相同的 border
樣式:
<div class="wrapper" style="border:5px solid blue;"> <div style="border:5px solid blue;">...</div> </div>
有了 inherit
關鍵詞之後,事情要變得簡單地多,只需要在 .wrapper
的子元素上設定 border: inherit;
:
<div class="wrapper" style="border:5px solid blue;"> <div style="border: inherit;">...</div> </div>
得到的效果將是一樣的:
上面的示例是父元素設定了 border
樣式,所以其繼承了父元素的 border
樣式。那如果將上面的示例稍做修改,在元素外套一個 div
,而這個 div
不做任何樣式的設定。將又會變成一個什麼樣呢?直接上示例吧:
<div class="wrapper" style="border:5px solid blue;"> <div> <div style="border: inherit;" class="ele">...</div> </div> </div>
效果如下:
可以看到 div.ele
僅繼承了其父元素 div
的 border
屬性的計算值,並未繼承其祖先元素 .wrapper
的 border
屬性的設定值,通過瀏覽器開發者工具,可以看得一目瞭然:
這個示例說明:僅管元素自身顯式的設定了 inherit
關鍵詞,但是,如果其父元素沒有明確指定樣式,那麼其最終效果將和 revert
的效果一致。 即繼承的是其父元素的計算值,也就是瀏覽器預設樣式(User Agent Stylesheet) 。
revert
revert
值早前被稱之為 default
。表示沒有使用任何屬性值。
我們都知道,如果沒有使用作者樣式表(也就是Web開發人員自己寫的樣式表),那麼瀏覽器將會按這樣的過程去檢測,元素呼叫的樣式:
unset
還是拿示例來說吧。比如我們一個 div
元素,我們並沒有顯式的在自己的樣式表中設定其 display
屬性的值。對於最後的渲染結果,瀏覽器將會使用使用者代理規則的樣式 display:block
。
根據前面介紹的,就算是我們在 div
中顯式的設定 display:revert
,該元素也將使用使用者代理規則中的 display:block
樣式。同樣,我們在另一個 div
元素中使用 display:initial
,根據前面介紹的,那在這個 div
將會採用 display
的初始值 inline
。比如下面的效果:
我們再來看一個繼承屬性的運用場景。因為在 div
元素上設定了 color:#fff;
樣式,這是使用者寫的樣式,而且 color
是一個繼承屬性,只要是 div
的後代元素都將會繼承 color
的屬性值。根據前面所說 revert
的檢測機制是,先檢測使用者自己寫的樣式,然後再檢測使用者代理樣式,如果兩都沒有,才會設定 unset
的樣式。所以最終我們看到的效果如下:(第二個設定了 color:initial;
)。
unset
unset
是 initial
和 inherit
的組合。當屬性設定為 unset
時,如果它是一個繼承屬性,那麼它相當於是 inherit
;如果它不是,則相當於 initial
。
有一些屬性,如果沒有明確指定,將預設是 inherit
。比如,我們給元素設定一個 color
,那它將適用於所有預設的子元素。而其它屬性,如 border
則預設是非繼承屬性。
<div class="wrapper" style="border: 5px solid blue;color: #fff;"> <div class="ele">...</div> </div>
此時效果如下:
示例中 color
屬性被繼承了,但 border
屬性沒有被繼承。
將上面的示例程式碼稍作調整:
<div class="wrapper" style="border: 5px solid blue;color: #fff;"> <div class="ele" style="border: unset; color:unset;">...</div> </div>
div.ele
元素的 border
和 color
都設定了 unset
值。也就是說它將運用 initial
或者 inherit
的值。具體取決於哪個值,這得根據屬性的預設行為是什麼來決定。如果預設屬性是 inherit
,那將運用的是 inherit
;如果預設屬性是 initial
,那將運用的是 initial
。
上面的示例中, border
屬性採用的是 initial
, color
屬性採用的是 inherit
。
all
在CSS中, all
是一個簡寫屬性,其重設除了 unicode-bidi
和 direction
之外的所有屬性至它們的 初始值 或 繼承值 。 all
有三個值:
-
initial
:該關鍵字代表改變該元素或其父元素的所有屬性至初始值。 -
inherit
:該關鍵字代表改變該元素或其父元素的所有屬性的值至他們的父元素屬性的值。 -
unset
:該關鍵字代表如果該元素的屬性的值是可繼承的,則改變該元素或該元素的父元素的所有屬性的值為他們父元素的屬性值,反之則改變為初始值。
來看示例。比如我們一個這樣的一個HTML結構:
<div>...<strong>...</strong> ...</div>
給他們設定一些樣式:
body { padding: 2vw; } div { background: #f36; padding: 2rem; font-size: 2rem; color: #fff; margin-bottom: 3rem; } strong { font-size: 3rem; }
看到的效果如下:
這效果正是我們想要的。 div
顯式的指定了 background
、 padding
、 font-size
、 color
和 margin-bottom
屬性值,而其中 background
、 padding
、 margin-bottom
是 非繼承屬性 ,而 color
和 font-size
是 繼承屬性 。除此之外, div
還有一個客戶端代理樣式 display:block
,這個屬性也是一個 非繼承屬性 。另外 strong
元素設定了一繼承屬性 font-size
,這個元素預設情況下繼承了共父元素的 color
屬性,同時還繼承了 html
元素的 font-family
和 line-height
屬性。當然, strong
元素也有一個客戶端代理樣式 font-weight:bold
。
上面看到的效果是我們平時使用的時候效果。如果這個時候,我們在 div
和 strong
同時設定 all:inherit;
時,得到的效果和前面的效果完全不一樣:
這個時候 div
和 strong
重置了當初自己設定的屬性,並且繼承了各自父元素的一些屬性:
-
div
元素繼承了body
元素的padding
、font-family
、line-height
,同時也繼承了body
代理客戶端的樣式color
、background
和display
-
strong
元素繼承了div
元素的樣式
所以最後你看到的效果就像上圖那樣子。我們再把 all
的值設定為 initial
:
這個時候 div
和 strong
樣式都重置到了對應的初始樣式,也就是對應的屬性的預設樣式,包括代理客戶端樣式也重置為對應屬性的初始值。
最後來看 all
的值設定為 unset
的效果,下面的示例,我只在 strong
元素上設定 all:unset
,其效果就足以說明一切:
效果中的第一個是沒設定 all:unset
,第二個設定了 all:unset
。這個時候 strong
元素的 font-size
和 font-weight
繼承了其父元素的 font-size
和 font-weight
。
all
在CSS中有時候是一個屬性,比如這裡說的就是屬性,但有的時候它還是CSS中某些屬性的值。比如我們常在 transition
中用到的 all
,那這個時候就是屬性值。到目前為止,CSS中的 all
屬性也得到了眾多瀏覽器的支援。
CSS樣式的計算
CSS屬性的最終值會經過四步計算:
- 通過指定來確定值,常稱之為 指定值(Specified Value)
- 接著處理得到一個用於繼承的值,常稱之為 計算值(Computed Value)
- 然後如果有必要的話轉化為一個絕對值,常稱之為 應用值(Used Value)
- 最後根據本地環境限tmhj進行轉換,常稱之為 實際值(Actual Value)
那麼什麼是 指定值 、 計算值 、 應用值 和 實際值 呢?
指定值
使用者代理必須先根據下列機制(按優先順序)給每個屬性賦值一個指定值:
- 如果層疊產生了一個值,就使用它
- 否則,如果屬性是繼承的並且該元素不是文件樹的根元素,使用其父元素的計算值
- 否則使用屬性的初始值,每個屬性的初始值都在屬性定義中指出了
計算值
指定值通過層疊被處理為計算值,例如, URI
被轉換成絕對的, em
和 ex
單位被計算為畫素或者絕對長度。計算一個值並不需要使用者代理渲染文件。使用者代理規則無法處理為絕對 URI
的話,該 URI
的計算值就是指定值。
一個屬性的計算值由屬性定義中Computed Value行決定。當指定值為 inherit
時,計算值的定義可以依據繼承中介紹的規則來計算。即使屬性不適用(於當前元素),其計算值也存在,定義在 'Applies To' 行。然而,有些屬性可能根據屬性是否適用於該元素來定義元素屬性的計算值
應用值
處理計算值時,儘可能不要格式化文件。然而,有些值只能在文件佈局完成時確定。例如,如果一個元素的寬度是其包含塊的特定百分比,在包含塊的寬度確定之前無法確定這個寬度。應用值是把計算值剩餘的依賴(值)都處理成絕對值後的(計算)結果。
實際值
原則上,應用值應該用於渲染,但使用者代理可能無法在給定的環境中利用該值。例如,使用者代理或許只能用整數畫素寬度渲染邊框,因此不得不對寬度的計算值做近似處理,或者使用者代理可能被迫只能用黑白色調而不是全綵色。實際值是經過近似處理後的應用值。
小結
本文涉及了大量的內容,希望它能幫助大家理解樣式是如何受到我們編寫和應用的影響。特別是CSS的層疊和繼承。在實際使用的時候,如果很好的運用這些概念和手段,可以更好的幫助大家少寫很多樣式程式碼,而且更易於維護自己的CSS程式碼。
用張圖來表示如下: