說明
在CSS當中,布局一直是一個非常重要的話題,在漫長的發展時間當中,出現了很多種布局方案,但是就整體來說主要分成解決PC端和移動端布局的兩大類,在這兩大類中包括例如傳統的table布局,后期的display+float+position的布局方案,再比如CSS3中新增加的Flex布局和多列布局等等,而在本篇博文中,將會主要講述一種比較新的布局方案Grid布局。
正是因為Grid布局的年紀較小,所以從兼容性上來說,要遜色於Flex布局,但是從實際的開發效率上來講,卻是比Flex更加強大,這也是本篇博文的出發點之一。
Grid布局從某種意義上來說和Flex布局很類似,都有着自己的一套屬性和布局邏輯。但是從復雜度來講,是要超過Flex布局的,體現最直觀的就是Grid當中包含的屬性,數量上遠遠超過Flex布局。
所以如果你是一個新人,連Flex都不是很熟練,那么建議你先去研究一下Flex布局方案。你可以把Flex理解為一維布局,而把Grid理解為二維布局,如果你對Flex非常熟練,將會讓你對Grid的學習更加的順暢。
因為Grid屬性較多,你在學習Grid布局之前一定要做好十足的心里准備。
基本概念
Grid布局某種意義上來說,和table是非常類似的,二者都是在網頁中構建一個表格,然后將內容放進表格中從而實現布局的目的。
但是作為布局界新人的Grid確是遠遠比table更加的強大。
首先,先來弄清楚在Grid當中非常重要的一些概念。
- 容器: 采用網格布局的區域稱之為容器。
- 項目: 容器內部的子元素為項目。
- 行和列: 既然要在網頁中構建表格,那么必然存在行和列。
- 單元格: 行和列的交叉區域,稱為單元格。
- 網格線: 將容器划分成不同區域的線,稱之為網格線。
如圖:

開啟Grid
如何將一個容器變成Grid容器呢? 可以通過下面的屬性來設置:
display:grid | inline-grid;
display:grid可以將容器變為一個塊級容器,容器內部采用網格布局,display:inline-grid可以將容器變為一個行內塊容器,容器內部采用網格布局。
行和列的划分
在容器的身上開啟了網格布局之后,就可以來規划行和列。主要應用到下面的兩個屬性:
grid-template-rows: 定義每一行的行高。grid-template-columns: 定義每一列的列寬。
例如下面的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid #222;
/* 開啟網格布局 */
display: grid;
/* 三行三列:每行高度100px 每列寬度100px */
grid-template-rows: 100px 100px 100px;
grid-template-columns: 100px 100px 100px;
}
</style>
</head>
<body>
<article></article>
</body>
</html>
實現的效果如下:

可以通過瀏覽器的輔助工具查看網格內部的具體划分,下面以火狐瀏覽器開發版為例:

需要注意的是,單元格的大小取決於行高和列寬,並不取決於單元格內部是否擁有子元素。例如上面的代碼中,單元格內部並沒有子元素,單元格也並沒有受到任何影響。
行高和列寬可以使用絕對單位
px,也可以使用相對的百分比。
repeat() 函數
在設置行高和列寬的時候,如果多個值重復的定義不免有些麻煩,所幸Grid提供了repeat()函數,通過repeat()函數可以實現批量的設置行高和列寬。例如上面的三行三列寬高各100px的網格,就可以通過下面的方式來設置:
article {
width: 300px;
height: 300px;
border: 1px solid #222;
/* 開啟網格布局 */
display: grid;
/* 三行三列:每行高度100px 每列寬度100px */
grid-template-rows: repeat(3, 100px); /* 第一個值表示重復的次數,例如三行就寫3次 */
grid-template-columns: repeat(3, 100px);
}
在使用repeat()函數時,也可以按照一定的規律重復,例如:
article {
...
grid-template-rows: repeat(2, 60px 100px)
...
}
上面的代碼看上去設置的是2行,但是卻被repeat函數的第二個參數所影響,第二個參數中,設置了第一行的行高是60px,第二行的行高是100px,這種情況再被repeat函數重復兩次,最后就在網格種出現了四行,行高分別是60px、100px、60px、100px。

auto-fill
在設置行高或者列寬的時候,行數或者列數並沒有固定,就可以使用auto-fill來自動進行填充。
例如:
article {
width: 400px;
height: 300px;
border: 1px solid #222;
/* 開啟網格布局 */
display: grid;
/* 每行行高為100px,設置自動填充, 最終的行數為高度300px / 100px = 3 */
grid-template-rows: repeat(auto-fill, 100px);
/* 每列的列寬為100px, 設置自動填充,最終的列數為寬度400px / 100px = 4 */
grid-template-columns: repeat(auto-fill, 100px);
}
效果如下:

fr
在規划行高或者列寬的時候,可以通過fr這個單位來實現按照比例划分。
例如:
article {
...
grid-template-rows: 1fr 1fr 1fr;
/* 等同於 */
grid-template-rows: repeat(3, 1fr)
...
}
上面的代碼意思是一共划出三行,每行的高度 = 總高度 / 分數(也就是1fr + 1fr + 1fr = 3) , 這樣做的好處是可以讓行高或者列寬通過fr進行動態的變化。
gap
可以通過gap屬性來設置行間距和列間距,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
width: 320px;
height: 320px;
border: 1px solid red;
/* 開啟grid */
display: grid;
/* 設置行高和列寬 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
/* 設置行間距和列間距 */
gap: 10px;
/*gap: 10px 5px; 設置兩個值的時候,第一個值是行間距,第二個值表示列間距*/
}
.content div {
background-color: orange;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
</body>
</html>
效果如下:

根據網格線編號移動單元格內的元素
每個單元格的位置是可以指定的,具體的方法就是指定單元格的四個邊框,分別定位在哪根網格線。
先來看代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid */
display: grid;
/* 設置行高和列寬 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
}
.content div {
background-color: orange;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
效果如下:

代碼的效果如上,現在假如想要將數字1所在的單元格移動到紅色區域的位置。就可以將單元格起始位置的行和列,結束位置的行和列所在的網格線調整為和紅色區域相同即可。
設置網格線所在的位置可以通過下面的屬性來進行設置:
grid-row-start: 設置起始位置的行所在的網格線編號grid-column-start: 設置起始位置的列所在的網格線編號grid-row-end: 設置結束位置的行所在的網格線編號grid-column-start: 設置結束位置的列所在的網格線編號
例如上面的紅色區域所在的行起始位置的網格線為第三根網格線,那么grid-row-start的值就為3,結束位置的網格線為第四根網格線,那么grid-row-end的值就為4。
完整的css代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid */
display: grid;
/* 設置行高和列寬 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
}
.content div {
background-color: orange;
}
/* 通過調整開始和結束的行和列網格線所在的位置來設置第一個元素的位置移動 */
.content div:first-child {
grid-row-start: 3;
grid-row-end: 4;
grid-column-start: 2;
grid-column-end: 3;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
所實現的效果如下:

可以發現,第一個元素已經順利的移動到了指定的位置。同時,需要注意的是,當第一個項目移動了之后,第二個項目立即占據了之前第一個項目的位置。
給每條網格線進行命名操作
在設置行高和列寬的時候,可以同時給每條網格線進行命名操作,命名之后可以方便后續的項目位置移動。
例如:
article {
...
grid-template-rows: [x1-start] 100px [x1-end x2-start] 100px [x2-end x3-start] 100px [x3-end];
grid-template-columns: [y1-start] 100px [y1-end y2-start] 100px [y2-end y3-start] 100px [y3-end];
...
}
通過網格線名進行項目位移
和上面說的位移一樣,就是將網格線編號換成了網格線名。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
.content {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid */
display: grid;
/* 設置行高和列寬 */
grid-template-rows: [x1-start] 100px [x1-end x2-start] 100px [x2-end x3-start] 100px [x3-end];
grid-template-columns: [y1-start] 100px [y1-end y2-start] 100px [y2-end y3-start] 100px [y3-end];
}
.content div {
background-color: orange;
}
/* 通過網格線名進行位移 */
.content div:first-child {
grid-row-start: x3-start;
grid-row-end: x3-end;
grid-column-start: y2-start;
grid-column-end: y2-end;
}
</style>
</head>
<body>
<div class="content">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
如果在設置行高和列寬的時候,是通過repeat()函數來完成,那么就可以通過下面的寫法來對每條網格線進行命名。
article {
...
grid-template-rows: repeat(3, [r-start] 100px [r-end]);
grid-template-columns: repeat(3, [c-start] 100px [c-end]);
...
}
項目位移時的用法如下:
div {
...
grid-row-start: r-start 1;
grid-column-start: c-start 1;
grid-row-end: span 1;
grid-column-end: span 3;
...
}
簡寫屬性: grid-row 和 grid-column
在設置項目的位置的時候,可以通過grid-row和grid-column這兩個簡寫屬性。
grid-row屬性的第一個值表示的是grid-row-start,第二個值表示grid-row-end,兩個值之間用/來分隔。
grid-column屬性第一個值表示grid-column-start,第二個值表示grid-column-end,兩個值之間用/來分隔。
如下:
div {
...
grid-row: 1 / 2;
grid-column: 1 / 4;
...
}
當然,在grid-row和grid-column兩個屬性種也可以寫網格線的名字。
div {
...
grid-row: 1 / x3-end;
grid-column: 1 / y2-end;
...
}
效果是一樣的。
span關鍵字
通過span關鍵字,可以讓項目跨域指定的單元格。
示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid */
display: grid;
/* 設置行高和列寬 */
grid-template-rows: repeat(3, 100px);
grid-template-columns: repeat(3, 100px);
}
article div {
background-color: orange;
}
/* 讓第一個div占據第一行 */
article div:first-child {
grid-row: 1 / span 1;
grid-column: 1 / span 3;
}
article div:last-child {
grid-row: 2 / span 2;
grid-column: 2 / span 1;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
</article>
</body>
</html>
效果如下:

grid-area
通過grid-area屬性能夠給項目設置具體的單元格位置。
第一個值表示開始的行 第二個值表示開始的列 第三個值表示結束的行 第四個值表示結束的列。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid #222;
/* 開啟grid布局 */
display: grid;
/* 畫出柵格線, 並且在畫出柵格線的同時,給柵格線起一個名字,行的柵格線統稱為r,列的柵格線統稱為c */
grid-template-rows: repeat(3, [r] 100px); /* 表示行的第一根網格線就叫做r 1,第二個就是r 2 , 其他的以此類推*/
grid-template-columns: repeat(3,[c] 100px);
}
article div {
background-color: orange;
}
/* 通過grid-area 來給每一個單元格設置區域,第一個值表示開始的行 第二個值表示開始的列 第三個值表示結束的行 第四個值表示結束的列 */
article div:first-child {
grid-area: r 2/ c 1 / r 3 / c 4;
}
article div:last-child {
grid-area: r 1/c 2 / r 2 / c 3;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
</article>
</body>
</html>
效果如下:

給網格不同區域進行命名
在使用grid進行布局的時候,可以通過grid-template-areas來給不同的區域進行命名。
先來通過grid布局畫出一個三行兩列的布局,模擬移動端網頁開發種經常出現的一種布局結構:
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid</title>
<style>
* {
margin: 0;
padding: 0;
}
/* 模擬移動端的小屏幕 */
.content {
width: 100vw;
height: 100vh;
/* 開啟grid */
display: grid;
/* 三行兩列 */
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 60px 1fr;
}
</style>
</head>
<body>
<div class="content">
</div>
</body>
</html>
效果如下:

現在如果需要將第一行的兩個部分合並,也同時將第三行的兩個部分該怎么辦呢?
這個時候可以通過grid-template-areas屬性來對不同的區域進行命名。
如下:
.content {
width: 100vw;
height: 100vh;
/* 開啟grid */
display: grid;
/* 三行兩列 */
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 60px 1fr;
grid-template-areas: "header header"
"nav main"
"footer footer";
}
現在假設有一個子元素想要占據第一行的全部內容,可以通過grid-area屬性來進行設置,例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid</title>
<style>
* {
margin: 0;
padding: 0;
}
/* 模擬移動端的小屏幕 */
.content {
width: 100vw;
height: 100vh;
/* 開啟grid */
display: grid;
/* 三行兩列 */
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 60px 1fr;
grid-template-areas: "header header"
"nav main"
"footer footer";
}
header {
/* 因為第一行的兩個部分都是header區域,所以此時直接全部占領 */
grid-area: header;
background-color: #222;
}
nav {
grid-area: nav;
background-color: lightblue;
}
main {
grid-area: main;
background-color: lightblue;
}
footer {
grid-area: footer;
background-color: #222;
}
</style>
</head>
<body>
<div class="content">
<header></header>
<nav></nav>
<main></main>
<footer></footer>
</div>
</body>
</html>
效果如下:

上面的這種結構是非常常見的,例如:

在通過
grid-template-areas屬性對不同區域進行命名的時候,如果某些區域不需要命名,可以通過.點來占位。
grid-auto-flow 調整排列方式
一般來說,默認的排列方式是從左向右,從上到下,可以通過grid-auto-flow屬性來設置項目到底是水平排列還是垂直排列。
如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid布局 */
display: grid;
/* 畫出柵格線 */
grid-template-rows: repeat(3,100px);
grid-template-columns: repeat(3, 100px);
}
article div {
background-color: orange;
}
</style>
</head>
<body>
<!-- 柵格的流動方向 -->
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</article>
</body>
</html>
上面的代碼種采用的是項目默認排列方式,等同於grid-auto-flow:row的效果,瀏覽器中效果如下:

現在來添加grid-auto-flow屬性,讓其垂直排列,如下:
article {
...
/* 改變網格排列方向,默認值是row,是行排列,column 則是列排列 */
grid-auto-flow: column;
}
效果如下:

grid-auto-flow還有一種特殊的應用,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid布局 */
display: grid;
/* 畫出柵格線 */
grid-template-rows: repeat(3,100px);
grid-template-columns: repeat(3, 100px);
/* 當存在剩余空間時,強制將剩余空間填滿 */
grid-auto-flow: row;
}
article div {
background-color: orange;
}
article div:nth-child(1) {
grid-column: 1 / span 2;
}
article div:nth-child(2) {
grid-column: 2 / span 1;
}
</style>
</head>
<body>
<!-- 柵格的流動方向 -->
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</article>
</body>
</html>
瀏覽器效果如下:

如果想要從上到下的用元素填補剩余空間,可以如下:
article {
...
/* 當存在剩余空間時,強制將剩余空間填滿 */
grid-auto-flow: row dense;
}
效果如下:

設置項目水平和垂直方向的排列位置
先來看下面的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid */
display: grid;
grid-template-columns: repeat(4, 60px);
grid-template-rows: repeat(3,60px);
}
article div {
background-color: pink;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</article>
</body>
</html>
在上面的代碼中,創建了一個三行四列的表格,但是和之前不一樣的是,在上面的代碼中創建的網格無論是寬度和高度都是小於最外層容器的寬度和高度。
瀏覽器中的效果如下:

這個時候可以通過justify-content屬性來調整所有項目在容器中的水平位置,通過align-content屬性來調整所有項目在容器中垂直位置。
兩個屬性的屬性值相同,如下:
start
end
center
space-around
space-between
space-evenly
如下,讓所有的項目在水平方向居中,垂直方向在底部。
article {
...
justify-content: center;
align-content: end;
}
效果如下:

justify-content屬性和align-content屬性的簡寫屬性為place-content, place-content第一個屬性值表示align-content的值,第二個屬性值表示justify-content的屬性值。
例如上面的設置就可以寫成:
place-content: center end;
設置項目在單元格中的位置
設置項目在單元格中的位置可以通過justify-items屬性來設置水平方向的位置,通過align-items屬性來設置元素在垂直方向上的位置。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>grid布局</title>
<style>
article {
width: 300px;
height: 300px;
border: 1px solid red;
/* 開啟grid */
display: grid;
grid-template-columns: repeat(4, 60px);
grid-template-rows: repeat(3,60px);
/* 設置項目在單元格中垂直居中 */
justify-items: center;
align-items: center;
}
article div {
background-color: pink;
width: 20px;
height: 20px;
}
</style>
</head>
<body>
<article>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</article>
</body>
</html>
效果如下:

justify-items和align-items的簡寫屬性為place-items,第一個值表示align-items也就是垂直位置,第二個值表示justify-items 也就是水平位置。
設置某個項目在單元格中的位置
可以通過justify-self屬性來設置項目在單元格中的水平位置,通過align-self屬性設置項目在單元格中的垂直位置。
如下:
article div:first-child {
justify-self: end;
align-self: center;
}
效果如下:

簡寫屬性為place-self,第一個值表示垂直方向align-self屬性的值,第二個值表示水平方向justify-self的位置。
屬性總結
因為grid屬性較多,所以在這列了一個思維導圖,如下:

想要查看具體可以點擊這里查看。
總結
ok,到這,基本上grid的內容就差不多了,雖然屬性特別多,但是當你熟練掌握后,你會發現布局會異常簡單。
