視覺格式化模型
在 CSS 中,視覺格式化模型(visual formatting model)描述了使用者代理如何獲取文件樹、進行處理並將其顯示在視覺媒體上。這包括了連續媒體(如計算機螢幕)和分頁媒體(如書籍或透過瀏覽器列印功能列印的文件)。大部分資訊對連續媒體和分頁媒體同樣適用。
在視覺格式化模型中,文件樹中的每個元素都會根據盒模型生成零個或多個盒。這些盒的佈局由以下因素決定:
- 盒的尺寸和型別。
- 定位方案(普通流、浮動和絕對定位)。
- 文件樹中元素之間的關係。
- 外部資訊(例如,視口大小、影像的固有尺寸等)。
關於視覺格式化模型的大部分資訊都在 CSS2 中定義,然而,各種 CSS 佈局模組對此資訊進行了擴充套件。在閱讀規範時,你會經常發現對 CSS2 中定義的模型的引用,因此,在閱讀其他佈局規範時,理解該模型及其在 CSS2 中使用的術語是很有價值的。
在本文中,我們將定義該模型並介紹一些相關的術語和概念,並連結到更具體的頁面以獲取更多詳細資訊。
視口的作用
在連續媒體中,視口(viewport)是瀏覽器視窗的可視區域。使用者代理可以在視口大小改變時更改頁面的佈局——例如,當你調整視窗大小或改變移動裝置的方向時。
如果視口小於文件的大小,那麼使用者代理需要提供一種滾動到文件未顯示部分的方法。我們最常見的是在塊級維度(block dimension)上滾動——在水平、從上到下的語言中是垂直滾動。然而,你也可能設計出需要在行內維度(inline dimension)上滾動的內容。
盒的生成
盒的生成是 CSS 視覺格式化模型的一部分,它根據文件的元素建立盒。生成的盒有不同的型別,這會影響它們的視覺格式化。生成的盒的型別取決於 CSS display 屬性的值。
最初在 CSS2 中定義的 display 屬性,在 CSS display、CSS 彈性盒佈局、CSS 網格佈局和 CSS ruby 佈局模組中得到了擴充套件。此外,自 CSS2 以來,圍繞 display 的一些術語也得到了更新和澄清。
CSS 會獲取你的源文件並將其渲染到畫布上。為此,它會生成一箇中間結構,即盒樹(box tree),它表示渲染文件的格式化結構。盒樹中的每個盒代表其對應的元素(或偽元素)在畫布上的空間和/或時間,而盒樹中的每個文字運行同樣代表其對應的文字節點的內容。
然後,對於每個元素,CSS 會根據該元素的 display 屬性值生成零個或多個盒。
備註: 盒通常以其顯示型別來稱呼——例如,由一個 display: block 元素生成的盒被稱為“塊盒”(block box)或簡稱“塊”(block)。但請注意,塊盒(block boxes)、塊級盒(block-level boxes)和塊容器(block containers)都有細微的差別;更多詳情請參閱下方的塊盒部分。
主盒
當一個元素生成一個或多個盒時,其中一個是主盒(principal box),它在盒樹中包含了其後代盒和生成的內容,並且也是參與任何定位方案的盒。
一些元素除了主盒外還可能生成額外的盒,例如 display: list-item 會生成多個盒(例如,一個主塊盒和一個子標記盒)。而一些值(如 none 或 contents)會導致元素和/或其後代根本不生成任何盒。
匿名盒
當沒有 HTML 元素可用於生成盒時,就會建立一個匿名盒(anonymous box)。這種情況發生在,例如,當你在父元素上宣告 display: flex,而在其直接內部有一段未包含在其他元素中的文字。為了修復盒樹,會圍繞該文字段建立一個匿名盒。然後它會像一個伸縮項一樣表現,但是,它不能像常規盒那樣被選擇和設定樣式,因為沒有元素可以定位。
<div class="flex">
I am wrapped in an anonymous box
<p>I am in the paragraph</p>
I am wrapped in an anonymous box.
</div>
body {
font: 1.2em sans-serif;
margin: 20px;
}
.flex {
display: flex;
}
.flex > * {
background-color: rebeccapurple;
color: white;
}
當文字段與塊級元素穿插時,也會發生同樣的事情。在下一個例子中,我在一個 <div> 中有一個字串;在我的字串中間是一個包含部分文字的 <p> 元素。
<div class="example">
I am wrapped in an anonymous box
<p>I am in the paragraph</p>
I am wrapped in an anonymous box.
</div>
body {
font: 1.2em sans-serif;
margin: 20px;
}
.example > * {
background-color: rebeccapurple;
color: white;
}
該字串在盒樹中被分割成三個盒。段落元素之前的字串部分被包裹在一個匿名盒中,然後是 <p>,它生成一個盒,接著是另一個匿名盒。
關於這些匿名盒需要考慮的一點是,它們會從其直接父級繼承樣式,但你無法透過定位匿名盒來改變它們的外觀。在我的例子中,我使用直接子選擇器來定位容器的子元素。這不會改變匿名盒,因為它們本身不是“元素”。
當一個字串被一個行內元素分割時,會建立行內匿名盒(inline anonymous boxes),例如,一個句子中包含一個用 <em></em> 包裹的部分。這會將句子分割成三個行內盒——在強調部分之前的一個匿名行內盒,被 <em> 元素包裹的部分,然後是最後一個匿名行內盒。與匿名塊盒一樣,這些匿名行內盒無法像 <em> 那樣被獨立設定樣式;它們只是繼承其容器的樣式。
其他格式化上下文也會建立匿名盒。網格佈局的行為與上面的 flexbox 示例相同,將文字字串透過匿名盒轉變為網格項。多列布局會在列周圍建立匿名列盒;這些也無法被設定樣式或以其他方式定位。表格佈局會新增匿名盒來建立正確的表格結構——例如,如果沒有 display: table-row 的盒,它會新增一個匿名錶格行。
行盒
行盒(Line boxes)是包裹每行文字的盒。如果你浮動一個專案,然後緊跟一個帶有背景色的塊,你就可以看到行盒和其包含塊之間的區別。
在下面的例子中,跟在浮動的 <div> 後面的行盒被縮短以環繞浮動元素。盒的背景延伸到浮動元素的後面,因為浮動項已經脫離了文件流。
<div class="float"></div>
<p class="following">
This text is following the float, the line boxes are shortened to make room
for the float but the box of the element still takes position in normal flow.
</p>
body {
font: 1.2em sans-serif;
margin: 20px;
}
.float {
float: left;
width: 150px;
height: 150px;
background-color: rebeccapurple;
margin: 20px;
}
.following {
background-color: #cccccc;
}
定位方案以及流內和流外元素
在 CSS 中,一個盒可以根據三種定位方案進行佈局——普通流(normal flow)、浮動(floats)或絕對定位(absolute positioning)。
普通流
在 CSS 中,普通流包括塊盒的塊級格式化、行內盒的行級格式化,也包括塊級和行級盒的相對定位和粘性定位。
閱讀更多關於 CSS 中流式佈局的內容。
浮動
在浮動模型中,一個盒首先根據普通流進行佈局,然後脫離文件流並進行定位,通常是向左或向右。內容可以沿著浮動元素的一側流動。
瞭解更多關於浮動的資訊。
絕對定位
在絕對定位模型中(也包括 fixed 定位),一個盒完全從普通流中移除,並相對於一個包含塊(對於固定定位,是視口)或 CSS 錨點定位中的一個或多個錨點元素來指定位置。
如果一個元素是浮動的、絕對定位的,或者是根元素,那麼它被稱為脫離文件流(out of flow)。如果一個元素不脫離文件流,那麼它被稱為在文件流內(in-flow)。
閱讀關於 CSS 定位佈局的內容。
格式化上下文和 display 屬性
盒可以被描述為具有一個外部顯示型別(outer display type),即 block 或 inline。這個外部顯示型別指的是盒在頁面上與其他元素並存時的行為方式。
盒也有一個內部顯示型別,決定了其子元素的行為方式。對於普通的塊級和行內佈局,或者說普通流,這個顯示型別是 flow。這意味著子元素也將是 block 或 inline。
然而,內部顯示型別也可能是像 grid 或 flex 這樣的值,在這種情況下,直接子元素將顯示為網格項或伸縮項。在這種情況下,該元素被描述為建立了一個網格或伸縮格式化上下文(formatting context)。在很多方面,這與塊格式化上下文相似,但其子元素表現為伸縮項或網格項,而不是普通流中的項。
塊級盒和行內級盒之間的互動在 display 屬性參考中有詳細描述。
此外,display 特定值的參考解釋了這些格式化上下文在盒佈局方面是如何工作的。
- CSS 網格佈局模組
- CSS 彈性盒子佈局模組
- CSS 多列布局模組
- CSS 表格模組
- CSS 列表和計數器模組
獨立的格式化上下文
元素要麼參與其包含塊的格式化上下文,要麼建立一個獨立的格式化上下文。例如,一個網格容器為其子元素建立一個新的網格格式化上下文。
獨立的格式化上下文會包含浮動元素,並且外邊距不會跨越格式化上下文的邊界摺疊。因此,建立一個新的塊格式化上下文可以確保浮動和外邊距保留在盒內。要做到這一點,在你希望建立新塊格式化上下文的盒上新增 display: flow-root。
下面的例子展示了 display: flow-root 的效果。帶有黑色背景的盒看起來包裹住了浮動項和文字。如果你移除 display: flow-root,浮動項會從盒的底部伸出,因為它不再被包含。
<div class="container">
<div class="item">Floated</div>
<p>Text following the float.</p>
</div>
.container {
display: flow-root;
}
.item {
margin: 10px;
float: left;
}
塊盒
在規範中,塊盒(block boxes)、塊級盒(block-level boxes)和塊容器(block containers)在某些地方都被統稱為塊盒。這些東西有些許不同,只有在沒有歧義的情況下才應使用術語“塊盒”。
塊容器
一個塊容器(block container)要麼只包含參與行內格式化上下文的行內級盒,要麼只包含參與塊格式化上下文的塊級盒。因此,我們看到了上面解釋的行為,即引入匿名盒以確保所有項都能參與塊或行內格式化上下文。一個元素只有在它包含塊級或行內級盒時才是一個塊容器。
行內級盒和塊級盒
這些是包含在塊容器內,並分別參與行內或塊級佈局的盒。
塊盒
一個塊盒(block box)是一個既是塊級盒又是塊容器的盒。如 CSS display 中所述,一個盒可以是塊級盒,但不是塊容器(例如,它可能是一個伸縮或網格容器)。