1. 程式人生 > 實用技巧 >grid佈局詳解

grid佈局詳解

說明

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函式重複兩次,最後就在網格種出現了四行,行高分別是60px100px60px100px

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-rowgrid-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-rowgrid-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-itemsalign-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的內容就差不多了,雖然屬性特別多,但是當你熟練掌握後,你會發現佈局會異常簡單。