現在終於可以開始講 Grid 單元的事情了,雖然可以講的事情可能不多,絕大部分會圍繞在造成容器影響的地方,當然基本的東西還是會先帶一下。
只是說講完之後到底能不能滿 30 天呢 XD
Grid 單元
首先,他跟 Flexbox 一樣,如果內容單元是包含在 Grid 容器裡面的話,他本身是宣告成 Grid 格式的文本(Grid formatting context),他並不會因為你把他宣告成 Box Module 而轉換成你所知道的區塊元件(Block formatting context),在 Grid 容器內的元件,基本上都會被自動指定為 display: block
這樣的樣式。
同時,在一個 Grid 容器宣告為 display: grid
或 display: 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 單元定義尺寸的方式,是使用對齊樣式來做設定。對,講來講去都會回到對齊模組這件事情,我也是覺得很神奇。
基本上單元可以使用這些設定值來對齊,然後只有 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-items
與place-items
這三個。normal
跟auto
基本上一樣,但為何要分兩個?老實說我不知道。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>
使用了 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>
這個時候你會得到像是這樣的結果,
接下來,我們把 5 號的 Grid 容器指定一個 高度 的尺寸,
.grid-item:nth-of-type(5) {
height: 200px;
}
根據常識判斷,我們這個 3x3
的容器,應該會變成第二列的高度為 200px
,然後其他列的尺寸應該會保持原本的 fit-content
的相關尺寸,對吧?
對吧?
不對!
為什麼?
根據官方軌道尺寸演算法 Track Sizing Algorithm 當中的第四點,
當你的剩餘空間是無定義長度,換句話說以 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 單元尺寸超出軌道限制時,他就是很單純的 超過軌道 了,而不會發生任何奇怪的事情的原因。
底下我們用一個簡單的示意圖來解釋上面一堆關鍵字,
在這些關鍵字當中,self-*
的關鍵字主要會跟 writing mode 有關。由於我們平常所面對的系統都是 LTR(由左至右書寫),所以當你遇到 RTL 的文本時,你使用 start
跟 self-start
就會出現差異。
我們可以用 direction: rtl
來模擬這種狀況,
所以對於直式書寫的設定也是一樣的道理。這個就是對於 Grid 單元的對齊方式,這邊之所以不提 stretch
的原因是,他本身就是將整個 Grid 單元填滿軌道,所以既然已經 填滿軌道 了,那麼就沒有對齊的問題。
小記
只要記得一件事情,
除了
stretch
以外,Grid 軌道尺寸不等於 Grid 單元尺寸。
剩下的我們明天再繼續。
目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0