簡明 CSS Grid 佈局教程
網格佈局是由一系列水平及垂直的線構成的一種佈局模式,使用網格,我們能夠將設計元素進行排列,幫助我們設計一系列具有固定位置以及寬度的元素的頁面,使我們的網站頁面更加統一。
一個網格通常具有許多的**「列(column)與行(row)」**,以及行與行、列與列之間的間隙,這個間隙一般被稱爲**「溝槽(gutter)」**。
一、定義一個網格
我們可以將 display
屬性設爲 grid
來定義一個網格。與彈性盒子一樣,將父容器改爲網格佈局後,他的直接子項會變爲網格項。
.container {
display: grid;
}
1.1 設置列(column)與行(row)
grid-template-columns
用於設置列,grid-template-rows
用於設置行。
.container {
display: grid;
grid-template-columns: 100px 200px;
}
這裏我們創建了兩列,寬度分別爲 100px
和200px
。當然,這裏可以使用任何長度單位以及百分比。
grid-template
是 grid-template-rows
和 grid-template-columns
的簡寫,例如:grid-template: 50px 50px / 100px;
會創建兩個 50px 高的行以及一個 100px 寬的列。
1.1.1 使用 fr
單位
除了長度和百分比,我們也可以用fr
這個單位來靈活地定義網格的行與列的大小。這個單位表示了可用空間的一個比例,類似 flex 佈局的 flex-grow
/ flex-shrink
。
.container {
display: grid;
grid-template-columns: 1fr 1fr;
}
上面我們創建了等分的兩列。如果希望兩列的比例是 1:2,可以這麼設置:
.container {
display: grid;
grid-template-columns: 1fr 2fr;
}
另外,fr
可以與一般的長度單位混合使用,比如grid-template-columns: 100px 1fr 2fr
的結果就是第一列寬度是 100px,剩下的兩列會根據去掉 100px 後的可用空間按比例 1: 2 來分配。
1.1.2 重複設置列 / 行
我們可以使用repeat
函數來重複創建具有某些寬度配置的列。如果要創建多個等寬列,可以用這麼寫:
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
傳入repeat
函數的第一個參數表明了後續列寬配置要重複多少次,而第二個參數表示需要重複的配置,這個配置還可以具有多個長度設定,舉個例子:repeat(2, 100px 200px)
會得到這樣的效果:
1.1.3 自動填充
某些情況下,我們需要給網格創建很多列來填滿整個容器,而容器的寬度是可變的,也就沒辦法確定 repeat
的次數了,這時可以使用 repeat
函數中的關鍵字auto-fill
來實現這個效果。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 150px);
}
可以看到在 500px 寬度的容器上創建了三個 150px 的列,剩餘的 50px 不足以再創建一列,所以第四個元素就被放置到了第二行。
1.1.4 minmax()
函數
我們可以使用 minmax
函數設置一個範圍。
.container {
display: grid;
grid-template-columns: 100px minmax(100px, 300px) 100px;
}
1.2 網格間隙
使用column-gap
屬性來定義列間隙;使用row-gap
來定義行間隙;使用gap
可以同時設定兩者。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 150px);
gap: 10px 20px;
}
*gap
屬性曾經有一個grid-
前綴,不過後來的標準進行了修改,目的是讓他們能夠在不同的佈局方法中都能起作用。儘管現在這個前綴不會影響語義,但爲了代碼的健壯性,可以把兩個屬性都寫上。
另外,雖然 gap
屬性在 grid 佈局的兼容性挺好的,但在 flex
佈局的兼容性目前看起來還不行:
二、放置元素
2.1 基於線的放置元素
我們的網格中有許多的分隔線,我們可以根據這些分割線來放置元素:
-
grid-column-start
開始的列網格線 -
grid-column-end
結束的列網格線 -
grid-row-start
開始的行網格線 -
grid-row-end
結束的列網格線
我們還可以使用 grid-column
、grid-row
同時指定開始和結束的線。需要注意的是,開始和結束的線的序號要用 /
分開。
.container {
display: grid;
grid-template-columns: 100px 400px;
grid-template-rows: 50px 150px 50px;
gap: 10px
}
.header {
grid-column: 1 / 3;
}
.sidebar {
grid-column: 1 / 2;
}
.content {
grid-column: 2 / 3;
}
.footer {
grid-column: 1 / 3;
}
我們還可以使用負數來指定分隔線,-n
就代表倒數第n
條網格線。有時候我們不好確定列數,但又想定位到最後一列,這就可以考慮使用負數網格線了,例如上面的 header 可以這麼寫:
.header {
grid-column: 1 / -1;
}
2.1.1 使用 span
除了使用開始和結束的分隔線來指定位置,我們還可以使用 span
來指定元素佔的列數 / 行數:
.header {
grid-column: 1 / span 2;
}
// 這麼寫也行
.header {
grid-column: span 2 / -1;
}
效果跟上圖一樣。
2.1.2 使用 grid-area
我們還可以使用 grid-area
來一次性指定所有的行 / 列序號:<grid-row-start> / <grid-column-start> / <grid-row-end> / <grid-column-end>
,也就是先指定開始座標的行 / 列序號,再指定結束座標的行 / 列序號。
.header {
grid-area: 1 / 1 / 2 / 3;
}
2.2 使用 grid-tempate-areas
放置元素
另一種放置元素的方式是用grid-template-areas
屬性,並且要命名一些元素並在屬性中使用這些名字作爲一個區域。
grid-template-areas
屬性的使用規則如下:
-
需要填滿網格的每個格子
-
對於某個橫跨多個格子的元素,重複寫上那個元素
grid-area
屬性定義的區域名字 -
所有名字只能出現在一個連續的區域,不能在不同的位置出現
-
一個連續的區域必須是一個矩形
-
使用
.
符號,讓一個格子留空
.container {
display: grid;
grid-template-columns: 100px 400px;
grid-template-rows: 50px 150px 50px;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
gap: 10px
}
.header {
grid-area: header;
}
.sidebar {
grid-area: sidebar;
}
.content {
grid-area: content;
}
.footer {
grid-area: footer;
}
如果想把 sidebar 延伸到底部,只需要把 grid-template-areas
改成這樣:
.container {
// ...
grid-template-areas:
"header header"
"sidebar content"
"sidebar footer";
}
效果如下:
通過命名的方式來放置元素是一種非常直觀的方式,你在 CSS 中看到的就是實際會出現的排版效果了。
三、顯式網格與隱式網格
顯式網格是我們用grid-template-columns
或 grid-template-rows
屬性創建的,而隱式網格則是當「網格項被放到已定義的網格外」或「網格項的數量多於網格的數量」時纔會自動生成。
假設現在我們定義一個 1 行 x 2 列的寬高都爲 100px 的網格容器,並在其中放置了 a 和 b 兩個網格項:
如果我們把網格項 a 和 b 放置到已定義的網格之外的話:
.a {
grid-column: 3;
}
.b {
grid-row: 2;
}
可以看到比之前定義的網格多了一些,而這些多出來的的網格就是隱式網格。另外,不僅網格多了,網格線也多了,列網格線 4 以及行網格線 3 都是自動生成的隱式網格線。
3.1 給隱式網格設置大小
上圖的 a 和 b 有點區別是,網格 a 寬度自動鋪滿了容器,而網格 b 的高度則是內容的高度,這是默認行爲。我們可以給網格容器設置 grid-auto-rows
和 grid-auto-columns
屬性來指定隱式網格的大小:
.container {
grid-auto-rows: 100px;
grid-auto-columns: 100px;
}
現在隱式網格的大小也都是 100px * 100px 了。
3.2 自動放置
上面提過,當網格項的數量多於網格的數量時也會自動生成隱式網格,默認情況下元素會逐行放置,不夠空間的話再生成新的行。我們可以通過 grid-auto-flow
屬性來修改這個行爲。
例如現在有 3 x 3 的網格容器,a 、b 都佔兩列,默認情況下由於 b 在第一行不夠空間,最終會放到第二行,然後 c 在 b 後面。
如果修改成grid-auto-flow: column
,會逐列放置元素,此時 c 會被放在第三行:
如果修改成grid-auto-flow: dense
,則會在 row
的基礎上填充前面網格留下來的空白:
還有
column dense
之類的值,具體可以去看 MDN: grid-auto-flow
四、調整對齊方式
下面的例子都基於這個網格容器:
.container {
display: grid;
grid-template-columns: repeat(2, 100px);
grid-template-rows: repeat(2, 50px);
gap: 10px
}
4.1 justify-items
沿行軸對齊網格項。
start
end
center
4.2 align-items
沿列軸對齊網格項。
start
end
center
4.3 justify-content
如果網格容器的尺寸比整個網格內容的大,這時候就可以使用 justify-content
或 align-content
來調整網格內容的對齊了。具體就參考 flex 佈局吧。
start
end
center
space-between
space-around
space-evenly
4.4 align-content
參考上面的吧
五、其他
最近在實現一個兩欄佈局的時候用了 grid 佈局,但效果看起來有點 bug,這裏簡單分享一下。
首先假設 html 和 css 長這樣:
<div class="container">
<div></div>
<div>abcdefg</b>
</div>
.container {
display: grid;
grid-template-columns: 100px 1fr;
gap: 10px;
width: 150px;
border: 2px solid red;
}
在寬度 150px 的容器裏,我定義了兩列:100px
固定寬度和 1fr
鋪滿剩餘空間。其中第二列裏的內容是一串連續字符,由於沒有特意設置 work-bread
屬性,所以顯然第二列的內容會超出預期的寬度:
這種問題設置下 word-break: break-word
就好,但這是最簡單的情景,如果遇上了 pre,情況就會有點迷惑。假設 html 變成了這樣裏面是 pre:
<div class="container">
<div></div>
<div width=100>
<div overflow>
<pre>123456789</pre>
</div>
</div>
</div>
可以看到代碼塊溢出了。通常我們都是想 pre 代碼塊過長時可以左右滾動,那給 pre 的父元素加個 overflow: auto
是不是能解決問題呢?其實不能... 而如果給第二列加一個固定寬度,的確可以解決問題,但這就不是預期的 1fr
了。
其實只要給第二列加一個 min-width: 0
就能解決問題,在 grid 的配置裏的話就是可以把上面的 1fr
改成 minmax(0, 1fr)
...
參考文檔:
https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Grids
https://developer.mozilla.org/zh-CN/docs/Web/CSS/grid-auto-flow
https://css-tricks.com/snippets/css/complete-guide-grid/
https://css-tricks.com/difference-explicit-implicit-grids/
https://cssgridgarden.com/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/AUIGC7C_TYhDNg_ADlZ7Pg