本文譯自A Complete Guide to CSS Grid(https://css-tricks.com/snippets/css/complete-guide-grid/#grid-animation)
CSS Grid 布局簡介
CSS Grid是一種二維的布局方式,和以前的布局方式完全不同,CSS常用來給頁面布局,但做的總不夠好,以前我們用table布局,後來用float、position、inline-block,這些方式本質上都是hack,缺少一些重要的特性(比如垂直居中),flex布局是一個很好的布局方式,但它基于軸的布局方式在其他方面用處更大,并且可以和Grid布局很好的配合使用,Grid布局是專門用來解決我們一直以來布局頁面時所碰到的各種問題的。
本指南圍繞Grid布局的最新特性講解,是以不會考慮老舊的浏覽器相容性。
CSS Grid基礎
到2017年3月,大部分浏覽器都支援Grid布局(無需字首):Chrome(包括Android)、Firefox、Safari(包括IOS)、Opera。IE10和IE11也能通過一定的途徑支援,是以現在是時候使用Grid布局了。
首先你需要一個父元素,并設定dispaly: grid,通過grid-template-columns和grid-template-rows設定列和行的大小,然後向父元素中添加子元素,并設定grid-column和grid-row屬性,和flex布局類似,子元素的原始順序并不重要,CSS可以任意控制它們的順序,這使得通過媒體查詢排列元素變得超級簡單。想象下你把整個頁面設定成grid布局,然後對不同的螢幕寬度自适應時完全重新排列,隻需要幾行CSS。Grid是有史以來引入的最強大的CSS子產品之一。
重要的CSS Grid術語
在深入學習Grid布局之前,先了解一些重要的術語,它們有些相似,如果不先了解,後面就比較容易混淆,不過不用擔心,這些概念并不多。
Grid Container
應用display: grid的元素,是所有grid items的直接父級。下面的例子中,container就是grid container。
<div class="container">
<div class="item item-1"> </div>
<div class="item item-2"> </div>
<div class="item item-3"> </div>
</div>
Grid Item
Grid Container的子元素,下面的例子中,item元素就是grid item,但sub-item不是。
<div class="container">
<div class="item"> </div>
<div class="item">
<p class="sub-item"> </p>
</div>
<div class="item"> </div>
</div>
Grid Line
Grid Line是用來構成網格布局的分隔線。它們可以是垂直的(“列網格線”)或水準的(“行網格線”),位于行或列的任意一側。這裡的黃線是列網格線的一個例子。
Grid Cell
Grid Cell是相鄰兩行和相鄰兩列之間的區域,稱為網格單元,下圖黃色部分是行網格線1和2,列網格線2和3之間的網格單元。
Grid Track
Grid Track是相鄰網格線之間的區域,你可以了解成一行或一列網格。下圖黃色部分是第2和第3行網格線之間的Grid Track。
Grid Area
Grid Area是由4條網格線圍起來的區域。一個Grid Area可以由若幹個網格單元組成,下圖黃色部分的是行網格線1和3,列網格線1和3圍起來的Grid Area。
CSS Grid屬性
CSS Grid屬性分為2類,分别是用在父元素和子元素上的。
用在父元素Grid Container上的屬性
- display
- grid-template-columns
- grid-template-rows
- grid-template-areas
- grid-template
- grid-column-gap
- grid-row-gap
- grid-gap
- justify-items
- align-items
- place-items
- justify-content
- align-content
- place-content
- grid-auto-columns
- grid-auto-rows
- grid-auto-flow
- grid
1、display
.container {
display: grid | inline-grid;
}
可取值:
- grid——生成塊級grid
- inline-grid——生成行内grid
2、grid-template-columns、grid-template-rows
用一行空格分開的值來定義網格的行和列,這些值代表Grid Track的大小,空格代表分隔線Grid Line。
- track-size —— 可以是一個長度、百分比、或者fr機關的值
- line-name —— 你對網格線的命名
.container {
grid-template-columns: ... ...;
/* e.g.
1fr 1fr
minmax(10px, 1fr) 3fr
repeat(5, 1fr)
50px auto 100px 1fr
*/
grid-template-rows: ... ...;
/* e.g.
min-content 1fr min-content
100px 1fr max-content
*/
}
網格線自動取正值,-1是最後一行的備選值。
但是你也可以直接給網格線命名,注意名稱時的括号文法:
.container {
grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px [end];
grid-template-rows: [row1-start] 25% [row1-end] 100px [third-line] auto [last-line];
}
需要注意的是分隔線可以有多個名字。比如下面第二條線有2個名字:row1-end 和 row2-start
.container {
grid-template-rows: [row1-start] 25% [row1-end row2-start] 25% [row2-end];
}
如果需要定義重複的部分,可以用repeat()來簡化:
.container {
grid-template-columns: repeat(3, 20px [col-start]);
}
上面的寫法和下面的等價:
.container {
grid-template-columns: 20px [col-start] 20px [col-start] 20px [col-start];
}
如果多條分割線使用同樣的名字,則可以通過名字加計數來區分。
.item {
grid-column-start: col-start 2;
}
fr機關可以把父元素空閑部分按比例劃分給Grid Track。比如下面的寫法會把Grid Track設為父元素寬度的三分之一。
.container {
grid-template-columns: 1fr 1fr 1fr;
}
空閑部分是非自适應元素計算完畢後剩下的空間。比如下面的例子中,對fr可用的空間不包括50px:
.container {
grid-template-columns: 1fr 50px 1fr 1fr;
}
3、grid-template-areas
grid-template-areas用名字定義網格區域,重複網格名字使該區域包括覆寫的網格單元,句号代表空網格單元。文法本身讓網格結構十分明了。
可取值:
- grid-template-areas —— 網格區域名字
- . —— 空的網格單元
- none —— 未定義網格區域
.container {
grid-template-areas:
"<grid-area-name> | . | none | ..."
"...";
}
舉個例子:
.item-a {
grid-area: header;
}
.item-b {
grid-area: main;
}
.item-c {
grid-area: sidebar;
}
.item-d {
grid-area: footer;
}
.container {
display: grid;
grid-template-columns: 50px 50px 50px 50px;
grid-template-rows: auto;
grid-template-areas:
"header header header header"
"main main . sidebar"
"footer footer footer footer";
}
上面的代碼會建立一個3行4列的網格區域,第一行是header區域,中間的一行由2塊main區域、1塊空單元和1塊sidebar區域組成,最後一行是footer區域。
每一行的網格單元數量要相同。可以用任意數量的緊挨着的句号來聲明一個空單元格。隻要句号之間沒有空格,它們就代表一個空單元格。
需要注意的是,grid-template-areas命名的是區域,當使用該屬性時,區域兩端的線會被自動命名。如果網格區域被命名成foo,該區域的第1行和第1列分隔線都會自動名稱成foo-start,最後1行和最後1列的分隔線被命名成foo-end,這也意味着一些分隔線會有多個名字,比如上面的例子中,有一條分隔線有3個名字:header-start, main-start, 和 footer-start。
4、grid-template
grid-template可以把grid-template-rows,grid-template-columns和 grid-template-areas簡寫到一起。
可取值:
- none —— 3個屬性都設為預設值。
- <grid-template-rows> / <grid-template-columns> —— 分别設定grid-template-rows和grid-template-columns的值,并把grid-template-areas設為none。
.container {
grid-template: none | <grid-template-rows> / <grid-template-columns>;
}
它的值還可以更複雜,但使用起來更友善:
.container {
grid-template:
[row1-start] "header header header" 25px [row1-end]
[row2-start] "footer footer footer" 25px [row2-end]
/ auto 50px auto;
}
上面的寫法和下面的等價:
.container {
grid-template-rows: [row1-start] 25px [row1-end row2-start] 25px [row2-end];
grid-template-columns: auto 50px auto;
grid-template-areas:
"header header header"
"footer footer footer";
}
由于grid-template不會重置隐式網格屬性(grid-auto-columns, grid-auto-rows和grid-auto-flow),這可能是您在大多數情況下想要做的,是以建議使用grid屬性而不是grid-template。
5、column-gap、row-gap、grid-column-gap、grid-row-gap
定義分隔線的粗細。可以了解成設定行或列的間距。
- <line-size> —— 長度值
.container {
/* 新寫法 */
column-gap: <line-size>;
row-gap: <line-size>;
/* 舊寫法 */
grid-column-gap: <line-size>;
grid-row-gap: <line-size>;
}
舉個例子:
.container {
grid-template-columns: 100px 50px 100px;
grid-template-rows: 80px auto 80px;
column-gap: 10px;
row-gap: 15px;
}
間距隻在行或列中間存在,邊緣部分不存在。
需要注意的是,grid字首會被移除,grid-column-gap 和 grid-row-gap 會被重命名成 column-gap 和 row-gap,無字首的文法已經被Chrome 68+, Safari 11.2 Release 50+, and Opera 54+支援。
6、gap、grid-gap
row-gap 和 column-gap的簡寫形式。
可取值:
- <grid-row-gap> <grid-column-gap> —— 長度值
.container {
/* 新寫法 */
gap: <grid-row-gap> <grid-column-gap>;
/* 舊寫法 */
grid-gap: <grid-row-gap> <grid-column-gap>;
}
舉個例子:
.container {
grid-template-columns: 100px 50px 100px;
grid-template-rows: 80px auto 80px;
gap: 15px 10px;
}
如果沒有定義row-gap的值,它會自動等于column-gap。
7、justify-items
grid items的水準對齊方式,适用container中所有的grid items。
可取值:
- start —— 向單元格的起始邊對齊。
- end —— 向單元格的尾部對齊。
- center —— 單元格内居中。
- stretch —— 寬度填滿單元格(這是預設值)。
.container {
justify-items: start | end | center | stretch;
}
舉個例子:
.container {
justify-items: start;
}
.container {
justify-items: end;
}
.container {
justify-items: center;
}
.container {
justify-items: stretch;
}
該對齊方式也可以用justify-self單獨給grid items設定。
8、align-items
grid items的垂直對齊方式,值和用法和justify-items一樣。該對齊方式也可以用align-self單獨給grid items設定。
9、place-items
align-items 和 justify-items的簡寫方式。
可取值:
- <align-items> / <justify-items> —— 第1格值代表align-items,第2個值代表justify-items,如果第2個值省略了,第1個值會預設代表這2個屬性。
.center {
display: grid;
place-items: center;
}
10、justify-content
有時整體網格的大小比容器小,比如所有的網格都是用px指定了大小,這時可以設定網格在容器中的對齊方式,justify-content設定網格的水準對齊方式。
可取值:
- start —— 向容器的頭部對齊。
- end —— 向容器的尾部對齊。
- center —— 在容器中水準居中。
- stretch —— 重新适配items的大小把容器的寬度填滿。
- space-around —— 使每個網格的列間距相等,兩邊的留白是列間距的一半。
- space-between —— 使每個網格的列間距相等,兩邊不留白。
- space-evenly —— 使每個網格的列間距相等,兩邊的留白等于列間距。
.container {
justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
}
舉個例子:
.container {
justify-content: start;
}
.container {
justify-content: end;
}
.container {
justify-content: center;
}
.container {
justify-content: stretch;
}
.container {
justify-content: space-around;
}
.container {
justify-content: space-between;
}
.container {
justify-content: space-evenly;
}
11、align-content
值和用法和justify-content一樣,差別是align-content用來設定垂直方向的對齊方式。
12、place-content
justify-content和align-content的簡寫方式。
可取值:
- <align-content> / <justify-content>
13、grid-auto-columns、grid-auto-rows
指定任何自動生成的grid tracks的大小。
用法:
.container {
grid-auto-columns: <track-size> ...;
grid-auto-rows: <track-size> ...;
}
舉個例子,來看看隐式grid tracks是怎麼建立的。
.container {
grid-template-columns: 60px 60px;
grid-template-rows: 90px 90px;
}
上面的代碼建立了一個2x2的網格。
接下來用grid-column和grid-row設定item的位置:
.item-a {
grid-column: 1 / 2;
grid-row: 2 / 3;
}
.item-b {
grid-column: 5 / 6;
grid-row: 2 / 3;
}
這時item-b就超出了2x2的網格範圍,超出部分會預設建立隐式的grid tracks,但是這些隐式的grid tracks的寬度是0,這時我們就可以通過grid-auto-columns和grid-auto-rows為它們設定寬度:
.container {
grid-auto-columns: 60px;
}
14、grid-auto-flow
grid-auto-flow控制item的自動排列方式。
可取值:
- row —— 按順序填充每一行,必要時新增行。
- column —— 按順序填充每一列,必要時新增列。
- dense —— 較小的item排在前面。
.container {
grid-auto-flow: row | column | row dense | column dense;
}
注意dense隻是虛拟的改變items的順序,這可能導緻順序混亂。
舉個例子:
<section class="container">
<div class="item-a">item-a</div>
<div class="item-b">item-b</div>
<div class="item-c">item-c</div>
<div class="item-d">item-d</div>
<div class="item-e">item-e</div>
</section>
接着你定義了一個2x5的網格,并把grid-auto-flow設為row(row也是預設值):
.container {
display: grid;
grid-template-columns: 60px 60px 60px 60px 60px;
grid-template-rows: 30px 30px;
grid-auto-flow: row;
}
在給item定位的時候,你隻設定了2個:
.item-a {
grid-column: 1;
grid-row: 1 / 3;
}
.item-e {
grid-column: 5;
grid-row: 1 / 3;
}
這時我們的網格看起來就是這樣的:
如果我們把grid-auto-flow設為column,item-b, item-c 和 item-d 就會按順序沿着列來排:
.container {
display: grid;
grid-template-columns: 60px 60px 60px 60px 60px;
grid-template-rows: 30px 30px;
grid-auto-flow: column;
}
15、grid
可以把 grid-template-rows, grid-template-columns, grid-template-areas, grid-auto-rows, grid-auto-columns, 和 grid-auto-flow 寫到一起。
可取值:
- none —— 所有屬性設為預設值。
- <grid-template> —— 跟 grid-template 一樣。
- <grid-template-rows> / [ auto-flow && dense? ] <grid-auto-columns>?
- [ auto-flow && dense? ] <grid-auto-rows>? / <grid-template-columns>
比如下面兩種寫法等價(<grid-template-rows> / <grid-auto-columns>):
.container {
grid: 100px 300px / 3fr 1fr;
}
.container {
grid-template-rows: 100px 300px;
grid-template-columns: 3fr 1fr;
}
下面的兩種寫法等價(auto-flow <grid-auto-rows> / <grid-template-columns>):
.container {
grid: auto-flow / 200px 1fr;
}
.container {
grid-auto-flow: row;
grid-template-columns: 200px 1fr;
}
下面的兩種寫法等價(auto-flow dense <grid-auto-rows> / <grid-template-columns>):
.container {
grid: auto-flow dense 100px / 1fr 2fr;
}
.container {
grid-auto-flow: row dense;
grid-auto-rows: 100px;
grid-template-columns: 1fr 2fr;
}
下面的兩種寫法等價(<grid-template-rows> / auto-flow <grid-auto-columns>):
.container {
grid: 100px 300px / auto-flow 200px;
}
.container {
grid-template-rows: 100px 300px;
grid-auto-flow: column;
grid-auto-columns: 200px;
}
還有更複雜但更簡潔的寫法,下面的兩種寫法等價:
.container {
grid: [row1-start] "header header header" 1fr [row1-end]
[row2-start] "footer footer footer" 25px [row2-end]
/ auto 50px auto;
}
.container {
grid-template-areas:
"header header header"
"footer footer footer";
grid-template-rows: [row1-start] 1fr [row1-end row2-start] 25px [row2-end];
grid-template-columns: auto 50px auto;
}
用在子元素Grid Item上的屬性
- grid-column-start
- grid-column-end
- grid-row-start
- grid-row-end
- grid-column
- grid-row
- grid-area
- justify-self
- align-self
- place-self
1、grid-column-start、grid-column-end、grid-row-start、grid-row-end
通過指定分隔線的方式定義網格單元在網格中的位置, grid-column-start/grid-row-start是網格單元開始的地方,grid-column-end/grid-row-end是結束的地方。
可取值:
- <line> —— 代表分隔線的數字或者名字。
- span <number> —— 包括指定數量的網格單元。
- span <name> —— 包括單元格直到碰到指定名字的分隔線。
- auto —— 自動放置,預設1個單元格。
舉個例子:
.item-a {
grid-column-start: 2;
grid-column-end: five;
grid-row-start: row1-start;
grid-row-end: 3;
}
.item-b {
grid-column-start: 1;
grid-column-end: span col4-start;
grid-row-start: 2;
grid-row-end: span 2;
}
如果沒有指定grid-column-end/grid-row-end,item預設包含1個單元。
item還能彼此覆寫,可以用z-index控制層級。
2、grid-column、grid-row
grid-column-start、grid-column-end、grid-row-start、grid-row-end的簡寫形式。
可取值:
- <start-line> / <end-line> —— 寫法和非簡寫值一樣。
.item {
grid-column: <start-line> / <end-line> | <start-line> / span <value>;
grid-row: <start-line> / <end-line> | <start-line> / span <value>;
}
舉個例子:
.item-c {
grid-column: 3 / span 2;
grid-row: third-line / 4;
}
3、grid-area
grid-area給item命名,這樣使用grid-template-areas可以直接引用item的名字。并且此屬性還可以作為 grid-row-start+grid-column-start+grid-row-end+grid-column-end的簡寫形式。
可取值:
- <name> —— item的名字。
- <row-start> / <column-start> / <row-end> / <column-end> —— 代表分隔線的數字或名字。
.item {
grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
}
舉個例子:
給item命名:
.item-d {
grid-area: header;
}
簡寫 grid-row-start+grid-column-start+grid-row-end+grid-column-end:
.item-d {
grid-area: 1 / col4-start / last-line / 6;
}
4、justify-self
設定item在單元格内的水準對齊方式。
.item {
justify-self: start | end | center | stretch;
}
舉個例子:
.item-a {
justify-self: start;
}
.item-a {
justify-self: end;
}
.item-a {
justify-self: center;
}
.item-a {
justify-self: stretch;
}
5、align-self
值和用法和justify-self一樣,差別是align-self用來設定垂直方向的對齊方式。
6、place-self
place-self是 align-self 和 justify-self的簡寫形式。
可取值:
- auto —— 預設對齊模式。
- <align-self> / <justify-self> —— 第1個值是align-self,第2個值是justify-self,如果隻有一個值,這個值會指派給align-self和justify-self。
特殊的機關和函數
機關fr
你可能會在grid布局中使用很多分數機關,比如fr,來代表剩餘空間的一部分,就像這樣用,表示25%和75%:
grid-template-columns: 1fr 3fr;
這種寫法比%更可靠,比如你增加了列數,就會破壞百分比的寬度,但是分數機關fr可以和其他機關更好的組合使用:
grid-template-columns: 50px min-content 1fr;
大小關鍵字
在設定行或列的大小時,可以用各種機關,比如px、rem、%等等,還可以用一些關鍵字:
- min-content: 内容的最小寬度。比如一行文字E pluribus unum,會占用的最小寬度是單詞pluribus的寬度。
- max-content: 内容的最大寬度。比如一行文字E pluribus unum,能占用的最大寬度就是一整行句子。
- auto: 和fr類似,但優先級低于fr。
- fit-content: 使用可用空間,但不小于min-content,不大于max-content。
- fractional units: 分數機關fr。
大小函數
- minmax() —— 設定大小範圍,minmax(最小值,最大值)。
- min()
- max()
repeat()函數和關鍵字
repeat()函數可以使寫法更簡潔:
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
/* 簡潔的寫法: */
grid-template-columns: repeat(8, 1fr);
/* 下面的情況更新凸顯簡潔: */
grid-template-columns: repeat(8, minmax(10px, 1fr));
repeat()和關鍵字結合時,會更加奇特:
- auto-fill: 在一行中放入盡可能多的列,即使它們是空的。
- auto-fit: 把所有列都放進去,增加列來填充空間即使是空的列。
這就有了CSS網格中最著名的寫法,也是有史以來最偉大的CSS技巧之一:
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
masonry
著名的瀑布流布局:
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: masonry;
}
詳情可以看這篇文章native-css-masonry-layout-css-grid(https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/)。
subgrid
grid-template-columns: subgrid;目前還隻有 Firefox 支援,不做詳細說明,有興趣的可以看原文。