[CSS] Flex/Grid Layout Modules, part 11

<img> 是可替代元件,單元尺寸以自然尺寸為主

現在終於可以開始講 Grid 單元的事情了,雖然可以講的事情可能不多,絕大部分會圍繞在造成容器影響的地方,當然基本的東西還是會先帶一下。

只是說講完之後到底能不能滿 30 天呢 XD


Grid 單元

首先,他跟 Flexbox 一樣,如果內容單元是包含在 Grid 容器裡面的話,他本身是宣告成 Grid 格式的文本(Grid formatting context),他並不會因為你把他宣告成 Box Module 而轉換成你所知道的區塊元件(Block formatting context),在 Grid 容器內的元件,基本上都會被自動指定為 display: block 這樣的樣式。

同時,在一個 Grid 容器宣告為 display: griddisplay: inline-grid 當下,其內容所包含的元件會全部被區塊化(blockification),也就是現在說的 Grid 單元。

有趣的地方在於,一個合法的 Grid 容器(其內容包含 Grid 單元),在改變區塊樣式時(例如:display: none),其 Grid 單元產生(DOMTree 渲染)的區塊化還是會發生,說的比較白話一點就是,他還是會把 Grid 單元區塊化動作完成之後,外層的 Grid 容器才會做 display: none 的動作。

但是,

如果 Grid 單元設定為 display:contents 則不在此列。關於這個設定我在整個 Grid 單元講完之後再來講這個特別的東西。其實我以前有約略講過,有興趣的人可以自己去看看 [CSS] 關於 Grid Layout 的使用姿勢

另外,在相連的 Grid 單元如果宣告為非區塊元件時,由於已經區塊化了,所以基本上他還是會屬於區塊元件,他會阻斷匿名區塊元件的產生(anonymous block box)。舉例來說,如果把 Grid 單元使用 display: table-cell 的設定,那麼他們最終會被轉換成 display: block,並不會建立成匿名表格單元(anonymous table box)。

關於匿名區塊這邊就不多說了,有興趣的我看最後有沒有時間再來聊聊。

有興趣可以看這裡 2.4. Layout-Internal Display Types: the table-* and ruby-* keywords


尺寸

Grid 單元有三種尺寸的面向,

尺寸規則 說明
normal 如果 Grid 是被替換的元件(replaced element),那麼這個 Grid 單元將會依據自然尺寸(natural size)來顯示。若該單元有定義尺寸,則使用定義尺寸,若都沒有,則使用 stretch 當作 Grid 單元尺寸。
stretch 預設當作 Grid 單元尺寸,會填滿整個 Grid 格線系統可使用空間。但,若同軸向有其他 Grid 單元指定了尺寸,會破壞原本 Grid 填滿的比例。
其他 使用 fit-content 當作預設尺寸。

何謂 自然尺寸?舉例來說,你這個單元是 <img> 元件的話,這個元件尺寸將會被定義為 src 屬性所指定的圖片尺寸。詳細的說明可以看看 w3c 的敘述 CSS Images Module Level 3, natural size

Grid 單元定義尺寸的方式,是使用對齊樣式來做設定。對,講來講去都會回到對齊模組這件事情,我也是覺得很神奇。

Self-Alignment: Aligning the Box Within Its Parent

基本上單元可以使用這些設定值來對齊,然後只有 stretch 跟尺寸有關,

樣式 可用值 預設值
justify-self auto, normal, stretch, <baseline-position> <overflow-position>?, [ <self-position>, left, right ] auto
align-self auto, normal, stretch, <baseline-position>, <overflow-position>? <self-position> auto
place-self <'align-self'> <'justify-self'>? auto

說在前面的,關於 auto, normal, stretch 基本上在沒有任何設定的情況,也不是被替換元件的話,基本上沒有差異。以下稍微說明一下各種設定數值的作法,

  • auto 基本上跟著 Grid 容器的設定,對應的數值就是 justify-items, align-itemsplace-items 這三個。
  • normalauto 基本上一樣,但為何要分兩個?老實說我不知道。
  • stretch 依照 Grid 網格格線比例填滿,但 margin 任何一個軸方向都不可為 auto,且還是會受到 min-width, min-height, max-width, max-heigth 限制。
  • <baseline-position> 可以使用 baseline, first baseline, last baseline 三種關鍵字。
  • <overflow-position> 可以使用 safe, unsafe 關鍵字。
  • <self-position> 可以使用 center, start, end, self-start, self-end 關鍵字。

可替換元件的尺寸

圖片就是一個很常見的可替換元件,

.grid-containter {
    display: grid;
    
    grid-template: repeat(3, 300px) / repeat(3, 300px);
    
    gap: 10px;
}

.grid-item:first-child {
    // 不做任何設定
}
<div class="grid-container">

    <img class="grid-item" src="..." alt="我是圖片">
    
    <div class="grid-item">2</div>
    <div class="grid-item">3</div>

    <div class="grid-item">4</div>
    <div class="grid-item">5</div>
    <div class="grid-item">6</div>

    <div class="grid-item">7</div>
    <div class="grid-item">8</div>
    <div class="grid-item">9</div>

</div>

最終你會得到這個畫面,

<img> 是可替代元件,單元尺寸以自然尺寸為主

當你把 <img> 設定尺寸後,基本上他就會變成 有定義尺寸 的區塊元件。但是呢,區塊單元的尺寸跟你所定義的軌道尺寸基本上是兩件事情,所以無論他是自然尺寸,還是有定義尺寸,該超出軌道的地方一樣都會超出去。

換句話說,除非上述的例子,你的 <img> 使用了 width: 100% 這種相對尺寸,這個時候才會填滿整個欄軌道(寬度的部分),但是,因為自然尺寸比例的關係,這種時候就會換列軌道超出範圍了。

所以,並不太建議將 Grid 單元使用可替換元件,尺寸的問題你可能會一直處於無解的狀況。


無尺寸限制方向的魔性

一般來說無尺寸設定在目前的裝置上,是指 row 方向,也就是 grid-template-rows 的樣式設定。為何說他有魔性呢?我們舉個例子來說,首先,我們先給來一個空的 Grid 容器,

.grid-containter {
    display: grid;
    
    grid-template: repeat(3, 1fr) / repeat(3, 1fr);
    
    gap: 10px;
}
<div class="grid-container">

    <div class="grid-item">1</div>
    <div class="grid-item">2</div>
    <div class="grid-item">3</div>

    <div class="grid-item">4</div>
    <div class="grid-item">5</div>
    <div class="grid-item">6</div>

    <div class="grid-item">7</div>
    <div class="grid-item">8</div>
    <div class="grid-item">9</div>

</div>

這個時候你會得到像是這樣的結果,

無指定任何 Grid 單元尺寸的容器

接下來,我們把 5 號的 Grid 容器指定一個 高度 的尺寸,

.grid-item:nth-of-type(5) {
    height: 200px;
}

根據常識判斷,我們這個 3x3 的容器,應該會變成第二列的高度為 200px,然後其他列的尺寸應該會保持原本的 fit-content 的相關尺寸,對吧?

對吧?

不對!

當有任何一個 Grid 單元指定了高度尺寸後,所有列尺寸都變成了 200px

為什麼?

根據官方軌道尺寸演算法 Track Sizing Algorithm 當中的第四點,

Expand Flexible Tracks

當你的剩餘空間是無定義長度,換句話說以 row 的角度來看,他可以是 無限大 的情況下,必須根據以下法則來計算尺寸,

  • 根據彈性係數 fr 來均分所有的軌道尺寸。
  • 根據每個 Grid 單元所貢獻的最大尺寸(max-content),來計算填充空間,用以當作 fr 的尺寸。

所以,我們把 5 號的 Grid 容器指定一個 高度200px 的話,那麼 Grid 單元當中的最大貢獻尺寸就是 200px,所以對於 grid-template-rows 來說,他的 max-content 就會被當作 1fr 來使用。


對齊的基本操作

我們先撇除那些對於會超出 Grid 軌道尺寸的東西,來講講 Grid 單元對齊這件事情。雖然我還是覺得,把 stretch 放在對齊模組裡面實在是很奇怪,但好多年來都這樣了好像也不能怎麼樣(笑)。

先決條件,請不要把 margin 的任何方向設定為 auto

首先,我們從空的容器來看,

.grid-containter {
    display: grid;
    
    grid-template: repeat(3, 400px) / repeat(3, 400px);
    
    gap: 10px;
}
<div class="grid-container">

    <div class="grid-item">1</div>
    <div class="grid-item">2</div>
    <div class="grid-item">3</div>

    <div class="grid-item">4</div>
    <div class="grid-item">5</div>
    <div class="grid-item">6</div>

    <div class="grid-item">7</div>
    <div class="grid-item">8</div>
    <div class="grid-item">9</div>

</div>

接著我們一樣把第 5 號的 Grid 單元作一些設定,

.grid-item:nth-of-type(5) {
    align-self: end;
    justify-self: end;
}

那麼我們就會得到這樣的結果,

Grid 單元的對齊設定

你會發現我指定了 Grid 單元的尺寸,然後再把他做一個對齊的動作。這個時候聰明的你應該就會發現,其實 Grid 單元跟整個 Grid 軌道根本沒有什麼太大的關係。

是的,Grid 軌道是軌道,Grid 單元是單元,沒有直接的關係。

這也就是為什麼 Grid 單元尺寸超出軌道限制時,他就是很單純的 超過軌道 了,而不會發生任何奇怪的事情的原因。

底下我們用一個簡單的示意圖來解釋上面一堆關鍵字,

關於單元對齊的關鍵字

在這些關鍵字當中,self-* 的關鍵字主要會跟 writing mode 有關。由於我們平常所面對的系統都是 LTR(由左至右書寫),所以當你遇到 RTL 的文本時,你使用 startself-start 就會出現差異。

我們可以用 direction: rtl 來模擬這種狀況,

當你的本文需要特殊流向時,需要使用 self-* 的關鍵字

所以對於直式書寫的設定也是一樣的道理。這個就是對於 Grid 單元的對齊方式,這邊之所以不提 stretch 的原因是,他本身就是將整個 Grid 單元填滿軌道,所以既然已經 填滿軌道 了,那麼就沒有對齊的問題。


小記

只要記得一件事情,

除了 stretch 以外,Grid 軌道尺寸不等於 Grid 單元尺寸。

剩下的我們明天再繼續。


目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0


鐵人賽同步放送:
https://ithelp.ithome.com.tw/articles/10264321

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei