CSS Grid網格布局全攻略


 

CSS Grid網格布局全攻略

所有奇技淫巧都只在方寸之間。

幾乎從我們踏入前端開發這個領域開始,就不停地接觸不同的布局技術。從常見的浮動到表格布局,再到如今大行其道的flex布局,css布局技術一直在不斷地推陳出新。其中網格布局(grid)作為css3的產物,它更加貼近網頁設計師所使用的布局策略,學習並利用好它可以讓我們免受很多布局困擾。

雖然網格布局好處有很多,但學習起來並不簡單,原因是用來設置布局的屬性實在太多,其中光是作用於父容器的屬性就有17種,再加上子元素屬性有10種,另外還有這些屬性值的不同取值方式。這些對於記憶來說絕對是個不小的負擔。那么這么多屬性以及用法,要如何在短時間內消化掉呢?在接下來這篇文章里,我將針對這27種屬性以及它們各自的用法,分享我獨家的學習策略,希望對大家的學習有所幫助。

布局之道

CSS作為一種網頁排版設計語言,其核心的設計思想必然要遵守相關的領域知識。網格布局是一種二維布局結構,它是由縱橫相交的兩組網格線形成的框架性布局結構。網頁設計者可以利用這些由行(row)和列(column)形成的框架性結構來布局設計元素。 在定義一種網格布局結構的時候,我們需要在父容器上描述要布局的主體框架結構。為了描述這一框架結構,我們就需要給它的基本構成元素命名。一個網格布局的構成元素可以概括為以下幾種概念:

  • row line: 行線
  • column line: 列線
  • track: 網格軌道,即行線和行線,或列線和列線之間所形成的區域,用來擺放子元素
  • gap: 網格間距,行線和行線,或列線和列線之間所形成的不可利用的區域,用來分隔元素
  • cell: 網格單元格,由行線和列線所分隔出來的區域,用來擺放子元素
  • area: 網格區域,由單個或多個網格單元格組成,用來擺放子元素grid基本概念

牢記上述這些概念是之后熟練掌握和應用網格布局的基礎。

構建之法

要熟練掌握一門技術,核心是找到最基本的套路,然后不斷練習從而可以在之后的實踐過程中減少決策的時間。所以,這一部分主要就是介紹網格布局構建過程中的一些常用套路。 這里我們要解決的問題是,如何利用最基本的規則來構建出理想的布局模型。在布局過程中,歸根結底需要處理的就兩種頁面元素:父容器和子元素。前者主要用來設置基礎的布局框架,相當於建築中的設計藍圖,而后者就是用來進行個性化的布局調整。因此我個人歸納了在使用網格布局過程中的套路是:針對父容器元素進行設置需要三個步驟:定框架、設間隔和找對齊,對子元素來說有兩個步驟:擺位置和找對齊。我把它們統稱為**"32構建之法"**。

在這一小節中,我將把重心主要放在網格布局中所有用到的27個屬性名的講解上,而取值邏輯將在最后一部分進行統一介紹。

父容器

定框架

設置父容器的網格布局的第一步就是將父容器的盒模型設置為grid,這樣就能觸發渲染引擎的網格布局算法。

.parent { display: grid; } 

接着我們要開始准備**"畫線"**,即設置所需行和列的基礎線。這些線條將構成我們接下來進行布局排布的基礎模板(template)。在畫線過程中,我們需要分別根據行(row)和列(column)兩個維度進行設置。你需要畫幾條線,就設置幾個值(不包括邊框),其取值是軌道(track)的大小。這里我先畫出一個3x3的網格框架,代碼如下:

.parent { display: grid; grid-template-rows: 100px 100px 100px; grid-template-columns: 100px 100px 100px; } 

在這里你也可以選擇使用縮寫形式同時為行和列設置值,采用/分隔開:

.parent { display: grid; grid-template: 100px 100px 100px / 100px 100px 100px; } 

 

image.png

 

畫完線后,下一步我們可以選擇為這些線條和線條之間形成的網格區域(area)進行命名,這樣在后續使用的時候就能直接使用這些名字,便於子元素的定位。

.parent { display: grid; grid-template-areas: "a a b" "c d e" "c d ." } 

上面這兩步畫線和命名同樣可以采用縮寫形式進行設置,代碼如下:

.parent { display: grid; grid-template: "a a b" 101px "c d e" 102px "c d ." 103px / 104px 105px 105px } 

因為使用grid-template同時設置行列和區域名的寫法比較復雜,為了講解方便,我把值設置成規律的遞增數字。其中(101, 102,103)設置的是grid-template-rows的值,而(104,105,106)設置的則是grid-template-columns的值。

 

image.png

 

到這一步,我們可以說已經完成所需工作的一大半了。

設間隔

間距(gap)的設置在實際開發中是可選的,主要根據網頁設計的需求而定。如果你需要給網格線之間設置間距,我們可以在行列兩個維度上分別進行設置, 下面這段代碼將給每個行和列分別設置10px的間隔:

.parent { display: grid; grid-template: 100px 100px 100px / 100px 100px 100px; grid-row-gap: 10px; grid-column-gap: 10px; } 

如果采用縮寫形式,上述代碼又可以簡化成:

.parent { display: grid; grid-template: 100px 100px 100px / 100px 100px 100px; grid-gap: 10px 10px; } 

設置后的效果如下:

 

 

找對齊

有了前面兩個步驟,我們的網格布局框架基本上算是搭建得差不多了。每個子元素都會默認占據一個網格區域。而在父容器這里我們如果有需要,就要進行最后一個步驟:找對齊。所謂對齊方式,可以分為內部和外部兩種(前者是針對每個網格區域的子元素而言,而后者是相對於網格區域本身)。另外在行和列(更專業的術語是main axis和cross axis)上又各自有兩個維度,這就構成了4種設置對齊的方式。

先來處理一下每個子元素相對網格區域內部的對齊方式:

.parent { display: grid; grid-template: 100px 100px 100px / 100px 100px 100px; grid-gap: 10px 10px; justify-items: center; align-items: center; } 

在上面的代碼中我分別在行和列方向上都設置了居中對齊,這樣每個網格區域中的子元素相對於各自的區域行為是一致的,都能均勻排布。可以看到效果如下圖所示:

 

image.png

 

再來看一下另一種情況:

.parent { display: grid; width: 500px; height: 500px; grid-template: 100px 100px 100px / 100px 100px 100px; grid-gap: 10px 10px; justify-content: space-between; align-content: center; } 

有時候我們設置的網格不足以覆蓋整個父容器的大小時,比如在上述的例子中整個父容器有500px*500px的大小,而我們只設置了300px*300px的網格區域,這時候就需要指定多出來空間的處理規則。justify-contentalign-content就是分別在行和列兩個方向上用來解決這個問題的屬性。它們將針對每個網格區域去設置其在父容器中的對齊方式。

 

image.png

 

justifyalign這兩個單詞在方向上比較容易搞混,所以我在記憶上采取的方式是記住justifyrowaligncolumn這兩個合並詞,它們長度差不多。如果你有更好的記憶方式,請留言告訴我。

子元素

我們通過在父容器上搭建好了基礎的框架后,對於大部分子元素來說,就已經能夠很好地滿足布局要求了。針對部分子元素,可以根據需求進行微調。如果要在子元素上進行布局微調,通常需要以下兩個步驟:擺位置和找對齊。

擺位置

像下棋一樣,針對子元素的排布,我們需要給它們指定要擺放的具體位置。要確定具體位置,可以利用之前在父容器中所指定的線名和區域名來定位。一種方式是直接通過設置起始行,結束行和起始列,結束列來給子元素划定它所要擺放的區域,另外一種方式是指定要擺放的區域名。

/* 指定起始行,結束行,起始列,結束列 */ .child:first-child { grid-row-start: 1; grid-row-end: 2; grid-column-start: 1; grid-column-end: 3; background: red; } /* 使用縮寫形式 */ .child:nth-child(2) { grid-row: 2/3; grid-column: 2/4; background: yellow; } /* 直接指定區域名 */ .child:nth-child(3) { grid-area: i; background: green; } 

這段代碼的效果如下:

image.png

 

還有一種更加靈活的設置位置方式,是指定跨越的行數和列數,關鍵字span用來控制一次跨越的行數或列數, 如上面第三個子元素可以改寫成:

.child-nth-child(3) {
  grid-row: 2/3;
  grid-column: span 2;
}

找對齊

和父容器中所設置的對齊方式類似,針對個別子元素的對齊處理,我們可以按照行列兩組屬性進行分別處理:

/* 列對齊 */ .child:nth-child(1) { align-self: end; } /* 行對齊 */ .child:nth-child(2) { justify-self: end; } /* 采用縮寫形式 */ .child:nth-child(3) { place-self: center center; } 

 

image.png

 

*隱式網格

靈活性是網格布局的一大優勢,除了采用上述那種手動指定框架結構的方式,網格布局還有一套自動化布局的機制,這套機制稱為**“隱式網格布局”**。當我們在網格定義的區域外放置子元素時,或因子元素數量過多而需要更多的網格線時,布局算法就會自動生成隱式網格。默認情況下這些隱式網格的大小也會隨着內容尺寸不同而變化,而我們可以利用屬性grid-auto-rowsgrid-auto-columns來控制隱式網格的大小。 考慮下面這個例子:

<div class="parent"> <div class="child" style="background: red"></div> <div class="child" style="background: yellow"></div> <div class="child" style="background: green"></div> </div> 
.parent { display: grid; grid-auto-rows: 100px; grid-auto-columns: 100px; } 

通過手動在父容器中設置隱式網格大小為100x100的大小后,效果如下:

 

image.png
如果子元素引用了不存在的行號和列號,父容器會自動生成隱式網格以容納所有子元素:

 

.child:first-child {
    grid-row-start: 1;
    grid-row-end: 2;
    grid-column-start: 1;
    grid-column-end: 3;
    background: red;
}

 

image.png

 

有了網格大小的控制,我們還需要位置的控制。默認情況下,子元素都是先將行填充滿,容器大小不夠的時候才會生成新的隱式行。如果要改變這一默認行為,我們需要使用grid-auto-flow屬性來控制:

.parent { display: grid; grid-auto-rows: 100px; grid-auto-columns: 100px; grid-template-areas: "a b c" "d e f" "g h i"; grid-auto-flow: column; }  
image.png

 

取值之術

介紹完所有的網格布局屬性后,我們再來說一下各種屬性的取值策略。

大小

在CSS中,我們通常使用px,em等取值單位進行屬性大小的設置,對於靈活的布局需求來說,百分比也是常用的取值單位。這些單位在平常工作中似乎已經足夠用了。不過,為了讓布局能夠更加靈活,網格布局中引入了一種新單位fr,它是fraction這個單詞的縮寫,意思是容器內剩余空間的分數比。考慮下面這個例子:

.parent {
  height: 100px;
  display: grid;
  grid-template-columns: 100px 1fr 100px;
}

我們通過設置100px 1fr 100px的布局框架,從而很輕松地就實現了兩邊寬度固定,中間自適應的效果。
image.png

 

如果要實現有比例關系的布局結構,還可以使用多個fr的取值:

.parent { width: 400px; height: 100px; display: grid; grid-template-rows: 100px 1fr 100px; grid-template-columns: 1fr 1fr 2fr; grid-template-areas: "a b c" } 

可以看到區域a,b,c之間的比例關系就是"1:1:2"的關系。

image.png

 

除了上述這個支持自適應的單位外,網格布局中還能夠使用max-contentmin-content這組關鍵字來達到自適應的目的。要理解這兩個關鍵字,首先需要理解內在尺寸(intrinsic size)和外部尺寸(extrinsic size)這兩個概念。先說一下extrinsic size,它的相對值計算是相對於父容器對應的屬性值。我們知道,width如果使用百分比單位,其計算值是相對於該元素所在的容器寬度的,比如父容器寬度100px, 子元素設置width: 20%,那么它的寬度就是100px * 20% = 20px。在css3中引入了intrinsic size則是相對於元素自身尺寸進行計算。max-contentmin-content就是相對於元素自身內容塊進行計算的屬性值。

min-content顧名思義是根據元素內容來設置的最小寬度大小,在英文句子中,通常是最長單詞的那個長度,而中文中則是一個字的長度。比如下面這個例子:

.parent { display: grid; grid-template-columns: auto min-content auto; } 

 

image.png

 

可以看到,中間那個網格的那個寬度就等於scq000這個單詞的長度。

min-content相對應,max-content會將尺寸設置成內容尺寸能達到的最大寬度。我們把代碼改成下面這樣:

.parent { display: grid; grid-template-columns: auto max-content auto; } 

 

image.png

 

有了這兩個屬性值,我們可以很容易地讓布局區域根據內容進行自適應。

函數

函數是用來避免重復性工作的一種有效工具,在網格布局中提供了一些常用CSS函數來方便我們的工作。

第一個要介紹的是minmax這個函數。在設置網格框架的過程中,對於自適應的網格區域,我們都會設置一個最小值和最大值,這個函數就是用來實現這個目的的。

.parent { display: grid; grid-template-columns: 100px minmax(100px, 200px) 100px; } /* 最常用的情況是只設置最小,不設置最大值 */ .parent { display: grid; grid-template-columns: 100px minmax(100px, auto) 100px; } 

利用這個函數設置的網格布局可以做到很好的自適應,在頁面伸縮過程中也能保證布局的穩定性。

另一個很有用的函數是fit-content,它實際上是min(maximum size, max(minimum size, argument))的簡寫,表示將元素寬度收縮到內容寬度。說得通俗點就是,使用這個函數后會盡量不占用多余的空間。如果內容的寬度小於fit-content中設置的長度,那么實際子元素寬度是內容寬度。如果內容寬度超出了fit-content中設置的長度,那么實際子元素寬度就是設置的那個長度。下面看兩個例子:

.parent { display: grid; grid-template-rows: auto fit-content(200px) auto; } 

 

image.png

 

 

image.png

 

第一個句子中的長度超出了200px,那么此時中間網格的寬度是200px。而第二個例子中內容寬度不足200px,此時中間網格的寬度是句子實際占用的寬度。

最后要介紹的是repeat函數,它主要用來批量設置框架的間距,這個函數接受兩個參數,第一個參數控制循環次數,第二個參數控制間距大小。讓我們用這個函數改寫一下上述例子:

.parent { display: grid; grid-template-rows: 100px 100px 100px; grid-template-columns: 100px 100px 100px; } /* 利用repeat函數改寫 */ .parent { display: grid; grid-template-rows: repeat(3, 100px); grid-template-columns: repeat(3, 100px); } 

另外,第一個參數除了可以使用數字顯示設置網格數量外,還能使用auto-fitauto-fill兩個關鍵字自動分配空間。通常情況下,這兩個關鍵字的使用效果都差不多,唯一的差別是空余空間的分配規則。搭配minmax函數可以看出區別,如下面這兩個例子:

.parent { display: grid; width: 500px; height: 100px; grid-gap: 10px; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); } .parent { display: grid; width: 500px; height: 100px; grid-gap: 10px; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); }  
auto-fit

 

 

image.png

 

在單行布局的時候,如果有空余空間auto-fit會將它們平均分配到所有子元素中,而auto-fill會自動創建空白的列。

命名

我們在創建網格布局框架的時候,通過"畫線"來指定基本布局結構。默認情況下,網格布局會給每一條網格線進行命名,命名順序同書寫順序一致:從左到右,由上至下按數字命名。假設我們指定的是3x3的網格布局結構,那么包含邊框線,就會生成4+4=8條線。

image.png
除了采用默認命名方式,我們還能夠自定義網格線的名稱以便於后續在子元素定位中使用。

 

.parent { display: grid; grid-template-rows: [row-a] 100px [row-b] 100px [row-c] 100px [row-d]; } 

對齊

對齊是布局過程中一個不可缺少的步驟,它的取值是通過已有的關鍵字來指定的。其中用於網格布局中對齊的關鍵字有start,centerendstretch四個。 我們先從默認值stretch看起,這個關鍵字是伸展的意思,所以在默認情況下網格中的子元素會盡可能地填充滿網格區域,由於是默認值所以平常寫代碼的時候就不太會可以去用這個關鍵字進行聲明。 接着再來看一下常用的對齊取值策略,只需要記住一點就可以了:用屬性justify-*,align-*來分別控制橫軸和縱軸兩個方向,屬性值控制其對齊位置。 startcenterend三個屬性值就分別對應了前中后三個位置。

.parent { display: grid; } .child:first-child { justify-self: start; } .child:first-child { justify-self: center; } .child:first-child { justify-self: end; } 

*排列

讓我們重新回到隱式網格那部分,隱式的排列規則是通過指定grid-auto-flow這個屬性來設置的。它的取值只有三個rowcolumndense。上面屬性部分已經介紹過rowcolumn兩個屬性值,前者是按行優先來擺放子元素,后者是按列優先來擺放子元素。這里主要介紹一下dense這個屬性值。通常情況下,排列子元素會按照順序填充行或列,如果空間不足的時候,會換行或換列。而使用了dense屬性后,會盡量使用空余空間,考慮下面這段代碼:

.parent { display: grid; grid-template-columns: repeat(3, 100px); } 
<div class="parent"> <div class="child">1</div> <div class="child">2</div> <div class="child">3</div> <div class="child">4</div> <div class="child">5</div> </div> 

默認情況下,顯示效果是這樣的:

image.png

 

當我們使用dense值進行排列的時候,就相當於開啟“緊湊”模式,會盡可能利用空余空間。

.parent { display: grid; grid-auto-flow: row dense; /* 由於默認為按行排列,可省略為dense */ grid-auto-flow: dense; } 

所以,顯示效果如下:

image.png

 

總結

在這篇文章里,我通過拆解分類所有網格布局相關的知識點,希望能夠為大家提供一個比較系統的運用網格布局的指導方法。在實際應用過程中,沿着"32構建之法”的這個套路來走,可以節約很多思考和決策時間。另外,由於這篇文章信息密度可能比較大,希望大家能夠多多復習,並跟着例子實際操練幾遍,這樣在實際工作運用中才能如魚得水。最后,送上一張思維導圖,幫助大家能夠一覽本文所有的重點。

 

image.png

 

推薦閱讀

grid.malven.co/

www.w3.org/TR/css-alig…

medium.com/@patrickbro…

www.zhangxinxu.com/wordpress/2…

css-tricks.com/difference-…

畫圖調試工具: www.mozilla.org/en-US/firef…

——本文首發於個人公眾號,轉載請注明出處———

最后,歡迎大家關注我的公眾號,一起學習交流。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM