本文譯自《A Complete Guide to Grid》。由於譯者水平有限,如有錯誤,請不吝指正。文中所有圖的版權歸原作者所有。
導語:CSS網格佈局是目前CSS中可用的最強大的佈局系統。它是二維繫統,即可以處理行和列,不像flexbox只是一維繫統。我們通過爲父元素(變爲grid container)和子元素(變爲grid items)應用CSS規則來使用Grid佈局。
一、介紹
CSS網格佈局(亦稱Grid)是一種基於網格的二維佈局系統,主要致力於改變我們在頁面中基於網格設計頁面的方式。我們通常使用CSS佈局網頁,但有時並不好用。一開始我們使用table,之後用浮動、定位和行內塊級元素,但是所有這些方法都需要兼容,並且未實現一些重要功能(如垂直居中)。彈性盒子可以提供幫助,但它主要面向一維佈局,不能處理複雜的兩維佈局(彈性盒子和網格佈局其實配合得不錯)。只要我們寫網頁,我們不得不考慮那些佈局問題的hack實現,而網格佈局是首個特地爲解決這種問題而創造出來的模塊。
我寫這篇教程主要是因爲受到兩件事的影響。一個是Rachel Andrew的那本了不起的書——Get Ready for CSS Grid Layout。這本書清晰透徹的介紹了Grid,也是本文的基礎。我非常提倡你能買本讀一下。另一個是Chris Coyier的A Complete Guide to Flexbox,它指引我使用flexbox來寫佈局。這篇文章幫助了很多人,從在google中搜索‘flexbox’該文排在第一位就能看出。你會看到兩篇文章有很多相似之處,因爲爲什麼我不借鑑好的東西中呢?
我寫這篇教程的目的是介紹目前存在的最新版本中Grid的概念。我不會覆蓋過時的IE語法,而且會隨着版本的成熟定期更新教程。
二、基礎和瀏覽器支持
首先我們要使用display:grid
定義一個網格佈局容器,用grid-template-columns
和grid-template-rows
設置列和行的尺寸,然後按grid-column
和grid-row
把子元素放進容器中。同flexbox類似,網格項的順序並不重要,可以按任意順序放置它們。這也使得使用媒體查詢調整網格非常簡單。想像一下,定義整個頁面的佈局,然後只需幾行CSS代碼就能將其重排爲適應另一個尺寸的屏幕。Grid真是有史以來引入的最強大的CSS模塊。
截至2017年三月,Chrome(包括安卓端),Firefox、Safari(包括iOS端)和Opera在內的很多瀏覽器都支持原生的無前綴的CSS網格佈局。IE10和11只部分支持,但使用的是過時的語法。Edge也已經宣告支持。
#桌面端
Chrome | Opera | Firefox | IE | Edge | Safari |
---|---|---|---|---|---|
57 | 44 | 52 | 11* | 16 | 10.1 |
#移動端、平板
iOS Safari | Opera Mobile | Opera Mini | Android | Android Chrome | Android Firefox |
---|---|---|---|---|---|
10.3 | No | No | 56 | 61 | 55 |
除了微軟,瀏覽器廠商都在嚴格規範Grid直到規範成熟。這是一件好事,因爲意味着我們不必學習多種語法。
在生產環境中使用Grid只是時間問題。是時候學它了。
三、重要術語
在我們正式學習Grid之前先了解一下其術語。由於這裏的概念很類似,所以如果不先記住Grid規範定義的含義的話很容易弄混淆。但不用擔心,術語並不是很多。
Grid Container
定義了display: grid
的元素就成了網格容器。它是網格項的直接父元素。在下面的例子中,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
網格項是網格容器的子元素(如直接後代)。這裏item元素是grid items,但是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
網格線是組成網格結構的分割線。它們要麼是垂直的(column grid lines),要麼是水平的(row grid lines),來分割行或列。下面是一個列網格線的例子。
Grid Track
網格軌跡是兩個相鄰的網格線之間的空間。你可以把它想成網格的列或行。下面是一個在第二條和第三條行網格線的網格軌跡的例子。
Grid Cell
網格單元是由兩個相鄰的行網格線或列網格線圍成的區域。它是網格的一個獨立單元。下面是一個處於行網格線1、2和列網格線2、3之間的網格單元的例子。
Grid Area
網格區域是四條網格線圍成的區域。網格區域可能由任意數量的網格單元構成。下面是一個處於行網格線1、3和列網格線1、3之間的網格區域的例子。
四、網格佈局屬性表
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
- justify-content
- align-content
- grid-auto-columns
- grid-auto-rows
- grid-auto-flow
- grid
該屬性將元素定義爲網格容器,建立一個網格格式上下文。
取值:
- grid -生成塊級網格
- inline-grid -生成行級網格
subgrid -如果網格盒子本身是一個網格項(如嵌套網格佈局),使用該值表示行列尺寸來源於其父元素而非自身
.container { display: grid | inline-grid | subgrid; }
注意:column、float、clear、vertical-align對網格盒子沒有影響。
#grid-template-columns
#grid-template-rows
用以空格分隔的列表值來定義網格的列和行。值表示軌跡大小,空格表示網格線。
取值:
<track-size>
- 可以爲長度、百分比或網格可用空間的一部分(單位是fr)<line-name>
- 可以取任意名稱
.container {
grid-template-columns: <track-size> ... | <line-name> <track-size> ...;
grid-template-rows: <track-size> ... | <line-name> <track-size> ...;
}
例如:
當兩個軌跡值之間留有空格的話,網格線會被自動賦值爲一個數字名字:
.container{
grid-template-columns: 40px 50px auto 50px 40px;
grid-template-rows: 25% 100px auto;
}
不過也可以爲網格線顯示聲明一個名字。注意下面聲明名稱的括號語法:
.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];
}
注意:每一行可能有不止一個名字。如下所示,第二行有row-1end和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]) 5%;
}
這等同於:
.container {
grid-template-columns: 20px [col-start] 20px [col-start] 20px [col-start] 5%;
}
fr單允許我們將軌道尺寸設置爲網格容器的一部分。如下所示會將每個項目的尺寸設置爲容器的三分之一:
.container {
grid-template-columns: 1fr 1fr 1fr;
}
可用空間的計算是要除去非彈性項目的。如下所示,fr單元可用空間的大小不包括50px:
.container {
grid-template-columns: 1fr 50px 1fr 1fr;
}
通過引用由grid-area
屬性指定的網格區域的名字來定義網格模板。重複網格區域的名稱會使其跨越那些單元格。一個句點表示一個空單元格。語法本身提供了對網格結構的可視化。
取值:
<grid-area-name>
-grid-area
屬性指定的網格區域的名字.
- 句點表示一個空網格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 {
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";
}
這會創建一個三行四列的網格。頂部單元格組成了header區域,中間一行由兩個main區域、一個空區域、一個sidebar區域構成,最後一行是footer。
聲明的每一行都要有相同數量的單元格。
您可以使用任意數量的相鄰句點表示一個單獨的空單元格。只要句點之間沒有間隔它們就表示一個單獨的單元。
要注意,該語法命名的是區域,而非行。不過使用該語法定義區域後,該區域兩端都會自動獲得命名。如,定義某個網格區域爲foo,則該區域起始行和起始列的網格線將叫做foo-start,終止行和列將叫做foo-end。這也意味着一些網格線或有好幾個名字。比如上例中最左邊的網格線有三個名字:header-start、main-start和footer-start。
同時設置grid-template-rows
、grid-template-columns
、grid-template-areas
的簡寫。
取值:
- none - 設置這三個屬性爲最初的值
- subgrid - 設置
grid-template-rows
、grid-template-columns
爲subgrid
,grid-template-areas
爲起始值 <grid-template-rows>
/<grid-template-columns>
- 設置這兩個屬性爲指定值,設置grid-template-areas
爲none
.container {
grid-template: none | subgrid | <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-auto-columns
、grid-auto-rows
和 grid-auto-flow
),而在很多時候我們需要重置,所以推薦用grid
屬性代替grid-template
。
#grid-column-gap
#grid-row-gap
這兩個屬性指定網格線的尺寸。可以把它們想成行、列之間的槽寬。
取值:
<line-size>
- 長度值
.container {
grid-column-gap: <line-size>;
grid-row-gap: <line-size>;
}
如下所示:
.container {
grid-template-columns: 100px 50px 100px;
grid-template-rows: 80px auto 80px;
grid-column-gap: 10px;
grid-row-gap: 15px;
}
槽只存在於行列之間,不會在外邊緣創建。
grid-row-gap
和grid-column-gap
的簡寫。
取值:
<grid-row-gap>
<grid-column-gap>
- 長度值
.container {
grid-gap: <grid-row-gap> <grid-column-gap>;
}
舉個例子:
.container{
grid-template-columns: 100px 50px 100px;
grid-template-rows: 80px auto 80px;
grid-gap: 10px 15px;
}
若<grid-row-gap>
未指定會設置爲同<grid-column-gap>
相同的值。
沿着行軸對齊網格項的內容(不同於align-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
屬性單獨設置。
沿列軸對齊網格項的內容(同沿行軸設置的justify-item相反)。該值應用到網格容器的所有網格項。
取值:
- start - 內容沿網格區域頂部對齊
- end - 內容沿網格區域底部對齊
- center - 內容沿網格區域垂直對齊
- stretch - 內容填滿整個網格區域的高度(默認值)
.container {
align-items: start | end | center | stretch;
}
舉例如下:
.container {
align-items: start;
}
.container {
align-items: end;
}
.container {
align-items: center;
}
.container {
align-items: stretch;
}
網格項可以用align-self
屬性單獨設置。
有時候網格的總尺寸比網格容器要小,比如當網格項目用諸如px這樣的非可伸縮單位時。在這種情況下,我們就要設置網格相對於網格容器的對齊方式了。該屬性將使網格沿行軸對齊(與align-content是沿列軸對齊正相反)。
取值:
- start -網格沿網格容器的左端對齊
- end -網格沿網格容器的右端對齊
- center -網格沿網格容器水平居中對齊
- stretch -調整網格項目的尺寸使網格填滿網格容器的寬度
- 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;
}
有時候網格的總尺寸比網格容器要小,比如當網格項目用諸如px這樣的非可伸縮單位時。在這種情況下,我們就要設置網格相對於網格容器的對齊方式了。該屬性將使網格沿列軸對齊(與justify-content是沿橫軸對齊正相反)。
取值:
- start -網格沿網格容器的頂部對齊
- end -網格沿網格容器的底部對齊
- center -網格沿網格容器垂直居中對齊
- stretch -調整網格項目的尺寸使網格填滿網格容器的高度
- space-around -網格項均勻分佈,兩端的空間是兩個網格項之間空間的一半
- space-between -網格項均勻分佈,兩端不留空間
- space-evenly -網格項均勻分佈,包括兩端
.container {
align-content: start | end | center | stretch | space-around | space-between | space-evenly;
}
如下所示:
.container {
align-content: start;
}
.container {
align-content: end;
}
.container {
align-content: center;
}
.container {
align-content: stretch;
}
.container {
align-content: space-around;
}
.container {
align-content: space-between;
}
.container {
align-content: space-evenly;
}
#grid-auto-columns
#grid-auto-rows
指定任何自動生成的網格軌跡(也稱隱式網格軌跡)的大小。 當您明確定位超出定義網格範圍的行或列(通過grid-template-rows / grid-template-columns)時,將創建隱式網格軌跡。
取值:
<track-size>
-可以是一個長度、百分比或網格可用空間的一部分(單位是fr)
.container {
grid-auto-columns: <track-size> ...;
grid-auto-rows: <track-size> ...;
}
下面將說明如何隱式創建網格軌跡,考慮如下代碼:
.container {
grid-template-columns: 60px 60px;
grid-template-rows: 90px 90px
}
這會創建2×2的網格。
如果像下面那樣使用grid-column和grid-row放置網格項的話:
.item-a {
grid-column: 1 / 2;
grid-row: 2 / 3;
}
.item-b {
grid-column: 5 / 6;
grid-row: 2 / 3;
}
.item-b起始於列線5,終止於列線6,但我們定義的網格並沒有列線5和6。因爲我們參考的線並不存在,所以寬度爲0的隱式的軌跡會被創建來填充缺口。我們可以使用grid-auto-columns和grid-auto-rows來指定這些隱式軌跡的寬度:
.container {
grid-auto-columns: 60px;
}
如果您沒有明確將網格項放置在網格里,則自動佈局算法會自動放置該項目。該屬性控制自動佈局算法的工作方式。
取值:
- row - 告訴自動佈局算法依次填充每一行,必要時添加新行
- column - 告訴自動佈局算法依次填充每一行列,必要時添加新列
- dense - 告訴自動佈局算法如果小項目出現的晚的話,就先嚐試在網格中填充孔
.container {
grid-auto-flow: row | column | row dense | column dense
}
注意dense可能會使您的項目無序,
舉例如下:
考慮如下html結構:
<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>
定義一個2行5列的網格,並設置grid-auto-flow
爲row
(這也是默認值)。
.container {
display: grid;
grid-template-columns: 60px 60px 60px 60px 60px;
grid-template-rows: 30px 30px;
grid-auto-flow: row;
}
當把這些項目放到網格里時,您可能只指定了兩個:
.item-a {
grid-column: 1;
grid-row: 1 / 3;
}
.item-e {
grid-column: 5;
grid-row: 1 / 3;
}
因爲我們設置了grid-auto-flow
爲row
,我們的網格看起來如下圖所示。注意到有3個項目(item-b、item-c和item-d)雖然並沒有顯示放置,但也流動到了可用行的位置。
要是設置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;
}
同時聲明grid-template-rows
、grid-template-columns
、grid-template-areas
、grid-auto-rows
、grid-auto-columns
和grid-auto-flow
等屬性的簡寫。grid-column-gap和
grid-row-gap`會被設置爲初始值,儘管在該屬性中沒有顯示設置。
取值:
none
- 設置所有子屬性爲起始值<grid-template-rows>/<grid-template-columns>
- 分別設置這兩個值爲指定值,而其他子屬性值爲起始值。<grid-auto-flow>[<grid-auto-rows>[/<grid-auto-columns>]]
- 分別接收與這三個屬性完全相同的值。如果省略grid-auto-columns
,則將其設置其爲grid-auto-rows
指定的值。 如果兩者都被省略,則將它們設置爲其初始值。
.container {
grid: none | <grid-template-rows> / <grid-template-columns> | <grid-auto-flow> [<grid-auto-rows> [/ <grid-auto-columns>]];
}
例子:
接下來的兩個代碼是等價的:
.container {
grid: 200px auto / 1fr auto 1fr;
}
.container {
grid-template-rows: 200px auto;
grid-template-columns: 1fr auto 1fr;
grid-template-areas: none;
}
接下來的兩個代碼塊是等價的:
.container {
grid: column 1fr / auto;
}
.container {
grid-auto-flow: column;
grid-auto-rows: 1fr;
grid-auto-columns: auto;
}
它同樣接收一個更復雜但相當方便的語法來同時設置所有屬性。指定grid-template-areas
、grid-template-rows
和grid-template-columns
,所有其他子屬性都設置爲其初始值。 您正在做的是指定與其各自的網格區域一致的行名和軌道大小。 這很容易用一個例子來描述:
.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 Items屬性及取值
- grid-column-start
- grid-column-end
- grid-row-start
- grid-row-end
- grid-column
- grid-row
- grid-area
- justify-self
- align-self
#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 {
grid-column-start: <number> | <name> | span <number> | span <name> | auto
grid-column-end: <number> | <name> | span <number> | span <name> | auto
grid-row-start: <number> | <name> | span <number> | span <name> | auto
grid-row-end: <number> | <name> | span <number> | span <name> | auto
}
如下例:
.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
,則跨度默認爲1。
項目可以彼此覆蓋。可以用z-index
來控制它們的堆疊順序。
分別是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;
}
如沒有聲明終止行的值,則默認跨度爲1。
該屬性可以引用grid-template-areas
創建的模板名字。另外,它也可以作爲grid-row-start
+grid-column-start
+grid-row-end
+grid-column-end
的簡寫。
取值:
<name>
- 選擇的名字<row-start> / <column-start> / <row-end> / <column-end>
- 可以是帶編號或命名的網格線
.item {
grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
}
舉例如下:
作爲給項目聲明名稱的一種方式:
.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
}
沿橫軸對齊網格項的內容(與沿縱軸對齊內容的align-self
正相反)。其值會在單個網格項內生效。
取值:
- start - 沿網格區域的左端對齊
- end - 沿網格區域的右端對齊
- center - 沿網格區域水平居中對齊
- stretch - 填充網格區域的整個寬度(默認值)
.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;
}
設置網格中所有項目的對齊方式,可以通過設置網格容器的justify-items
屬性來完成。
沿縱軸對齊網格項的內容(與沿橫軸對齊內容的justify-self
正相反)。其值會在單個網格項內生效。
取值:
- start - 沿網格區域的頂部對齊
- end - 沿網格區域的底部對齊
- center - 沿網格區域垂直居中對齊
- stretch - 填充網格區域的整個高度(默認值)
.item {
align-self: start | end | center | stretch;
}
.item-a {
align-self: start;
}
.item-a {
align-self: end;
}
.item-a {
align-self: center;
}
.item-a {
align-self: stretch;
}
設置網格中所有項目的對齊方式,可以通過設置網格容器的align-items
屬性來完成。