若是你在平常工做中使用 CSS,你的主要目標可能會重點圍繞着使事情「看起來正確」。如何實現這一點常常是遠不如最終結果那麼重要。這意味着比起正確的語法和視覺結果來講,咱們更少關心 CSS 的工做原理。瀏覽器
CSS 的視覺結果一般是操做隱藏屬性的間接後果,你可能尚未意識到這一點。某些 CSS 屬性(好比 background-color
)與你看到的內容有直接而明顯的關係。同時,其它像 display
這樣的屬性對於咱們不少人來講仍然是模棱兩可的,由於結果彷佛與上下文環境有很大關係。因此...咱們須要對於CSS進行再次的整理和探索佈局
當加載一個 HTML 文檔時,爲了讓該頁面渲染,有不少事情要發生。性能
第一個步驟就是解析 HTML 文檔。瀏覽器從這一步建立一個文檔樹。樹狀結構是表示像 HTML 這種具備明顯層次結構信息的一種方法。樹中的元素能夠被描述爲相似於族譜,好比祖先、父親、孩子和兄弟姐妹。字體
你可能據說過術語 DOM。它表明文檔對象模型(Document Object Model)。這是文檔樹結構的一種擴展,被用於存儲和操做 Web 文檔內容有關的信息。flex
隨着 HTML 被解析,樣式表和其它資源也被獲取。樣式聲明經過一個稱爲層疊的過程來解釋和肯定。網站
在此過程期間,CSS 屬性的最終值被肯定。在計算後,這些值可能與樣式表中定義的有所不一樣。例如,像 auto
這種關鍵字和相對單位被指派了真實值,而且繼承值被應用了。這些計算值被存儲在一個樹中,相似於 DOM 中的元素,絕不奇怪,它被稱爲 CSS 對象模型或者 CSSOM。spa
如今纔有可能開始渲染頁面的過程。這個過程的第一步是盒模型的計算。這是一個算出元素的尺寸和間距的重要步驟,雖然不必定是元素的最終位置。code
與盒模型相比,不那麼被人熟知的是一個稱爲視覺格式化模型的過程。這個過程肯定元素在頁面上的佈局和位置。它包含了一些你可能已經熟悉的概念,好比定位方案、格式化上下文、顯示模式和堆疊上下文。orm
最後,頁面被渲染。對象
上面的段落中可能有一些你還不熟悉的一些術語。若是是這樣的話,最重要的是要理解層疊、盒模型以及視覺格式化模型。這些術語都是解釋、處理和渲染 HTML 和 CSS 的核心步驟。咱們下面要更詳細地研究一下這三個步驟。
層疊多是最被誤解的 CSS 特性之一。它是指組合不一樣樣式表以及解決 CSS 選擇器之間衝突的過程。
層疊查看聲明的重要性、來源、特殊性以及順序,來肯定使用哪一個樣式規則。
你須要知道什麼:
大多數網站有多個樣式表。一般,樣式是用引用一個 CSS 文件的一個 link
標記,或者 HTML 主體部分的 style
標記添加的。即便最基礎的頁面也會有瀏覽器提供的默認樣式。這種默認樣式表有時被稱爲 user-agent 樣式表。
在層疊期間,樣式表是按以下順序被解釋的:
!important 聲明
做者的樣式表
瀏覽器默認的樣式表
注意:這裏我跳過了用戶樣式表,是由於它們再也不常見,可能不會是讀這本文的全部人要考慮的因素。
在組合這些來源後,若是多個規則應用到同一元素,就用特殊性來肯定應用哪條規則。
特殊性(Specificity)是給選擇器的權重。它常被誤認爲是一個數字。其實是 4 個單獨的數字或者 4 種權重類別。
要計算特殊性,要統計以下選擇器的數目:
ID 選擇器
類選擇器、屬性選擇器和僞類選擇器
元素選擇器和僞元素選擇器
例如:#nav .selected:hover > a::before
將是 1, 2, 2
。
不管有多少個類選擇器,都不會比一個 ID 選擇器有更高的特殊性。當比較選擇器時,咱們首先比較 ID 選擇器的特殊性。只有 ID 選擇器相等時,才比較類選擇器、屬性選擇器和僞類選擇器的特殊性值。若是最後依然相等,才比較元素和僞元素選擇器。
若是每一個類別的特殊性都是相等的,那麼來源中最後的聲明獲勝。
是的!我知道我說的是 4 個類。行內樣式比 ID 選擇器有更高的特殊性。不過因爲在技術上行內樣式在特殊性計算中是第一類,一般你不會與行內樣式競爭,因此很容易記住行內樣式的特殊性老是會佔優。
重要的注意事項:雖然 !important 聲明不會做爲特殊性計算的因素,不過它們比層疊中的普通聲明有更大的優先級。
繼承不是層疊的一部分,不過我在這裏把它包含進來,是由於它常常被與層疊結合在一塊兒討論。
繼承是應用給一個元素的值能夠被傳遞到(或者繼承)子元素上的過程。
你確定知道這個事實,即當把字體屬性應用到 body 或者其它容器元素時,該屬性也會被容器內的全部元素所繼承。這就是繼承。
並不是全部屬性默認都被繼承。理解繼承是編寫更優雅更簡潔 CSS 的關鍵。有時候用 inherit
關鍵字強制繼承會至關有用。
注意: 有些屬性(好比 border-color
)默認值是 currentcolor
。也就是說,它們會使用在 color
屬性上設置的值。默認值與繼承不是一碼事。不過,color
屬性自己常常是被繼承的,因此我傾向於認爲這是一種事實上的繼承。
理解盒模型是佈局和定位時防止挫敗所必需的。盒模型是 CSS 中最基本的概念之一。
盒模型用於計算元素的寬度和高度。它只是計算過程當中的一個步驟,肯定元素的最終佈局和定位並不是徹底依賴於它。
你須要知道什麼:
HTML 中的每一個元素都是一個矩形的盒子。每一個盒子有用元素的外邊距(margin)、邊框(border)、內邊距(padding)和內容區定義的四個區域。
默認狀況下,當咱們設置一個元素的寬度時,只是設置內容區的寬度。當給一個元素添加內邊距、邊框或者外邊距時,是增長了除寬度之外的部分。實際上,這就是說寬度爲 50% 的兩個元素若是添加了內邊距、外邊距或者邊框,就不會並排填滿寬度(由於已經超過了 100% 寬度)。
當設置元素的背景時,不只會填充內容區,還會填充內邊距區和邊框區。
概念上,咱們把一個 HTML 元素看成是一個東西,因此很容易認爲一個元素的視覺邊界等於它的寬度,不過實際卻並不是如此。雖然一個元素的視覺邊界包含了內邊距和邊框區,不過 width 屬性是顯式地被應用到內容盒。
另外一個潛在的困惑來源是 width: auto
的工做機制。寬度爲 auto
是大多數 HTML 元素的默認元素,對於像 div、p 這種塊元素來講,auto
會計算寬度,這樣外邊距、邊框、內邊距以及內容區都組合在一塊兒也能恰好放在可用空間內。
在這種狀況下,感受添加內邊距和外邊距會向內擠壓內容,不過實際上,寬度會被從新計算,以確保全部東西都能恰好放下。相比之下,在設置寬度爲 100%
時,光內容區就會把可用空間填滿,而無論外邊距、內邊距和邊框。
box-sizing
屬性會改變盒模型的工做方式。當 box-sizing
被設置爲 border-box
時,內邊距和邊框會減小內容區的內部寬度,而不是添加元素的總體寬度。也就是說,元素的寬度如今與其視覺寬度是相同的。
盒模型計算元素的大小,而視覺格式化模型(Visual Formatting Model)負責肯定這些盒子的佈局。視覺可視化模型考慮盒子的類型、定位方案、元素之間的關係以及內容施加的約束,來肯定頁面上每一個元素的最終位置和呈現。
你須要知道什麼:
視覺格式化模型遍歷文檔樹,根據 CSS 盒模型生成一到多個渲染元素所需的盒子。CSS 的 display
屬性在肯定一個元素如何參與當前上下文和定位方案中發揮關鍵做用。這些部分綜合在一塊兒,肯定元素的最終佈局和定位。
這是一個複雜的步驟,也是目前爲止最難嘗試和總結的。若是你還沒了解全部這些知識的話,沒有關係。理解如何經過 CSS 屬性操縱定位方案和格式化上下文是個好的開端。若是你理解了該模型的不一樣部分之間的相互做用的話,你就會比大多數人作得更好。起碼你應該知道它們的存在。
咱們知道,在 CSS 中設置 display
屬性能夠肯定如何渲染元素,可是還不清楚其工做原理。事實上,它有時甚至好像是不可預測的。
這是由於,display
屬性肯定了元素的」盒子類型「。這個隱藏屬性由一個內部顯示類型和一個外部顯示類型組成,兩者在一塊兒幫助肯定如何渲染元素。
外部顯示類型一般要麼是解析爲 block
,要麼是解析爲 inline
,而且與咱們對 CSS 中這些 display
屬性的指望幾乎是一致的。從技術上講,外部顯示類型規定一個元素如何參與其父元素的格式化上下文。
內部顯示類型決定元素會生成什麼樣的格式化上下文。這會影響其子元素的佈局排列方式。
想一想彈性盒容器的工做機制。其外部類型是 block
,內部類型是 flex
。它的子元素也能夠有一個 block 外部類型,不過子元素的佈局是受彈性盒容器的格式化上下文所影響的。
思考這種問題的一個方法是,display 的職責是在元素及其父元素之間共享的。
格式化上下文都是與佈局有關。它們是控制容器內元素的佈局以及它們之間如何相互做用的規則。
有些格式化上下文能夠直接在容器上設置,好比經過用 display
屬性值 flex
、grid
或 table
。其它類型的格式化上下文,好比塊格式化上下文和行內格式化上下文能夠被瀏覽器根據須要建立。
注意:在過去,由於塊格式化上下文與浮動交互的方式,理解若是讓瀏覽器創建一個新的塊格式化上下文是很重要的。設置爲塊格式化上下文的元素會包含浮動。不過如今它沒有之前那麼重要。事實上,它甚至都不是現代清除浮動技術的工做原理了。
一個盒子能夠根據三種定位方案之一來佈局,包括常規流、浮動和絕對定位。你可能熟悉浮動和絕對定位,由於在編寫 CSS 時,咱們會更直接與這兩個打交道。常規流只是一個在元素沒有浮動或者定位時默認定位方案的名字。
常規流(Normal Flow)描述了默認定位方案,'in-flow'(流內)描述聽從該方案的元素。你能夠把流內看成是元素根據其源順序和格式化上下文佈局時的天然位置。
浮動(float)是一個 CSS 屬性,它會致使元素脫離常規流,儘量遠地向左或者向右移動,直到它挨着其包含盒或者另外一個浮動元素的邊緣。當浮動發生時,文本和行內元素會環繞着浮動元素。
正常狀況下,若是沒有設置浮動,一個元素的高度會調整以容納其全部的後代元素。當元素被浮動時,就會從流中脫離出來,這意味着容器不會調整其高度以清除它們。
正是這種行爲,讓多行文本、標題以及其它元素環繞着浮動內容而流動。可是有時這是有問題的。清除浮動並設立新的塊格式化上下文會致使容器清除其浮動子元素。這種技術容許浮動被用在佈局上,已經成爲 Web 開發技術的奠定石好久了。它依然是要知道的重要技術,不過正在逐漸被像 Flexbox 和 Grid 這種更新的佈局技術所替代。
絕對定位的元素是完全從流中刪除,而且不像浮動元素,它們對周圍的內容沒有影響。
相對定位的容器容許咱們用絕對定位來控制後代元素的偏移。
相對定位元素也能夠設置偏移,不過這種偏移是相對於父元素的常規位置,而不是另外一個相對定位的容器。
CSS 屬性 top
、bottom
、left
和 right
被用於計算盒子的偏移。這些屬性都不是二維偏移,不過均可以相對於其容器的內容盒來定位每一個邊。
具備重疊偏移的定位元素能夠致使元素佔用同一空間。堆疊上下文用於解決這個問題。
堆疊上下文決定渲染到頁面上的東西的順序。你能夠把一個堆疊上下文看成一個層。堆疊底部的層先繪製,該堆疊之上更高的元素出如今上面。
在一個絕對或者相對定位元素上設置 z-index
屬性是創建新的堆疊上下文的最多見手段。不過,有不少組成堆疊上下文的其它手段,包括設置透明度(opacity)、轉換(transform)、濾鏡(filter)或者使用 will-change
屬性。
不過,使用這些其它手段的緣由並不直觀,而且比開發者的預期相比,這些手段對渲染性能有必定影響。只不過它們有助於理解這些層能夠被瀏覽器單獨渲染。所以,有時由於性能的緣由,有意建立新的堆疊上下文是頗有用的。
除非創建了堆疊上下文,不然設置 z-index
是沒有效果的。z-index
越高,層在堆疊中放在的位置越高。
有關堆疊的最讓人感到困惑的部分之一是,一個新的堆疊上下文能夠創建在一個已有的堆疊上下文內。也就是說能夠有層中層。
在這種狀況下,並不是老是最高的 z-index
會放在最上面。
過久沒有過好好寫博客了...感受好多東西彷彿你理解和寫出來徹底是兩個不一樣的概念...慶幸本身憋着氣仍是把想寫的內容寫出來了。其實關於CSS,我一直都不認爲是一個簡單的樣式表而已。深刻去研究,會發現其難度並不比JS的各類理論知識輕鬆。後面我會嘗試去更深刻的去分析CSS中的各類屬性,以及認真研究研究grid和flex佈局的優點。
完結撒花~
PS:參考資料:《CSS權威指南(第3版)》