我們繼續來深入關於 Grid 容器的相關樣式設定。雖然目前 CSS 框架在多數情況下並不需要特別在意,但,就老話一句,誰在意誰痛苦。
踩到雷而且你還不知道為什麼
新的單位 fr
從 Grid 出現之後,我們有一個新的尺寸單位可以使用,他叫做彈性長度(flexible length),在 CSS 當中,因應這個彈性長度,所以就有了新單位 fr
,而 fr
的全名為 彈性軌道(flexible tracks),或者你會聽到有人稱他為 彈性係數(flexible factor)。
這個單位的定義與使用狀況在這邊稍微給大家解釋一下,
- 分配 剩餘空間
- 計算方式
<fr 係數> * <剩餘空間> / <所有 fr 係數總和>
fr
會因為內容計算而產生不同寬度minmax()
可以用於計算fr
calc()
不能使用fr
與其他單位數值混合運算fr
在介於0
與1
之間的小數時有其特殊計算方式
首先,我們來定義一下何謂 剩餘空間?
好的,所以我們的容器如果是一個固定尺寸的空間,我們舉個簡單的數字來方便大家裡解,
.grid-container {
display: grid;
grid-template-rows: repeat(4, 1fr);
grid-template-columns: repeat(4, 1fr);
width: 1000px;
height: 1000px;
}
這樣我們會獲得一個 1000x1000
尺寸的容器,然後裡面有 4x4
總共 16 個 Grid 單元空間。由於上述的公式,我們經過簡單的四則運算,
1 * 1000px / 4 = 250px
所以我們可以很明確的知道,我們所畫出來的 Grid 單元空間有 250px
寬,然後 250px
高。在這個地方很容易理解,但,我們暫時 不考慮 Grid 單元內容會造成的影響。這個在後面提到尺寸的時候會再詳細描述。
另外,關於介於 0
與 1
之間的 fr
設定,由於這邊解釋起來相當繞口,所以我先舉幾個例子,然後大家先看看圖,後面我再來解釋到底發生了什麼事情。
首先,我們一樣使用固定尺寸空間,然後稍微換一下裡面的東西,
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr) 0.5fr;
grid-template-columns: repeat(3, 1fr) 0.5fr;
width: 1000px;
height: 1000px;
}
這樣,由於分配的 剩餘空間 還是使用 1000px
整份去切割,所以基本上這個還是符合我們剛剛所說的剩餘空間分配公式,
關於
1fr
的部分是:
1 * 1000px / 3.5 = 285.714px
關於
0.5fr
的部分是:
0.5 * 1000px / 3.5 = 142.857px
接著,我們來看看搭配了非彈性空間單位的情況,
.grid-container {
display: grid;
grid-template-rows: repeat(3, 150px) 0.5fr;
grid-template-columns: repeat(3, 150px) 0.5fr;
width: 500px;
height: 500px;
}
根據上述的公式,我們可以簡單計算 剩餘空間 如下,
500px - 150px * 3 = 50px
所以這邊我們還可以預期的到,我們的 0.5fr
是使用剩餘空間來分配,
0.5 * 50 = 25px
接著,當我們出現了第二個小數點的 fr
單位時,
.grid-container {
display: grid;
grid-template-rows: repeat(2, 150px) 0.5fr 0.5fr;
grid-template-columns: repeat(2, 150px) 0.5fr 0.5fr;
width: 500px;
height: 500px;
}
當我們天真的以為,兩個 0.5fr
就會幫我把剩餘空間各半分配。但是,實際上並不會這樣運作,你會看到 Grid 容器幫你把剩餘空間全部分配完畢了。這邊的計算方式很類似 Flexbox 的 flex-grow
在介於 0
與 1
的計算方法,但稍微有點不同。
他的運作方式是,
當
fr
介於0
與1
之間時,軌道尺寸計算會使用非 100% 的剩餘空間做計算。其計算的方式,是先假定內容最大尺寸(max-content
)來當作彈性係數,此時會產生一個假想的1fr
寬度來做運算,最後再將空間分配給所設定的彈性係數,最後會得到一個 最終分配係數。
如果把他想成數學的話會比較簡單,
<剩餘空間> / <fr 單位數量> * <fr 係數> = <最終分配 fr 係數>
所以我們剛剛的設定最後會得到什麼 Grid 單位空間呢?
200px / 2 * .5 = 1fr
所以,最後剩餘空間會變成兩個 1fr
去分配。而不是你所想像的,有兩個 Grid 單位空間,然後各佔一半的事情發生。
為什麼?
fr
尺寸這樣的設計,是為了在無指定容器尺寸的情況下,避免分配狀況出現問題,進而搭配max-content
與假想1fr
(hypothetical 1fr size)的方式來均分剩餘空間。重點在於確保彈性尺寸在 Grid 軌道上有確切尺寸,且能還能依照所設定的比例去分配空間。
再來一個比較討厭的例子,
.grid-container {
display: grid;
grid-template-rows: repeat(2, 150px) 0.25fr 0.5fr;
width: 100%;
}
你可能會以為他跟剛剛一樣會把空間填滿?並不會喔!你會獲得一個 沒有分配的剩餘空間,然後基本上他雖然屬於 Grid 容器,但他 永遠不會被使用到。
因為所有介於 0
與 1
之間的彈性係數設定,都僅會拿剩餘空間來做重新分配,且不會填滿(也就是非 100%)剩餘空間的計算方式,來重新分配,所以出現沒有分配的剩餘空間是很有可能發生的事情。
所以,當你在使用
fr
的時候,如果不確定你在幹嘛,請不要使用介於0
與1
之間的設定。
最後,來提及內文影響 Grid 單元的部分,假設我們設定了一個很簡單的 Grid 容器,
.grid-container {
display: grid;
grid-template-rows: repeat(4, 1fr);
}
我們有一個很美好的 Grid 容器,然後我們請一隻貓來輸入內容,
<div class="grid-container">
<div class="grid-item">
我的第一個 Grid 內文
</div>
<div class="grid-item">
我的第一個 Grid 內文
</div>
<div class="grid-item">
我的第一個 GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGrid 內文
</div>
<div class="grid-item">
我的第一個 Grid 內文
</div>
</div>
說好的1fr
會均分呢?誰跟你說好,格線系統並沒有跟你說一定會均分。
這個問題起源在這裡,
翻成白話的意思就是,對於 1fr
實際上的預設定義是 minmax(auto, 1fr)
,所以這就很顯而易見,當那個 auto
生效的時候,就會取 max-content
來當作一個尺寸。這也就是為何你的 1fr
不會均分的原因。
所以,後來就出現了 minmax(0, 1fr)
的寫法,我不知道這算不算一種 Workaround,但是,Workaround 雖然可恥,但是相當有用!
.grid-container {
display: grid;
grid-template-rows: repeat(4, minmax(0, 1fr));
}
請注意,當你使用 minmax(0, 1fr)
的時候,你的內文會被切割,請自行使用換行相關的樣式,將你的內文做適當的換行。當然,這個問題會發生在兩個軸方向上,換句話說,當你在列(row)相關的地方使用 fr
均分時,也會發生一樣的問題。
但,由於我們的文件流向大部分是由上到下,所以比較少特別限制 高度 的部分,但,這還是取決於你的使用目的,只是在此提醒一下兩個軸方向皆會發生上述的所有事情。
容器對齊與定位
上一篇沒有特別提及的對齊,這邊會連同定位再次讓大家裡解一下關於 Grid 容器對齊的事情。首先關於對齊,我們可以分成兩個面向來看,
- 所有 Grid 單元集合
- 每一個 Grid 單元
- 僅單一 Grid 單元
樣式 | 適用對象 |
---|---|
justify-content , align-content , palce-content |
所有 Grid 單元集合 |
justify-items , align-items , place-items |
每一個 Grid 單元 |
align-self , justify-self , place-content |
僅單一 Grid 單元 |
上圖中綠色的框線代表了 所有 Grid 單元集合,換句話說,當你的容器有剩餘空間時,你所設定的 justify-content
與 align-content
才會發生效果。這個情況在 Grid 單元所使用的 justify-self
與 align-self
也是一樣的道理。
也就是說,當你的 Grid 單元尺寸設定,相較於整個 Grid 容器所規劃的尺寸,有產生 剩餘空間 時,才會發生效果。
另外,這邊必須要特別提及可以使用 stretch
的系列樣式,包含了,
justify-items
justify-self
align-items
align-self
當你的 Grid 單元並無指定特定尺寸時(或有產生剩餘空間時),這個 stretch
才會生效,請特別留意。
另外,在 Grid 容器整個系統內,有一個特別的設計,這個部分跟定位有關。在前幾天我們聊到 Flexbox 對於定位會打破 Flex 流向的狀況比較不同,具體上可以分為這兩種,
- 指定網格單元容器(Container block)的絕對定位元件
- 直接相對於 Grid 容器的絕對定位元件
第二點的部分跟 Flex 雷同,他會跳脫整個 Grid 生態系,但是,在不指定位置的情況下,還是會跟著整個生態係的設定走,舉例來說,
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
position: relative;
}
.grid-item:first-child {
position: absolute;
}
而,如果你的 Grid 單元也有相關設定,如 justify-self
, align-self
時,在沒有指定定位點的情況下,也是會跟著 Grid 生態係的設定呈現,
接著是比較特別的網格單元容器,當你的網格單元有指定一個區域時,這個定位的效果就會發生一些變化。何謂 指定一個區域 呢?我們舉個簡單的例子來看,
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
position: relative;
}
.grid-item:first-child {
grid-row: 2 / 4;
grid-column: 2 / 4;
position: absolute;
top: 50px;
left: 50px;
}
在這個時候,我們將 Grid 單元指定了所使用的 Grid 容器內的範圍,如果還不熟悉樣式寫法的人先不用緊張,我在此簡單解釋一下,
- 指定列的範圍從網格格線 2 開始,直到網格格線 4 結束
- 指定欄的範圍從網格格線 2 開始,直到網格格線 4 結束
接著我們定義了定位點的上邊與左邊,各設定了 50px
的距離,最終,我們會得到這樣的結果,
上圖的紅色框線部分,就是我們所指定的區塊範圍,
grid-row: 2 / 4;
grid-column: 2 / 4;
這一點是比較特別的,當你在 Grid 單元中使用絕對定位(absolute
)時,他會依照不同的情況而發生不一樣的效果。
position: fixed
無此效果,請注意!
小結
關於尺寸與定位今天就先聊到這邊,明天我們繼續來聊 Grid 生態系中的格線系統。
目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0