CSS Grid 其實已經不是什麼很新的東西,只是最近看到有一個實驗性功能,被 Chrome 正式版本採用,覺得好像應該來寫一個筆記文來記錄一下。順便當作是小小的教學文章也可以,反正 CSS 對我來說也從來沒有真正熟悉過。
Grid 網格系統
CSS 自從 Level 3 之後,後續所有發展的項目,都有屬於自己的 Level。所以 CSS Grid 自然就會有自己的一套。正式的名稱叫做 CSS Grid Layout Module Level 1,實際上被提出的時間是在 2012 年左右,還是屬於 CSS Level 3 的新功能。等到他正式被 RC 的時間,已經來到 2017 年年底了。
何謂 網格系統?
如果說 display: flex
是屬於一個維度的方向操作,那麼 display: grid
就可以視為是兩個維度的方向操作。簡而言之,這個網格系統提供了一個 欄 與 列 的操作概念,當你的操作區塊被定義為網格時,其下的子區塊,就被賦予網格系統的操作特性。
你可以想像一下 Excel 的操作方式,就是有 欄 與 列 的那個樣子,然後,要對你的每一個儲存格做操作,而所謂的儲存格,其實就把他想成你的 DOM 元件,差不多是那個樣子。然後 Excel 所擁有的合併儲存格,基本上也是有差不多的作法。
偷偷工商一下,我都看 AMOS 的影片長大的 XD
本篇不會提到關於對齊的相關事宜,只會講容器跟項目之間的事情。如果需要研究對齊,網路上有超多文章可以參考。
Grid Garden 這是小遊戲,可以玩玩看!
格線佈局的基本概念
CSS Grid 网格布局教程
基本概念
在整個網格佈局的概念裡面,最外面的東西叫做容器( Grid Container ),然後這個容器裡面所包含的物件,叫做項目( Grid Item ),基本的組成大概會類似這樣,
紅色的區塊,是屬於 容器 的部分,而中間粉紅色的區塊,則是屬於 項目 的部分。而他的 HTML 的內容很簡單,就是定義出四個區塊而已。為了要讓畫面上比較容易區分,所以我在區塊中間加上了 10px
的間隔,容器本身也給出了 10px
的留白填色,方便辨識。
容器裡面的子項目,就會依照你所定義的規範,以欄列的方式來排列,
與 Excel 一樣,這種欄列排列方式,交叉的地方就可以當作一個單元格,如果你有 3 欄 4 列的容器,那麼你所擁有的項目單元格就會有 3 x 4 = 12
個項目單元。
既然有了單元格,那麼中間就會有所謂的 格線,以上述為例,3 欄 4 列的容器,會有多少格線呢?以水平方向來說,會有 3 + 1 = 4
條水平格線,垂直方向來說,會有 4 + 1 = 5
條垂直格線。
網格容器
容器( Grid Container )的定義很簡單,
display: grid;
/* OR */
display: inline-grid;
打完收工。這樣就是告訴瀏覽器,我是一個網格容器,然後我什麼事都沒做(所以你活該畫面顯示不對)。
特別注意,當一個 DOM 被定義為
grid
時,其內部的子節點會自動被定義為網格項目。而當被定義為網格項目時,他就是屬於網格層級的元件( grid-level boxes ),而並非原有的區塊層級元件( block-level boxes )。原本的行內層級,會被轉譯為網格層級,例如
display: inline-block
,display: table-cell
,會被轉譯為display: block
的網格層級元件。
而以下設定會失效:float
,vertical-align
,column-*
我們有了容器,就可以開始定義容器的內容該怎麼長。首先,基本定義上我們有兩個,
grid-template-columns
定義容器有多少欄grid-template-rows
定義容器有多少列
所以如果要做一個 3x4 的容器,那麼我們可以這樣寫,
#container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px 100px;
}
這樣我們就會有一個項目是 100x100
的 3x4 的容器了。這兩個屬性有多種寫法,目的是相同的,但是有幾種衍生的變化類型。首先,他們可以接受的數值,加上特別關鍵字,有七種可使用的方式,
- 任何非負值的單位表示法,例如
100px
,10%
,1rem
,與新的fr
單位。 - Flex 佈局單位
fr
。 minmax(min, max)
格線系統中,以最小尺寸min
定義項目尺寸,最大尺寸限制需小於等於max
設定的尺寸。max-content
格線系統中,取得項目最大的尺寸當作填充條件。min-content
格線系統中,取得項目最小的尺寸當作填充條件。auto
自動設定項目尺寸。
所以我們可以寫這種比較複雜的東西,
#container {
display: grid;
grid-template-columns: 1fr min-content minmax(100px, max-content);
grid-template-rows: 100px 100px 100px 100px;
}
請留意
min-content
,max-content
的參考值是容器內的項目。
另外,這邊還提供了一個函數 repeat()
,讓你可以不用重複寫一樣的設定,例如以現今流行的 12 欄位佈局來說,就這樣設定就好了,
#container {
display: grid;
grid-template-columns: repeat(12, 1fr);
}
而這個 repeat()
函數還有兩個特殊關鍵字,
repeat(auto-fill, 100px)
利用100px
的尺寸,自動填滿容器。repeat(auto-fit, 100px)
利用100px
的尺寸,僅設定所擁有的欄或是列。
這兩個關鍵字的差異可能不明顯,先上圖讓大家瞭解一下差異,
這樣大家看出差異了嗎?如果你的容器比較大,項目本身並無法填滿時,auto-fill
會幫你填滿(即便他是空的項目),而 auto-fit
則只做完你所擁有的項目而已。
接著還有一個比較特別的地方,就是你可以替你的欄或列命名,主要的寫法是這樣,
#container {
display: grid;
grid-template-columns: [c1] 100px [c2] 100px [c3] 100px;
grid-template-rows: [r1] 100px [r2] 100px 100px 100px;
}
前面用中刮號刮起來的地方,就是那個欄或列的名稱,這個名稱可以多值,所以可以寫成 [c1 column_one]
這樣也是可行的。
接著還有間距的設定,目前 gap
系列的參數,已經將 grid-
前綴拿掉了,所以目前只要寫 row-gap
, column-gap
與 gap
即可。
#container {
display: grid;
row-gap: 5px;
column-gap: 5px;
gap: 5px 5px;
}
column-gap
設定欄之間的間距尺寸。row-gap
設定列之間的間距尺寸。gap
統一設定欄與列之間的間距,規則是<row> <column>
。
接著是 grid-auto-flow
這個屬性,預設的網格排序方式,先欄後列的排序。所以,這個屬性可以改變排列的順序。
row
預設的排序方式,先欄後列。column
先排列,後排欄位。dense
自動填滿的關鍵字,他會依照排序方式,盡量填滿容器。
關於 dense
的寫法為,
#container {
grid-auto-flow: row dense;
/* OR */
grid-auto-flow: column dense;
}
最後,還有一個 grid-template-areas
的屬性,是用於定義每一個網格項目的名稱,而每個網格項目可以使用 grid-area
來指定名稱,便可以指定要放到那個位置。
#container {
grid-template-areas:
"head head"
"nav main"
"footer footer";
}
指定區域名稱,當不使用該區域時,可以使用點( . )來略過他。
#container {
grid-template-areas:
". head"
"nav main"
". footer";
}
網格項目
容器項目( Grid Item )的定義,就是網格系統內的區塊元件。簡單來說,雖然都是 display: block
,但是容器項目會有網格系統特有的屬性。
grid-column-start
定義欄位開始的格線的位置。grid-column-end
定義欄位結束的格線的位置。grid-row-start
定義列開始的格線的位置。grid-row-end
定義列結束的格線的位置。grid-column
整合grid-column-start
與grid-column-end
。grid-row
整合grid-row-start
與grid-row-end
。grid-area
指定項目要放到哪一個位置。
還記得最前面所提到的 格線 嗎?上述這些屬性就是依照格線,來定義你的網格項目要放在什麼位置。而 grid-area
比較特別,除了可以放在某個命名的位置上外,也可以使用格線的位置來指定。
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
使用方式,
.item {
grid-area: header;
/* OR */
grid-area: 1 / 1 / 3 / 1;
}
display: contents
其實這才是我要分享的東西,上面沒看到都沒關係(欸)。
一圖解千言:
當然他的問題還是很多,但我覺得,如果是應用在大範圍的結構上,還是多少有一點點好處。
More accessible markup with display: contents
Display: Contents Is Not a CSS Reset
Vanishing boxes with display contents
那麼實際上他到底做了什麼事情?簡單的來說,如果你使用 Flex 或是 Grid 容器,當你的項目當中的子元件,如果設定了 display: contents
,那麼,那一個子元件將會從原有的結構樹中移除,然後僅保留內容,如果你的父元件是 Flex 或是 Grid 容器,那麼這個元件就會變成容器項目的一員。
僅只是 內容 被留下,其他原有套用的背景什麼的會失效,所以被移出去這件事情是瀏覽器做的事,但是你的 DOM 還是沒變。但是,你使用 Chrome DevTool 去看該元素時,畫面不會幫你框出來(燦笑)。
實際操作可以參考這一篇文章:
但目前 CSS Grid Layout 還是在吵 subgrid,也就是子項目的網格系統。就先靜觀其變吧。
小結
醫院真不是寫文章的地方。