在 CSS display 中使用多關鍵字語法

CSS display 模組為 CSS display 屬性定義了一種多關鍵字語法。本指南將解釋這種多關鍵字語法。

注意:多關鍵字語法也稱為“雙值語法”或“多值語法”。

當我們改變 display 屬性的值時會發生什麼?

我們學習 CSS 時最先了解的事情之一就是,有些元素是塊級元素,有些是行內元素。這些是它們的外部顯示型別。例如,<h1><p> 預設是塊級的,而 <span> 是行內的。使用 display 屬性,我們可以在塊級和行內之間切換。例如,要使標題變為行內,我們會使用以下 CSS:

css
h1 {
  display: inline;
}

display 屬性還允許我們在設定 display: griddisplay: flex 時使用 CSS 網格佈局Flexbox。需要理解的重要概念是,改變一個元素的 display 值可以改變其直接子元素的格式化上下文。當你使用 display: flexdisplay: grid 時,該元素的子元素會變成伸縮項或網格項,並響應網格和 Flexbox 規範中的屬性。

然而,網格和 Flexbox 展示的是,一個元素同時具有外部內部顯示型別。外部顯示型別描述了該元素是塊級還是行內級。內部顯示型別則描述了該盒子內子元素的行為方式。

舉個例子,當我們使用 display: flex 時,我們建立了一個塊級容器,其子元素是伸縮項。這些子元素被描述為參與伸縮格式化上下文。如果你拿一個 <span>——通常是行內級元素——並對其應用 display: flex,你就可以看到這一點。這個 <span> 變成了一個塊級元素。它在佈局中與其他盒子的關係表現得就像塊級元素一樣。這就像你對 span 應用了 display: block,但同時我們也得到了子元素行為的改變。

下面的即時示例中有一個應用了 display: flex<span>。它變成了一個塊級盒子,佔據了行內方向上所有可用的空間。你現在可以使用 justify-content: space-between 來將這些空間分配到兩個伸縮項之間。

html
<span class="flex"> Some text <em>emphasized text</em> </span>
css
body {
  font: 1.2em / 1.5 sans-serif;
}
.flex {
  border: 5px solid #cccccc;
  display: flex;
  justify-content: space-between;
}

建立行內伸縮容器也是可以的。如果你使用單值 inline-flex,你將得到一個行內級盒子,其子元素是伸縮項。這些子元素的行為方式與塊級容器的伸縮子元素相同。唯一改變的是父元素現在是一個行內級盒子。因此,它的行為就像其他行內級元素一樣,不會像塊級盒子那樣佔據整個寬度(或行內維度的尺寸)。這意味著一些後續的文字可以出現在伸縮容器的旁邊。

html
<div class="flex">
  <div>One</div>
  <div>Two</div>
</div>
Text following the flex container.
css
body {
  font: 1.2em / 1.5 sans-serif;
}
.flex > div {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.flex {
  border: 5px solid #cccccc;
  display: inline-flex;
}

在使用網格佈局時也是如此。使用 display: grid 會得到一個塊級盒子,它為其直接子元素建立一個網格格式化上下文。使用 display: inline-grid 會建立一個行內級盒子,它為其子元素建立一個網格格式化上下文。

使用多關鍵字語法

從上面的解釋可以看出,display 屬性具有相當大的能力。它不僅指示了某個元素在頁面上與其他盒子的關係是塊級還是行內級,還指示了它所應用的盒子內部的格式化上下文。為了更好地描述這種行為,display 屬性允許設定兩個值——一個外部值和一個內部值。原來的單值語法也仍然有效。

這意味著,我們不再使用 display: flex 來建立帶有伸縮子元素的塊級盒子,而是使用 display: block flex。不再使用 display: inline-flex 來建立帶有伸縮子元素的行內級盒子,而是使用 display: inline flex。下面的例子演示了這些值。

html
<h1>Multiple values for display</h1>

<div class="flex flex1">
  <div>Item One</div>
  <div>Item Two</div>
  <div>Item Three</div>
</div>

<p>The first example is a block element with flex children.</p>

<div class="flex flex2">
  <div>Item One</div>
  <div>Item Two</div>
  <div>Item Three</div>
</div>
The second example is an inline element with flex children.
css
body {
  font: 1.2em / 1.5 sans-serif;
}
.flex {
  border: 5px solid #cccccc;
  gap: 10px;
}

.flex > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.flex1 {
  display: block flex;
}

.flex2 {
  display: inline flex;
}

所有現有的 display 值都有對應的對映關係;下表列出了最常見的幾種。要檢視完整列表,請參閱 display 屬性規範中的表格。

單個值 多值
block block flow
flow-root block flow-root
inline inline flow
inline-block inline flow-root
flex block flex
inline-flex inline flex
grid block grid
inline-grid inline grid

display: block flow-root 和 display: inline flow-root

關於這種多值語法如何幫助澄清 CSS 佈局,我們可以看看上表中一些你可能不太熟悉的值。多關鍵字 display: block flow-root 對映到一個單值:display: flow-root。這個值的唯一目的就是建立一個新的塊格式化上下文(BFC)。BFC 確保你盒子裡的所有東西都留在裡面,而盒子外的東西不能侵入進來。

在下面的示例中,兩個 <p> 元素,其中一個在 <div> 內,演示了 display 值如何影響格式化上下文。為了讓我們能專注於後面的元素,第一個帶有演示控制元件的 <div> 元素被隱藏了。我們應該關注的元素是“parent”、“child”和“sibling”這幾個 <div><p> 元素,你可以透過它們的 ID 來區分。

這個佈局值得注意的是,父元素和子元素之間沒有內容,並且子元素應用了上外邊距。你可能期望上外邊距能有效地將子元素在父元素內部向下推,但實際發生的是一種叫做外邊距摺疊的現象。在這種情況下,子元素的外邊距延伸到了父元素邊界框的上方,並將父元素進一步向下推。如果你在瀏覽器的開發者工具中檢查子元素的盒模型,會更容易看到這一點。

更改 <select> 元素中選定的選項,以檢視不同 display 值的效果。你可以使用任何帶有 flow-root 的值來為父元素建立一個新的格式化上下文,使得子元素的外邊距相對於其父元素的外邊緣,從而避免外邊距摺疊。在 display: flow-rootdisplay: block flow-root 之間切換將達到與單值 flow-root 關鍵字相同的效果。

css
div,
p {
  outline: 2px solid black;
  background-color: cornflowerblue;
  display: block;
  margin-bottom: 2rem;
}

#parent {
  background-color: oldlace;
  min-height: 2rem;
}

#child {
  margin-top: 4rem;
  outline: 2px dashed red;
}

#sibling {
  background-color: lavender;
}
html
<div id="parent">
  <p id="child">The #child paragraph (nested in #parent).</p>
</div>
<p id="sibling">The #sibling paragraph (sibling of #parent).</p>

如果你考慮到塊級和行內佈局(有時稱為常規流),flow-root 這個值就說得通了。我們的 HTML 頁面會建立一個新的格式化上下文(浮動和外邊距不能超出邊界),並且我們的內容會以常規流的方式進行佈局,使用塊級和行內佈局,除非我們改變 display 的值來使用其他格式化上下文。建立一個網格或伸縮容器也會建立一個新的格式化上下文(分別是網格或伸縮格式化上下文)。它們也能包含內部的所有東西。然而,如果你想包含浮動和外邊距,但繼續使用塊級和行內佈局,你可以建立一個新的流根(flow root),並重新開始塊級和行內佈局。從那一點向下,所有東西都被包含在新的流根內。

這就是為什麼 display: flow-root 可以用多關鍵字語法 display: block flow-root 來書寫。你正在建立一個塊格式化上下文,它有一個塊級盒子,其子元素參與常規流。那麼與之配對的 display: inline flow-root 呢?這是目前描述 display: inline-block 的方式。

display: inline-block 這個值自 CSS 早期就存在了。我們傾向於使用它,是為了讓內邊距(padding)能將行內項推離一個元素,例如在建立導航項時,或者像下面的例子中那樣,想為一個行內元素新增帶內邊距的背景。

html
<p>
  This paragraph has a span <span class="inline-block">with padding</span> it is
  an inline-block so the padding is contained and pushes the other line boxes
  away.
</p>
css
body {
  font: 1.2em / 1.5 sans-serif;
}
p {
  border: 2px dashed;
  width: 300px;
}
.inline-block {
  background-color: rgb(0 0 0 / 0.4);
  color: white;
  padding: 10px;
  display: inline-block;
}

然而,一個帶有 display: inline-block 的元素也會包含浮動。它包含了這個行內級盒子內部的所有東西。因此,display: inline-block 的作用與 display: flow-root 完全相同,只是它是一個行內級盒子,而不是塊級盒子。雙值語法準確地描述了該值所發生的情況。在上面的例子中,你可以將 display: inline-block 改為 display: inline flow-root,並得到相同的結果。

display 的舊值怎麼辦?

display 的單值在規範中被描述為遺留值,目前使用多關鍵字版本並沒有任何好處,因為每個多關鍵字版本都有一個直接對映到遺留版本,如上表所示。

為了處理 display 的單值,規範解釋瞭如果只使用外部值 blockinline 該怎麼辦:

“如果指定了 <display-outside> 值但省略了 <display-inside>,則元素的內部顯示型別預設為 flow。”

這意味著其行為與單值世界中完全一樣。如果你指定 display: blockdisplay: inline,那會改變盒子的外部顯示值,但任何子元素都會繼續在常規流中。如果只指定了內部值 flexgridflow-root,那麼規範解釋說外部值應設定為 block

“如果指定了 <display-inside> 值但省略了 <display-outside>,則元素的外部顯示型別預設為 block——除了 ruby,它預設為 inline。”

最後,我們有一些遺留的預組合行內級值

  • inline-block
  • inline-table
  • inline-flex
  • inline-grid

如果支援的瀏覽器遇到這些單值,它會像處理多關鍵字版本一樣處理它們:

  • inline flow-root
  • inline table
  • inline flex
  • inline grid

所以所有當前的情況都得到了妥善處理,這意味著我們既能保持使用單值的現有和新網站的相容性,又允許規範繼續發展。