視覺格式化模型

在 CSS 中,視覺格式化模型(visual formatting model)描述了使用者代理如何獲取文件樹、進行處理並將其顯示在視覺媒體上。這包括了連續媒體(如計算機螢幕)和分頁媒體(如書籍或透過瀏覽器列印功能列印的文件)。大部分資訊對連續媒體和分頁媒體同樣適用。

在視覺格式化模型中,文件樹中的每個元素都會根據盒模型生成零個或多個盒。這些盒的佈局由以下因素決定:

  • 盒的尺寸和型別。
  • 定位方案(普通流、浮動和絕對定位)。
  • 文件樹中元素之間的關係。
  • 外部資訊(例如,視口大小、影像的固有尺寸等)。

關於視覺格式化模型的大部分資訊都在 CSS2 中定義,然而,各種 CSS 佈局模組對此資訊進行了擴充套件。在閱讀規範時,你會經常發現對 CSS2 中定義的模型的引用,因此,在閱讀其他佈局規範時,理解該模型及其在 CSS2 中使用的術語是很有價值的。

在本文中,我們將定義該模型並介紹一些相關的術語和概念,並連結到更具體的頁面以獲取更多詳細資訊。

視口的作用

在連續媒體中,視口(viewport)是瀏覽器視窗的可視區域。使用者代理可以在視口大小改變時更改頁面的佈局——例如,當你調整視窗大小或改變移動裝置的方向時。

如果視口小於文件的大小,那麼使用者代理需要提供一種滾動到文件未顯示部分的方法。我們最常見的是在塊級維度(block dimension)上滾動——在水平、從上到下的語言中是垂直滾動。然而,你也可能設計出需要在行內維度(inline dimension)上滾動的內容。

盒的生成

盒的生成是 CSS 視覺格式化模型的一部分,它根據文件的元素建立盒。生成的盒有不同的型別,這會影響它們的視覺格式化。生成的盒的型別取決於 CSS display 屬性的值。

最初在 CSS2 中定義的 display 屬性,在 CSS displayCSS 彈性盒佈局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 會生成多個盒(例如,一個主塊盒和一個子標記盒)。而一些值(如 nonecontents)會導致元素和/或其後代根本不生成任何盒。

匿名盒

當沒有 HTML 元素可用於生成盒時,就會建立一個匿名盒(anonymous box)。這種情況發生在,例如,當你在父元素上宣告 display: flex,而在其直接內部有一段未包含在其他元素中的文字。為了修復盒樹,會圍繞該文字段建立一個匿名盒。然後它會像一個伸縮項一樣表現,但是,它不能像常規盒那樣被選擇和設定樣式,因為沒有元素可以定位。

html
<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>
css
body {
  font: 1.2em sans-serif;
  margin: 20px;
}

.flex {
  display: flex;
}

.flex > * {
  background-color: rebeccapurple;
  color: white;
}

當文字段與塊級元素穿插時,也會發生同樣的事情。在下一個例子中,我在一個 <div> 中有一個字串;在我的字串中間是一個包含部分文字的 <p> 元素。

html
<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>
css
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> 後面的行盒被縮短以環繞浮動元素。盒的背景延伸到浮動元素的後面,因為浮動項已經脫離了文件流。

html
<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>
css
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),即 blockinline。這個外部顯示型別指的是盒在頁面上與其他元素並存時的行為方式。

盒也有一個內部顯示型別,決定了其子元素的行為方式。對於普通的塊級和行內佈局,或者說普通流,這個顯示型別是 flow。這意味著子元素也將是 blockinline

然而,內部顯示型別也可能是像 gridflex 這樣的值,在這種情況下,直接子元素將顯示為網格項或伸縮項。在這種情況下,該元素被描述為建立了一個網格或伸縮格式化上下文(formatting context)。在很多方面,這與塊格式化上下文相似,但其子元素表現為伸縮項或網格項,而不是普通流中的項。

塊級盒和行內級盒之間的互動在 display 屬性參考中有詳細描述。

此外,display 特定值的參考解釋了這些格式化上下文在盒佈局方面是如何工作的。

獨立的格式化上下文

元素要麼參與其包含塊的格式化上下文,要麼建立一個獨立的格式化上下文。例如,一個網格容器為其子元素建立一個新的網格格式化上下文

獨立的格式化上下文會包含浮動元素,並且外邊距不會跨越格式化上下文的邊界摺疊。因此,建立一個新的塊格式化上下文可以確保浮動和外邊距保留在盒內。要做到這一點,在你希望建立新塊格式化上下文的盒上新增 display: flow-root

下面的例子展示了 display: flow-root 的效果。帶有黑色背景的盒看起來包裹住了浮動項和文字。如果你移除 display: flow-root,浮動項會從盒的底部伸出,因為它不再被包含。

html
<div class="container">
  <div class="item">Floated</div>
  <p>Text following the float.</p>
</div>
css
.container {
  display: flow-root;
}

.item {
  margin: 10px;
  float: left;
}

塊盒

在規範中,塊盒(block boxes)、塊級盒(block-level boxes)和塊容器(block containers)在某些地方都被統稱為塊盒。這些東西有些許不同,只有在沒有歧義的情況下才應使用術語“塊盒”。

塊容器

一個塊容器(block container)要麼只包含參與行內格式化上下文的行內級盒,要麼只包含參與塊格式化上下文的塊級盒。因此,我們看到了上面解釋的行為,即引入匿名盒以確保所有項都能參與塊或行內格式化上下文。一個元素只有在它包含塊級或行內級盒時才是一個塊容器。

行內級盒和塊級盒

這些是包含在塊容器內,並分別參與行內或塊級佈局的盒。

塊盒

一個塊盒(block box)是一個既是塊級盒又是塊容器的盒。如 CSS display 中所述,一個盒可以是塊級盒,但不是塊容器(例如,它可能是一個伸縮或網格容器)。

另見