我先問一個問題,如果我有一個
3x3
的 Excel 方塊,請問我有幾條格線?
Grid 容器中的格線是整個排版定位中的靈魂,但,他沒有 Excel 那麼單純。
網格格線
Grid 容器的靈魂就是網格格線,我們在宣告一個 Grid 容器的時候,每個方塊就會產生相對應的格線,而這些格線當中還會有先前提及的隱性格線(implicit grid)的設計。
我們從 Grid 容器先看起,首先我們定義一個 3x3
的 Grid 容器,
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
}
當然,如果你很明確的定義你的容器,那麼他就會給你很明確的網格格線。如果你沒有、或缺少定義網格區塊的話,那麼就會落入隱性格線的設定裡面。我們這邊暫且先不談,後面會繼續提及隱性格線的相關事宜。
首先,既然格線有數字,那麼那些數字代表了什麼意思?
- 每個 Grid 單元的 開始 與 結束 的地方
- 依照格線的數字可以定義一個 Grid 單元的尺寸
- 可以使用負數
- 可以命名
- 命名格線請避開
-start
與-end
結尾
這麼一來,我如果要做一個 Grid 單元像是這樣,
.grid-item {
grid-row-start: 1;
grid-row-end: 3;
grid-column-start: 2;
grid-column-end: 4;
}
我還沒介紹網格單元的基礎,這邊請先當作你知道這件事情(欸)。
接著我們再回來看 Grid 容器設定,由於格線可以命名,所以我們可以這樣寫,
.grid-container {
display: grid;
grid-template-rows: [first] 1fr [second] 1fr [boo] 1fr [last];
grid-template-columns: repeat(3, [foo] 1fr [boo]);
}
由於我中間混合了 repeat()
的寫法,所以他會有一點點不一樣,
雖然說格線有了名字,但原本的數字還是可以使用的。既然有名字,那麼我們的 Grid 單元就能使用名字來決定要使用的區域,
.grid-item {
grid-row-start: second;
grid-row-end: last;
grid-column-start: foo 2;
grid-column-end: boo -1;
}
好的,關於 <命名格線> <數字>
的結構請先當作你知道這件事情(燦笑)。
由於我使用了 repeat()
的關係,所以你會發現同一條格線會有兩個名字,在格線的命名上這樣是合法的,也就是說一條格線可以有好幾種名字,如果你不覺得煩的話可以這樣做沒關係。
以下是奇怪的例子請不要亂用,
.grid-container {
display: grid;
grid-template-rows: [hello world please begin here] 1fr [second] 1fr [boo] 1fr [last];
grid-template-columns: repeat(3, [foo boo too] 1fr [xoo]);
}
隱性格線 The Implicit Grid
這件事情其實是一種輔助的設計,當你的 Grid 容器設定並沒有辦法滿足一些使用狀況的時候,Grid 的渲染引擎會加上這種隱性格線來當作輔助,目的是用以確保整個網格系統的完整性。簡單來說,就是當你的網格元件超出了容器設定時,就會產生出隱性網格軌道,然後網格軌道就會帶著網格格線出現。
官方的說明再貼一次,雖然不一定看得懂。
至於,哪些狀況會產生隱性網格軌道(隱性格線)呢?
合理的說法是,
- 網格單元超出了網格容器的設計
所謂的 超出 了網格容器的設計,代表的就是在設定上的資料量,超過了原有網格容器可容納的網格單元數量。就像是 3x3
的方格,你硬要放入 4x4
的資料的意思一樣。網格系統為了確保網格軌道的正確性,就會增加軌道來放入這些資料,這些 被增加 的軌道,就是所謂的隱性網格軌道(Implicit Grid Tracks),而這些軌道的產生就會帶來了隱性網格格線(Implicit Grid Line)。
但是,
實際上(現實面)的狀況是,
- Grid 容器設定不完整
- Grid 單元指定了不存在的網格格線(或網格單元)
- 使用命名 Grid 單元,卻不存在於 Grid 容器規範中
雖然說這些隱性格線目的是為了確保 Grid 容器的完整性,但是,我覺得更多的部分應該是要 防止人類隨意亂寫樣式造成的錯誤。我們可以來看看這些例子,
.grid-container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 100px);
}
<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-template-rows
的情況下,所有的列(row)的產生都算在隱性軌道頭上。
爾或者是指定了一個奇怪的網格軌道,逕而產生了隱性網格軌道,造成後面網格呈現出現不同的流向,
.grid-container {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(3, 100px);
}
.grid-item:first-child {
grid-column: 1 / 5;
grid-row: 1 / 2;
}
<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>
這種情況是最可怕的,由於隱性軌道跟一般網格軌道的功能是一樣的,所以,當你原本設想的網格容器 3x3
的區域,如果因為設定錯誤,他就會變成 4x3
的網格容器。是的,網格系統很貼心的幫你把你要的區域給畫出來了。
驚不驚喜,意不意外。
另外,在容器使用 grid-template-areas
的設定方式時,由於指定名稱與其網格單元名稱的配對不合(或沒有配對),也會產生隱性格線,而且,在更複雜的網格單元設定時,更容易會造成不必要的隱性格線產生。
這邊必須要先提及,grid-template-areas
有一個 附加特性,
所有命名區塊的位置,都會產生 4 條隱性命名格線,也就是欄(row)的方向兩條,列(column)的方向兩條。
我們先看範例,後面再來講解到底為什麼。這個時候,我們還是請一隻貓來切版,
.grid-container {
display: grid;
grid-template-areas:
"nav nav nav"
"sidebar main main"
"sidebar main main";
width: 500px;
height: 500px;
}
.nav {
grid-area: nav;
}
.sidebar {
grid-area: sidebarrrrr;
}
.main {
grid-area: main;
}
<div class="grid-container">
<nav class="grid-item nav">1</nav>
<aside class="grid-item sidebar">2</aside>
<main class="grid-item main">3</main>
</div>
最終的結果會是這樣,
命名網格最可怕的地方在於,你的整個 Grid 容器尺寸不變,然後會依照比例 均分 這些網格格線所劃分出來的區域,無論你是不是隱性網格格線都一視同仁。
之所以名字打錯會造成這個問題,主要還是命名網格單元設定的 附加特性 有關。以上述的例子來看,我們單純看欄(column)的部分就好,畢竟是正方形轉過去就可以通了。
首先是 nav nav nav
,在欄方向有兩條線,名稱分別是,
[nav-start]
,[nav-end]
在列方面也會有兩條線,名稱跟欄一樣。其他的區塊也分別都會有四組網格格線,如同我剛剛所描述的。那麼,我們再來看看那個不存在於命名區域設定的網格單元 sidebarrrrr
,
首先,他一樣會有四條線,
欄方向
[sidebarrrrr-start]
,[sidebarrrrr-end]
列方向[sidebarrrrr-start]
,[sidebarrrrr-end]
上面的計算結果,我們發現網格格線的計算數字被標記到了 6
,原本只有 4
而已,原因是,你一個隱性命名網格單元,每個方向都會產生 2
條線,然而,他並不會將 上一個 網格單元的最後一條線做合併的動作,換句話說,
無論是一般命名網格單元還是隱性命名單元,他的網格格線都是獨立的。
所以我們從欄方向來看 sidebarrr
這個單元,就很容易理解為何變成 6
。因為對於 nav nav nav
這個命名區塊來說,他的網格格線為 [nav-start]
, [nav-end]
,但由於隱性命名單元的發生,所以,單就 nav nav nav
這個區塊來看,他的網格格線就變成了,
[nav-start]
,[nav-end]
,[sidebarrrrr-start]
,[sidebarrrrr-end]
由於 [nav-start]
等同於 網格格線數字 1
,而 [nav-end]
為 4
,所以後面增加的隱性命名區塊網格格線,就變成了 5
跟 6
了。
當然,你不要打錯名字就好了。
另外,如果在命名 Grid 單元上,使用了錯誤的網格區域設定,雖然不一定會造成隱性網格區域的產生,但一樣會打壞原本網格單元擺放的邏輯。
使用命名格線與隱性格線的雷
前面有提到了格線可以取名字,但是不建議你的名字當中有 -start
或 -end
結尾。原因在於,在 Grid 格線系統中,他會使用 -start
與 -end
這兩個關鍵字來組合你的網格名稱,用以找到確切的網格格線位置。
又是一個小貼心,然後雷死你不償命的。
我們舉一個實際的例子來看,
.grid-container {
display: grid;
grid-template-columns: [first] 100px [foo foo-start] 100px [foo-end] 100px [last];
grid-template-rows: repeat(2, 100px);
}
.grid-item:first-child {
grid-column-start: first;
grid-column-end: foo;
grid-row: 1 / 2;
}
聰明如你,一定會覺得,阿不就是把第一個 .grid-item
放在第一格 100x100
的地方,這有什麼好困難的?
來,我們來看實際的結果,
驚不驚喜,意不意外。
我雷死你這小王八蛋!
這是網格格線一個不知道是不是因為隱性命名網格區域會用到 -start
, -end
的關係,索性將這兩個字也納入了一般命名網格格線的規則裡。為什麼?我上面之所以把 grid-column
分開來設定的原因,就恰巧可以解釋這件事情。
grid-column-start
如果使用命名格線,他會去找看看有沒有叫做<我格線>-start
的命名格線,如果找不到才會先去找格線名字相同的,例如<我格線>
。grid-column-end
跟上面的邏輯一樣邏輯同上,只是後面改成<我格線>-end
。grid-row-start
,grid-row-end
邏輯同上。grid-column
,grid-row
這兩個縮寫的邏輯也同上。
所以,我上面的例子是這樣命名的,
grid-template-columns: [first] 100px [foo foo-start] 100px [foo-end] 100px [last];
依據網格格線的規則,他會先去找 [foo-end]
,所以就會有上面圖片中的效果。
為什麼不是先找
foo
而是先找foo-end
?
w3c 官方說的,不爽你可以去他 Github 發 PR(欸
Line-based Placement
First attempt to match the grid area’s edge to a named grid area: if there is a grid line whose line name is <custom-ident>-start (for grid--start) / <custom-ident>-end (for grid--end), contributes the first such line to the grid item’s placement.
所以說,沒事請不要亂用 -start
或 -end
來命名你的格線,他會 優先配對。
另外,如果使用了不存在的格線名稱,那麼你的格線系統就會多一個尺寸為 0
的軌道,然後多一條線出來。跟剛才的命名單元一次多兩條不一樣。這個部分一樣會破壞 Grid 原本擺放單元的位置,舉例來說,
.grid-container {
display: grid;
grid-template-columns: [first] 100px [foo] 100px [boo] 100px [last];
grid-template-rows: repeat(3, 100px);
}
.grid-item:first-child {
grid-column-start: first;
grid-column-end: qoo;
grid-row: 1 / 2;
}
總括來看,其實我並沒有特別覺得網格系統很貼心,而這些真的只是為了維持網格展示能正常的一些補救(防呆防蠢不防雷)的作法。
對於不明就裡的人來說,被雷到應該只是剛好。
小結
其實講下來,應該跟格雷的陰影差不多。格線如果你都很規矩的操作,基本上不太容易出亂子。
目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0