# 視覺格式化模型中的各種框
> 原作者:doyoe
原文鏈接:http://blog.doyoe.com/2015/03/09/css/%E8%A7%86%E8%A7%89%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%A8%A1%E5%9E%8B%E4%B8%AD%E7%9A%84%E5%90%84%E7%A7%8D%E6%A1%86/
在聊這個話題之前,我們可能得先簡單說說 `視覺格式化模型` 這個概念。
`視覺格式化模型` 的全稱是 `Visual formatting model`,它被用來描述用戶代理(比如瀏覽器)在圖形媒體下如何處理文檔樹。
在 `視覺格式化模型` 中,每個文檔樹的元素會根據[框模型](http://www.w3.org/TR/CSS2/box.html)產生零到多個框(boxes)。這些框的布局取決于框的尺寸,類型,定位方式(正常流,浮動和絕對定位),元素之間的關系和外部信息(例如:視口 ① 大小,置換元素的固有尺寸等等)。
>舉個最簡單的例子來講,假設一個頁面上有2個div,那么第2個div的位置會取決第1個div的高度定義;假設更復雜一點,第1個div是浮動的,那么第2個div的位置還要取決于第1個div的寬度。
## 不同類型的框
在 `CSS` 中,可能會產生不同類型的框,框的類型取決于 `display` 屬性的設定。某種程度上,框的類型會影響其在視覺格式化模型中的表現。接下來會詳細的聊聊這些不同類型的框以及它們在視覺可視化模型中的表現。
在說這個之前,我們先回憶一下,大家常說的一些名詞:
- Containing block(包含塊)
- Block-level element(塊級元素)
- Block element(塊元素)
- Block-level box(塊級框), Block container box(塊容器框), Block box(塊框)
- Inline-level element(行內級元素)
- Inline element(行內元素)
- Inline-level box(行內級框), Atomic inline-level box(原子行內級框), Inline box(行內框)
- Anonymous boxes(匿名框)
## 包含塊
首先,來看看什么是 `包含塊`?這個說及 `CSS` 一般的都會提及的基本概念。
一個元素,它的框的尺寸和位置會相對于一個特定的矩形框邊緣來計算而得到,這個特定的矩形框稱之為該元素的 `包含塊`。
(為)一個元素生成的框通常會充當其子框的包含塊;當我們叫一個 `框的包含塊` 時,其實表達的是 `該框所處的包含塊`,而不是其自身產生的包含塊。
每個框相對于其包含塊(`該框所處的包含塊`)都會被給予一個位置,不過該框并不局限在包含塊內,有可能會溢出,通常這個時候你會借助 `overflow` 屬性來進行處理。
除了說什么是包含塊,這里順帶再介紹一下生成包含塊的一些特殊場景:
- 由根元素生成的包含塊叫做 `初始包含塊(initial containing block)`。
- 對于其它元素,如果元素的 `position` 值是 `relative` 或者 `static`,其包含塊由最近的祖先 `塊容器框` 的內容邊界(如果想知道什么是內容邊界,可以先看看CSS盒模型)形成。
> 舉個例子,`td`, `th` 就算有父容器 `tr`,但它們的包含塊卻是由 table 生成,因為 `table` 是 塊容器框 而 `tr` 不是
- 絕對定位元素的包含塊由最近的定位(`position` 值非 `static`)祖先生成,如果不存在這樣的祖先,則采用初始包含塊;
- 固定定位元素(`position: fixed`)的包含塊一般情況下都由視口 ① 生成;
說了這幾個特殊的情景,你會發現并不是所有的包含塊都是由父元素所生成。
## 什么是塊級元素?
塊級元素是那些視覺上會被格式化成塊狀的元素,通俗一點來說就是那些會換新行的元素。`display` 屬性的:`block`, `list-item`, `table`, `flex`, `grid` 值都可以將一個元素設置成塊級元素。
>舉個例子來說,`li` 是一個 塊級元素,但也有人會說它是一個 `塊元素`。嗯,`li` 確實是一個塊級元素,但并不是一個塊元素,為什么?
## 什么是塊元素?
塊元素是 `display` 屬性值為 `block` 的元素,它應該是 `塊級元素` 的一個子集,而不是等同的,一個 `塊元素` 是一個 `塊級元素`,但一個 `塊級元素` 不一定是一個 `塊元素`,所以不要混淆。
## 塊級框,塊容器框,塊框
### 什么是塊級框?
塊級元素生成塊級框,這些框會參與某些 `BFC`。每個塊級元素都會生成一個主要的塊級框來包含其子框和生成的內容,同時任何定位方式都會與這個主要的塊級框有關。
某些塊級元素還會在主要的塊級框之外產生額外的框:例如 `list-item` 元素,它需要生成一個額外的框用于包含 `list-style-type`。這些額外的框會相對于主要的塊級框來進行排版。
### 什么是塊容器框?
一個 `塊容器框` 要么只包含 `塊級框`,要么創建一個 `IFC` 而只包含 `行內級框`,但不能同時包含 `塊級框` 和 `行內級框`。
除了 `table框` 和 `置換元素`,一個 `塊級框` 同時也是一個 `塊容器框`。`非置換的行內塊` 和 `單元格` 是 `塊容器` 但不是 `塊級框`。
并不是所有的 `塊級框` 都是 `塊容器框`,也并不是所有的 `塊容器框` 都是 `塊級框`。
`塊級框` 和 `塊容器框` 的另外一個重要的區別是:`塊級框` 需要能夠包含其生成的內容,但 `塊容器框` 并不需要。這是什么意思呢?簡單的解釋一下:
> 比如一個 `iframe` 其內容由 `src` 屬性所決定,這可以當成是生成的內容,所以 `iframe` 是一個 `塊級框` 但卻不是 `塊容器框`
### 什么是塊框?
簡要的來說,是 `塊容器` 的 `塊級框` 稱之為 `塊框`。
可以通過下面這個圖來快速的梳理清楚這3者之間的聯系:
(圖一)
說完了塊級框,接下來說說行內級框
## 什么是行內級元素?
行內級元素是那些不會為自身內容形成新的塊,而讓內容分布在多行中的元素。`display` 屬性的:`inline`, `inline-table`, `inline-block`, `inline-flex`, `inline-grid` 值都可以將一個元素設置成行內級元素。
## 什么是行內元素?
如同塊元素之于塊級元素的關系,行內元素僅僅是 `display` 屬性值為 `inline` 的元素,這里不再贅述。
## 行內級框,原子行內級框,行內框
行內級元素生成行內級框,而這些框會參與某個 `IFC`。一個 `行內框` 是 `行內級框`,且其內容參與了包含它的 `IFC`。一個 `display` 值是 `inline` 的非置換元素會生成一個行內框。那些不是行內框的行內級框(例如行內級置換元素、行內塊元素、行內表格元素)被稱為 `原子行內級框`,因為它們是以單一不透明框的形式來參與其 `IFC` 的。
細心的你會發現并沒有一個 `行內容器框` 與 `塊容器框` 相對應,但卻多了一個 `原子行內級框`。并且有趣的是`行內塊`(包括置換和非置換元素)是`原子行內級框`,而`非置換行內塊`卻同時還是`塊容器框`。
(圖二)
## 匿名框
`匿名框` 包括 `匿名塊框` 和 `匿名行內框`。
假設一個 `div` 中包含有一個 `p` 和一段純文本 `xxx`。由于 `塊容器框` 只允許要么包含 `塊級框`,要么包含 `行內級框`,所以,為了符合這種情況,`div` 會生成一個匿名的塊級框用于包裹 `xxx`,這個匿名框就叫做 `匿名塊框`。
我們拿 `W3C` 上的一個例子來加深對匿名塊框的印象:
```html
<div>
Some text
<p>More text</p>
</div>
```
(圖三)
與此同時,我們將上面的代碼稍微改一下,將 `p` 變成 `span`:
```html
<div>
Some text
<span>More text</span>
</div>
```
`div` 生成一個塊框,`More text` 由 `span` 生成一個行內框,由于 `Some text` 沒有與之相關的行級元素,將由 div 為其生成一個行內框用以包裹,這個框稱為匿名行內框。如圖四:
(圖四)
假設一個匿名框的類型可根據上下文來清晰界定,則 `匿名行內框` 和 `匿名塊框` 都可被簡稱為 `匿名框`。
匿名框的繼承屬性會從包含它的非匿名框那里繼承,非繼承屬性取其初始值。
## 附注:
- ① 用戶代理一般會向用戶提供一個載體(屏幕上的一個窗口或其它可視區域)用以訪問文檔,這個載體就叫做 `視口`。用戶代理可以在視口大小被調整時改變文檔的布局。如果視口小于渲染文檔的畫布區域,用戶代理應當提供一個滾動機制。每個畫布只能擁有一個視口,但用戶代理可以把文檔渲染至多個畫布上(即為相同文檔提供不同的視圖)。
## 說明:
- 最近的文章都是斷斷續續寫的,如讀者朋友發現存在描述錯誤地方請及時提醒。