Grid佈局

gaiyabing發表於2020-09-07

grid佈局是一種新的佈局方案。傳統佈局使用浮動和定位,而grid佈局可以像表格一樣將頁面容器分割成一塊一塊的區域,定義子元素的排布和位置。可以說是flex佈局的升級版。

借用大神旭哥的話:

Grid佈局就像是“分田種地”。故事是這樣的,張老闆是個程式設計師,省吃儉用攢了點小錢,然後老家因為城鎮化建設,農村都沒什麼人,土地都荒廢在那裡,於是就承包了一塊地,打算養養魚,種種果樹。承包的地方很挺大,如何劃分土地就成了問題,於是張老闆打算藉助Grid佈局來劃分。

對於身為碼農的我們來說,種地就對了!

相容性

Grid佈局是微軟在2010年提出來的一種新的佈局方式,到2016年的時候提交了該佈局的草案。經過近幾年發展,相容性越來越好。

grid佈局相容性

引用自 Can I Use 。相容火狐52+、谷歌57+等現代瀏覽器,IE10和11需要新增-ms-來實現相容。

名詞概念

Grid Container、Grid Items(網格容器、網格項)

元素應用 display: grid;即成為網格容器,它是所有網格項的父元素。

很好理解,下面這個例子,class為container的div設定了 display: grid;,container就是網格容器,裡面的item都是網格項。

<style>
  .container{ display: grid; }
</style>
<div class="container">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
</div>

Grid Lines(網格線)

所謂的網格都是由橫向和豎向的若干線交錯產生的,這些線就是網格線。

熟知數學的我們知道,正常情況下,n行有n + 1根水平網格線,m列有m + 1根垂直網格線,比如三行就有四根水平網格線。

網格線是定義容器的時候產生的,和網格項沒有直接的關係,網格項是直接存在於html中的,但是網格線正常看不到它。說白了網格線是虛擬的。

Grid Track(網格軌道)

兩個相鄰的網格線之間為網格軌道。

網格軌道的特點是能接觸到網格容器的邊緣。

Grid Cell(網格單元)

兩個相鄰的列網格線和兩個相鄰的行網格線之間的空間為網格單元,類似於excel中的單元格。

Grid Areas(網格區域)

四條網格線包圍的地方稱為網格區域,類似於excel中的合併單元格。

關鍵字

fr(剩餘空間分配數)

fr單位被用於在一系列長度值中分配剩餘空間,如果多個已指定了多個部分,則剩下的空間根據各自的數字按比例分配。

fr相容性

gr(網格數)

目前未被w3c採納,可以作為了解。


屬性

介紹完概念,進入正題。

gird佈局的屬性可以分為兩類:容器屬性和專案屬性。

容器屬性分為這幾類:

  • display:佈局方式
  • grid-template:定義網格線的名稱和網格軌道的尺寸大小
  • gap:定義網格項之間的間隙
  • items:定義網格單元內容的位置
  • content:定義整個內容區域在容器裡面的位置
  • grid-auto:定義多餘網格的大小
  • grid-auto-flow:定義網格項的放置順序

專案屬性分為這幾類:

  • column / row:指定專案位於哪(些)條網格線
  • area:指定專案放在哪一個區域
  • self:指定網格單元內容的位置

下面來看具體的使用

容器屬性

1. display

有三種取值:grid | inline-grid | subgrid

gird:生成塊級網格
inline-grid: 生成行內網格
subgrid: 網格容器本身就是網格項(巢狀網格容器),此屬性用來繼承其父網格容器的行、列大小。(目前所有瀏覽器都不相容)

注意,設為網格佈局以後,容器子元素(專案)的float、display: inline-block、display: table-cell、vertical-align和column-*等設定都將失效。但是position是不會失效的。

2.1 grid-template-columns和grid-template-rows

grid-template-columns用來定義每一列的寬度,grid-template-rows用來定義每一行的高度。

.container{
    grid-template-columns: 40px 50px auto 50px 40px;
    grid-template-rows: 25% 100px auto;
}

例項1

上面這個例子,就是一個三行五列的網格佈局。

  • grid-template-columns的取值可以是絕對長度(px、em)、百分比、auto、fr、函式。
  • auto表示由瀏覽器自己決定長度。
函式&關鍵字:

$minmax(min, max):$

  • 用來定義一個範圍,最小值為min,最大值為max。
  • max可以設定為fr,min不可以。
  • 如果max值小於min值,則該值會被視為min值。
.container{
    grid-template-columns: 40px 50px minmax(50px, 2fr) 1fr 40px;
    grid-template-rows: 25% 100px auto;
}

$repeat(repeat, values):$

  • 表示軌道列表的重複片段,允許以更緊湊的形式寫入大量顯示重複模式的列或行。
  • 第一個值repeat可以是固定的數字(n),表示重複n次,也可以是auto-fitauto-fill,表示自動填充,容納不下的放到下一行。
    例如:
/* css */
.container{
   width: 800px;
   height: 300px;
   display: grid;
   grid-template-columns: repeat(auto-fit, 200px);
   grid-template-rows: auto auto;
   gap: 5px;
}
<!--html-->
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

repeat測試

這裡有兩點要注意:

  • 佈局設定的是兩行,最終由於單個軌道容納不下,最終變成4行。多出的這兩行可以用grid-auto-rows來進行設定,這個屬性後面會講到。
  • auto-fitauto-fill通常是沒有區別的,只有在容器寬度大於子元素的最小寬度總和時才有顯示區別。
    我們來把上面的例子稍微改動一下,
.container{
   grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
}

auto-fill關鍵字

我們再看一下auto-fit

.container{
   grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
}

auto-fit關鍵字

可以發現,使用auto-fill的網格寬度為52.5px,auto-fit的網格寬度為75.5px
這個值我們可以計算一下
假設一行有n個單元格,兩個單元格之間的間隙是5
所以 $50n + 5(n - 1) = 800$,n ≈ 14.6,向下取整得到14
有了列數,假設單元格寬度為 $x$ 帶入上面這個式子:$14x + 5*13 = 800$,得到單元格寬度為52.5px

由此可知

  • auto-fitauto-fill都會以最小的寬度建立單元格,建立完成以後,如果有多餘的單元格,auto-fit會把這些多餘的單元格均分給每一網格項,再重新建立單元格,而auto-fill不會。
  • 需要注意的是:只有在容器寬度大於子元素的最小寬度總和時才會這樣,除此之外兩者的顯示是一樣的。
網格線名稱:

grid-template-columns屬性裡面,還可以使用方括號,指定每一根網格線的名字,方便以後的引用。
名字可以寫多個,用空格隔開。

.container{
    grid-template-columns: [c1 r1] 40px [c2] 50px [c3] auto [c4] 50px [c5] 40px;
    grid-template-rows: 25% 100px auto;
}

grid-template-rows的用法與grid-template-columns完全一樣。

2.2 grid-template-areas

areas是區域的意思,grid-template-areas用來給各個網格單元命名,就像給網格線命名一個道理。寫法上類似於矩陣。

.container{
    grid-template-columns: 40px 50px auto 50px 40px;
    grid-template-rows: 25% 100px auto;
    grid-template-areas:
      'a a a b b'
      'c c d . e'
      'c c d . e';
}

上面這個例子將網格分為a、b、c、d、e五個區域。如果某些區域不需要利用,則使用"點"(.)表示。

需要注意:

  • 寫法上分號的位置,最後單元格的末尾才加分號。
  • 多個同名的,跨越相鄰行或列的單元稱為網格區塊(grid area)。非矩形的網格區塊是無效的。
  • 網格區塊會影響到網格線。每個區塊的起始網格線,會自動命名為區塊名-start,終止網格線自動命名為區塊名-end。上面例子中,第一行第3、4列間的網格線自動命名為a-endb-start,水平網格線亦同理。
  • 網格的命名並不會馬上影響網格項的顯示,需要使用專案屬性指定其位置才可以,專案屬性下面會講到。

2.3 grid-template

grid-template屬性是grid-template-columns、grid-template-rows和grid-template-areas這三個屬性的合併簡寫形式。
語法是 grid-template: <'grid-template-rows'> / <'grid-template-columns'>

.container {
  display: grid;
  width: 100%;
  height: 200px;
  grid-template: [header-left] "head head" 30px [header-right]
                 [main-left]   "nav  main" 1fr  [main-right]
                 [footer-left] "nav  foot" 30px [footer-right]
                 / 120px 1fr;
}

先寫行屬性,再寫列屬性,中間用 / 隔開,從易讀性考慮,不建議這樣寫。

3. grid-gap

grid-row-gap 屬性是用來設定網格行與行之間的間隙,grid-column-gap 屬性是用來設定網格列與列之間的間隙。

.container{
   display: grid;
   grid-template-columns: repeat(auto-fit, 200px);
   grid-template-rows: auto auto;
   grid-row-gap: 20px;
   grid-column-gap: 10px;
}

上面的程式碼設定列間隙為10px,行間隙為20px。

grid-gap是 grid-row-gap 和 grid-column-gap的簡寫形式,語法如下:
grid-gap:<row-gap> <column-gap>
如果省略了第二個值,瀏覽器認為第二個值等於第一個值。

根據最新標準,上面三個屬性名的grid-字首已經刪除,grid-column-gap和grid-row-gap寫成column-gap和row-gap,grid-gap寫成gap。
但是為了相容瀏覽器,比如火狐52-61,還需要使用帶有字首的屬性。

4.1. justify-items和align-items

justify-items 定義了網格佈局中專案的水平對齊方式,align-items 定義了豎直對齊方式。

這個與flex佈局的有些區別,flex佈局的對齊方式分主軸和交叉軸,主軸方向的改變影響屬性作用的方向。而grid佈局是二維的,行列方向是固定的,所以說到 justify 一定就是指行(水平)方向,align就是指列(豎直)方向。

屬性值:

  • start:內容與網格區域的左端對齊
  • end: 內容與網格區域的右端對齊
  • center: 內容位於網格區域的中間位置
  • stretch: 內容寬度佔據整個網格區域空間(預設值)

justify-items屬性示例
上圖顯示了justify-items不同取值的結果,align-items與justify-items效果一樣,只是作用的方向不同。

4.2. place-items

place-items屬性是align-items屬性和justify-items屬性的合併簡寫形式。
語法是 place-items: <align-items> <justify-items>;
如果省略第二個值,則瀏覽器認為與第一個值相等。

.container{
   display: grid;
   grid-template-columns: repeat(auto-fit, 200px);
   grid-template-rows: auto auto;
   place-items: center center;
}

有了這個屬性,水平垂直居中就很容易了。

5.1. justify-content和align-content

justify-content定義了網格容器中,網格區域在水平方向上的對齊方式,align-content定義了網格區域在豎直方向上的對齊方式。

屬性值比items要多:

  • start:網格區域與容器的左端對齊
  • end: 網格區域與容器的右端對齊
  • center: 網格區域位於容器的中間位置
  • stretch: 網格區域佔據整個容器空間(預設值)
  • space-around:網格項之間設定等寬的間隙,與容器邊框的間隙是項之間間隙的一半
  • space-between: 網格項之間設定等寬的間隙,與容器邊框沒有間隙
  • space-evenly:網格項之間和與容器邊框設定等寬的間隙

justify-content屬性示例

與items一樣,上圖顯示了justify-content不同取值的結果,align-content與justify-content效果一樣,只是作用的方向不同。

5.2. place-content

place-content屬性是align-content屬性和justify-content屬性的合併簡寫形式。
語法是 place-content: <align-content> <justify-content>;
如果省略第二個值,則瀏覽器認為與第一個值相等。

.container{
   display: grid;
   grid-template-columns: repeat(auto-fit, 200px);
   grid-template-rows: 100px 100px;
   place-content: space-around space-evenly;
}

6. grid-auto-columns和grid-auto-rows

指定自動生成的網格軌道的大小。

當定義的網格不夠容納所有網格項,多出的網格項另起一行排列,或者給某一個網格項指定到定義好的網格區域的外部,這個時候,瀏覽器會自動生成多餘的網格,以便放置專案。

grid-auto-columns和grid-auto-rows就可以用來定義這些自動生成的多餘的網格的大小。

.container{
   width: 500px;
   height: 400px;
   display: grid;
   grid-template-columns: 40px auto 50px 40px;
   grid-template-rows: 200px auto;
   grid-auto-columns: 50px;
   grid-gap: 5px;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

如上面的程式碼所示,定義的網格是2行4列一共8個單元格,但是網格項有10個,所以最終生成的網格是3行4列,grid-auto-columns: 50px;讓最後一行的行高為50px。

grid-auto-columns和grid-auto-rows的預設值為auto

7. grid-auto-flow

屬性控制著自動佈局演算法怎樣運作,精確指定在網格中被自動佈局的元素怎樣排列。
語法為 grid-auto-flow: [ row | column ] || dense

屬性值

  • row: 指定自動佈局演算法按照通過逐行填充來排列元素,在必要時增加新行。(預設值)
  • column:指定自動佈局演算法通過逐列填充來排列元素,在必要時增加新列。
  • dense: 指定自動佈局演算法使用一種“稠密”堆積演算法,如果後面出現了稍小的元素,則會試圖去填充網格中前面留下的空白。

dense的意思即填充元素的時候,輪到一個元素了,發現這個格子放不下了,這個元素就被放到了新一行,如果沒有dense的話這個格子就空著了,加上dense的話,這個格子就會找其它能放下的元素放在這裡。
這樣做會填上稍大元素留下的空白,但同時也可能導致原來出現的次序被打亂。

博主不建議使用隱式的網格,即不建議使子元素超出定義的網格區域,也不建議使用grid-auto-flow,尤其dense屬性值,會讓佈局變得難以控制。

專案屬性

1.1. grid-column-start和grid-column-end和grid-row-start和grid-row-end

這四個屬性用來指定網格項在網格中的位置。從意思上就很明確。

  • grid-column-start:專案左邊框的位置
  • grid-column-end:專案右邊框的位置
  • grid-row-start:專案上邊框的位置
  • grid-row-end:專案下邊框的位置

可以指定的有網格線數字(1、2、3...的順序)、網格線名稱(之前命過名的話)、span(跨越)

語法為: <name> | <number> | span <number>

.container{
  width: 600px;
  height: 400px;
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-auto-rows: 80px 1fr 1fr;
  grid-gap: 10px;
}
.item {
  background: skyblue;
}
.toolbar{
  grid-column-start: 1;
  grid-column-end: 3;
}
.tree{
  grid-row-start: 2;
  grid-row-end: 4;
}
<div class="container">
  <div class="item toolbar">toolbar</div>
  <div class="item tree">tree</div>
  <div class="item table">table</div>
  <div class="item subtable">subtable</div>
</div>

上面的程式碼所示的就是一個典型的佈局,通過定義toolbar元素和tree元素的開始結束位置,達到佔用多個單元的目的。

使用span關鍵字也可以實現,span表示"跨越",即左右邊框(上下邊框)之間跨越多少個網格。

.toolbar{
  grid-column-start: 1;
  grid-column-end: span 2;
}
.tree{
  grid-row-start: 2;
  grid-row-end: span 2;
}

1.2. grid-row和grid-column

grid-row是grid-row-start和grid-row-end的簡寫形式。
grid-column是grid-column-start和grid-column-end的簡寫形式。

語法為:<start-line> / <end-line>

.toolbar{
  grid-column: 1 / span 2;
}
.tree{
  grid-row-start: 2 / span 2;
}

2. grid-area

grid-area屬性指定專案放在哪一個區域。

我們肯定還記得前面的這個屬性grid-template-areas,給容器劃分割槽域並命名,命名完成以後,使用grid-area屬性就可以指定專案放到哪裡了。

還是剛才那個典型佈局,這次我們用grid-area屬性來試試。

.container{
  width: 600px;
  height: 400px;
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-auto-rows: 80px 1fr 1fr;
  grid-gap: 10px;
  grid-template-areas:
    "toolbar toolbar"
    "tree table"
    "tree subtable";
}
.item {
  background: skyblue;
}
.toolbar{
  grid-area: toolbar;
}
.tree{
  grid-area: tree;
}
<div class="container">
  <div class="item toolbar">toolbar</div>
  <div class="item tree">tree</div>
  <div class="item table">table</div>
  <div class="item subtable">subtable</div>
</div>

可以看到,效果跟剛才是一樣的。

我們需要注意的是:

  • 我們給容器劃分割槽域,但是網格是不變的。
  • 如果多個專案放在同一個位置,這些專案會重疊,可以使用z-index指定哪個在上面顯示。使用這個功能實現選項卡的切換也很方便。

3.1 justify-self和align-self

justify-self設定元素中內容的水平對齊方式。align-self設定元素中內容的豎直對齊方式。

  • start:內容與網格項的左端對齊
  • end: 內容與網格項的右端對齊
  • center: 內容位於網格項的中間位置
  • stretch: 內容寬度佔據整個網格項空間(預設值)

3.2 place-self

place-self屬性是align-self屬性和justify-self屬性的合併簡寫形式。
語法為:<align-self> <justify-self>

.item {
  place-self: center center
}

如果省略第二個值,place-self屬性會認為這兩個值相等。

結束

以上就是grid佈局的簡單介紹~~

參考內容:
mdn api: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Grid_Layout
阮一峰CSS Grid 網格佈局教程:http://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html

相關文章