深刻理解文檔流(Document Flow)和視覺格式化模型(CSS Visual Formatting Model)

前言

最開始學習CSS的時候,我一度認爲CSS基礎很簡單,可是CSS佈局是一塊難啃的骨頭。又到後來,我去亞馬遜買了暢銷書HTML and CSS, design and build websites,書上內容淺顯易懂,可是關於CSS佈局,也就只有一章,並且彷佛也沒有提到複雜的文檔流,我印象中也並不知道'BFC', 'Stacking Contexts'這些名詞。css

最近的複習中不斷出現'負margin','BFC','Stacking Contexts'等等概念,又在MDN上了解到CSS Visual Formatting Model,我以爲有必要系統地梳理一遍思路。html

本文以MDN Visual Formatting Model 這篇文章做爲引導,並加入本身的理解和新增的知識點,不會涉及太多的佈局真實場景,例如雙飛翼佈局,聖盃佈局,多列等高佈局等等,這裏只做爲基礎。web

CSS標準盒子模型(Box Model)

CSS標準盒子模型是呈現每個獨立元素的基礎,組合起來構成文檔流ide

如下是一個標準的盒子,由4個部分組成佈局

標準盒模型咱們設置width屬性的時候,width = content-width,怪異盒模型width = content-width + padding-width + border-widthpost

當咱們設置背景色或者背景圖片時,默認會延伸到border外圍,可是Z-Ordering的順序在border如下,也就是說,若是同時設置了backgroundborder屬性,border區域仍是顯示border,可是其Z-Order如下是background。咱們可使用background-clip屬性改變這一默認行爲,具體再也不敘述。學習

block元素和inline元素造成盒子的區別

<p>test</p>
<p>test</p>
<a href="">test</a>
<a href="">test</a><br />
<a href="">test</a>
<a href="">test</a>
複製代碼
p, a {
  width: 100px;
  height: 40px;
  border: 1px solid red;
  margin: 20px;
  padding: 30px;
}
複製代碼

圖一

圖二

仔細看上面兩幅圖,塊級和行級元素盒子有如下幾點不一樣flex

  1. 塊級元素盒子會自動佔滿一行,行級元素盒子不會
  2. 塊級元素能夠設置寬高,行級元素寬高設置無效
  3. 塊級元素之間上下margin有摺疊,行級元素設置上下margin無效,可是左右margin有效,並且不會摺疊
  4. 行級元素content部分出如今正常文檔流中,設置上下padding會與其餘元素髮生重合,左右padding不會

CSS容器盒子(Containing Block)

僅僅瞭解完每一個元素本身造成的盒子模型是不夠的,由於自身的盒子在某些時候還會受到容器盒子(Containing Block)的影響,例如ui

  1. 基於百分比的值來設定元素盒子的width,height,margin,padding,這裏須要注意,除了height是基於父元素盒子的height以外,其他三個屬性都是基於父元素盒子的width
  2. 當設置爲Absolute Positioning以後,基於百分比的值來設置盒子的偏移量,例如top,left等等,此時是基於父元素盒子border內側進行定位

關於容器盒子,咱們最常認爲它就是父元素的content部分,其實否則,有以下三種狀況spa

  1. 若是子元素position屬性爲默認值static或者是relative,則其Containing Block爲父元素的content部分
  2. 若是子元素position屬性爲absolute,則其Containing Block爲第一個position不爲static的父元素的content+padding部分,若是都是static,則爲Initial Containing Block

Initial Containing Block

The initial containing block has the dimensions of the viewport, and is also the block that contains the htmlelement. Simply put, the absolutely positioned element will be contained outside of the html element, and be positioned relative to the initial viewport.

簡而言之,就是視窗(Viewport)

  1. 若是子元素position屬性爲fixed,則其Containing Block爲視窗(Viewport)

知道標準盒子模型以及容器盒子模型後,須要用必定的規則將這些盒子「組裝」起來,如下咱們來看看「組裝」方式

正常流(Normal Flow)

一旦盒子模型建立成功,接下來即是將這些盒子按照必定規則組裝起來,默認的組裝規則就是Normal Flow

使用position: relative或者默認的position: static,且沒有設定浮動,此時元素就是在Normal Flow中

Normal Flow中,塊級元素盒子垂直方向一個挨着一個,行級元素盒子水平方向一個挨着一個,一行不夠以後換到下一行

若是使用position: relative,能夠將盒子基於原來的位置,經過設定top,bottom,left,right進行位移

浮動流(Floats)

設置一個元素爲浮動以後,這個元素盒子在一行中移動到最左邊或者最右邊,而且脫離Normal Flow

浮動元素對其後面的元素或者是其父元素都會產生影響,除非其後元素使用clear屬性清除這一影響,我以前寫過一篇從聖盃和雙飛翼看浮動流的文章,裏面詳細解釋了浮動流的過程。

絕對定位(Absolute Positioning)

在Floats中,雖然盒子脫離了文檔流,可是其對於以後的元素盒子或者是父元素盒子都會產生影響,除非影響被清除。可是在Absolute Positioning Scheme中,盒子能夠說徹底抽離Normal Flow,且再也不對其餘元素產生影響

此時元素盒子的位置徹底基於其容器盒子(Containing Block),主要兩種狀況

  1. 若是絕對定位使用position: absolute,容器盒子爲第一個position不爲static的父元素的content+padding部分
  2. 若是絕對定位使用position: fixed,容器盒子爲視窗(Viewport)

堆疊順序(Z-Ordering)

前面提到的都是元素應該出如今文檔中的位置,可是若是發生堆疊,那麼顯示順序(Z-Ordering, or Stacking Ordering)是怎麼樣的呢?

不使用z-index

HTML文檔默認顯示給咱們的是第0層,這也是z-index的默認值,爲0

  • bottom layer (farthest from the observer)
  • ...
  • Layer -3
  • Layer -2
  • Layer -1
  • Layer 0 (default rendering layer)
  • Layer 1
  • Layer 2
  • Layer 3
  • ...
  • top layer (closest to the observer)

正常的堆疊順序,遵循以下幾個規則(順序由底向上,若是發生重疊,只有最上的元素能被用戶看到)

  1. 根元素(<html>)的backgroundborder,在堆底
  2. 沒有被定位的元素(position: static),根據在HTML文檔出現順序,後面出現的在堆頂
  3. 被定位的元素(除了static),根據在HTML文檔出現順序,後面出現的在堆頂
  4. 若是使用display: flex,且對flex container下的子元素使用order屬性改變順序,也會影響到堆疊順序,order最高的元素出如今堆頂

依據上面的規則,咱們看一個例子

5個DIV在文檔的出現順序爲1,2,3,4,5

使用z-index

首先來看看z-index的定義

The z-index CSS property specifies the z-order of a positioned element and its descendants or flex items (children of an element with display: flex). When elements overlap, z-order determines which one covers the other. An element with a larger z-index generally covers an element with a lower one.

也就是說,z-index只能在flex-item或者positioned-items下設置纔有效,不然無效

再看一個例子(在上一個例子基礎上使用z-index

5個DIV在文檔的出現順序爲1,2,3,4,5

Stacking Context

其實咱們使用z-index,其實是造成了一個Stacking Context,不一樣的Stacking Context下的元素層疊順序互不影響,它們之間的層疊順序由上層元素之間的層疊順序決定

建立Stacking Context有以下幾種常見方法

  1. HTML文檔根元素<html>建立了默認的Stacking Context
  2. 設置了position: fixed或者absolute的元素,且z-index值不爲默認值auto
  3. 設置了display: flexcontainer下的子元素,且z-index值不爲默認值auto
  4. 設置opacity值小於1的元素

使用z-index改變堆疊順序

前面提到過,不一樣的Stacking Context下的元素層疊順序互不影響,它們之間的層疊順序由上層元素之間的層疊順序決定

z-index的initial(默認值)爲auto

auto

The box does not establish a new local stacking context. The stack level of the generated box in the current stacking context is the same as its parent's box.

也就是說,若是一個元素沒有建立Stacking Context,則其順序由父級Stacking Context決定,最頂層的Stacking Context爲根元素<html>建立,其z-index值爲0

仍是先看一個例子,下面分別是文檔結構和結果圖

Root

  • DIV #1
  • DIV #2
  • DIV #3
    • DIV #4
    • DIV #5
    • DIV #6

總結

到這裏Document Flow和CSS Formatting Model也就結束了,涵蓋了基本的佈局規則。只有深刻理解這些基礎,才能更好地去理解之前的一些佈局Trick,好比雙飛翼佈局,聖盃佈局等等,這些佈局創建在這些基礎上,若是理解了這些內容,本身實現一些佈局Trick也不是不可能,雖然如今Flex佈局已經能夠解決不少問題了

參考

  1. developer.mozilla.org/en-US/docs/…
  2. developer.mozilla.org/en-US/docs/…
相關文章
相關標籤/搜索