Grid 布局
什么是 grid 布局
flex 布局是 一维布局 模型,当面对一些场景时无法满足要求,比如下面这种情况,会出现最后一行无法左侧对齐的场景。
<div class="flex">
<p>p1</p>
<p>p2</p>
<p>p3</p>
<p>p4</p>
<p>p5</p>
<p>p6</p>
<p>p7</p>
<p>p8</p>
</div>
.flex {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
width: 400px;
height: 100px;
border: 1px solid #eee;
padding: 10px;
}
p {
box-sizing: border-box;
width: 100px;
border: 1px solid #d9d9d9;
margin: 0 10px;
}
此时就需要使用 grid 布局,先看具体效果。
<div class="grid">
<p>p1</p>
<p>p2</p>
<p>p3</p>
<p>p4</p>
<p>p5</p>
<p>p6</p>
<p>p7</p>
<p>p8</p>
</div>
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
width: 400px;
height: 100px;
border: 1px solid #eee;
padding: 10px;
}
p {
box-sizing: border-box;
width: 100px;
border: 1px solid #d9d9d9;
margin: 0 10px;
}
基本概念
容器和项目
和 flex 一样,采用网格布局的区域,被称为容器,其中的子元素称为项目。
行、列、间距
一个网格通常具有许多的列(column)与行(row),以及行与行、列与列之间的间隙,这个间隙一般被称为间距(gutter)。
单元格
行和列的交叉区域就是单元格,正常情况下,n 行和 m 列会产生 n x m 个单元格。比如,3 行 3 列会产生 9 个单元格。
网格线
划分网格的线,称为"网格线"(grid line)。水平网格线划分出行,垂直网格线划分出列。
正常情况下,n 行有 n + 1 根水平网格线,m 列有 m + 1 根垂直网格线,比如三行就有四根水平网格线。
容器属性
display
display: grid
指定一个容器采用网格布局。
<span>foo</span>
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
<span>bar</span>
span {
font-size: 2em;
}
#container {
display: grid;
grid-template-columns: 50px 50px 50px;
grid-template-rows: 50px 50px 50px;
}
.item {
font-size: 2em;
text-align: center;
border: 1px solid #e5e4e9;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
默认情况下,容器元素都是块级元素,但是也可以设置为行内元素 display: inline-grid
。
<span>foo</span>
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
<span>bar</span>
span {
font-size: 2em;
}
#container {
display: inline-grid;
grid-template-columns: 50px 50px 50px;
grid-template-rows: 50px 50px 50px;
}
.item {
font-size: 2em;
text-align: center;
border: 1px solid #e5e4e9;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
注意
注意,设为网格布局以后,容器子元素(项目)的 float
、display: inline-block
、display: table-cell
、vertical-align
和 column-\*
等设置都将失效。
grid-template-columns
和 grid-template-rows
容器指定了网格布局后,需要设置行和列信息。grid-template-columns
属性定义每一列的列宽,grid-template-rows
属性定义每一行的行高。
#container {
display: grid;
grid-template-columns: 50px 50px 50px;
grid-template-rows: 50px 50px 50px;
}
上述代码指定了一个 3 x 3 的网格,行高列高都是 50px。
repeat()
一般情况下,可以使用 repeat()
函数避免重复书写同样的值。
.container {
display: grid;
grid-template-columns: repeat(3, 50px);
grid-template-rows: repeat(3, 50px);
}
repeat()
接受两个参数,第一个参数是重复的次数(上例是 3),第二个参数是所要重复的值。
repeat()
重复某种模式也是可以的,下面代码表示定义了 6 列,第一列和第四列的宽度为 100px
,第二列和第五列为 20px
,第三列和第六列为 80px
。
grid-template-columns: repeat(2, 100px 20px 80px);
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
#container {
display: grid;
grid-template-columns: repeat(2, 100px 20px 80px);
grid-template-rows: repeat(2, 100px);
}
.item {
font-size: 4em;
text-align: center;
border: 1px solid #e5e4e9;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
auto-fill
有时,单元格的大小是固定的,但是容器的大小不确定。如果希望每一行(或每一列)容纳尽可能多的单元格,这时可以使用 auto-fill
关键字表示自动填充。
注意
非常有用,开发中经常出现这种情况。
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
#container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
}
.item {
font-size: 4em;
text-align: center;
border: 1px solid #e5e4e9;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
除了 auto-fill
,还有一个关键字 auto-fit
,两者的行为基本是相同的。只有当容器足够宽,可以在一行容纳所有单元格,并且单元格宽度不固定的时候,才会有 行为差异:auto-fill
会用空格子填满剩余宽度,auto-fit
则会尽量扩大单元格的宽度。
fr
为了方便表示比例关系,网格布局提供了 fr
关键字(fraction 的缩写,意为"片段")。如果两列的宽度分别为 1fr
和 2fr
,就表示后者是前者的两倍。
<div class="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
.container {
display: grid;
grid-template-columns: 1fr 1fr;
}
.item {
font-size: 4em;
text-align: center;
border: 1px solid #e5e4e9;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
fr
可以与绝对长度的单位结合使用,这时会非常方便,grid-template-columns: 150px 1fr 2fr;
表示第一列的宽度为 150 像素,第二列的宽度是第三列的一半。
<div class="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
.container {
display: grid;
grid-template-columns: 150px 1fr 2fr;
}
.item {
font-size: 4em;
text-align: center;
border: 1px solid #e5e4e9;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
minmax()
minmax()
函数产生一个长度范围,表示长度就在这个范围之中。它接受两个参数,分别为最小值和最大值。
grid-template-columns: 1fr 1fr minmax(100px, 1fr);
上面代码中,minmax(100px, 1fr)
表示列宽不小于 100px
,不大于 1fr
。
布局实例
grid-template-columns
属性对于网页布局非常有用。两栏式布局只需要一行代码。
.wrapper {
display: grid;
grid-template-columns: 70% 30%;
}
上面代码将左边栏设为 70%,右边栏设为 30%。
传统的十二网格布局,写起来也很容易。
grid-template-columns: repeat(12, 1fr);
grid-row-gap
grid-column-gap
grid-gap
控制了行和列,接下来还有行列之间的间距需要控制。
grid-row-gap
属性设置行与行的间隔(行间距),grid-column-gap
属性设置列与列的间隔(列间距)。
.container {
grid-row-gap: 20px;
grid-column-gap: 20px;
}
grid-gap
属性是 grid-column-gap
和 grid-row-gap
的合并简写形式,语法如下。
grid-gap: <grid-row-gap> <grid-column-gap>;
因此,上面一段 CSS 代码等同于下面的代码。
.container {
grid-gap: 20px 20px;
}
如果 grid-gap
省略了第二个值,浏览器认为第二个值等于第一个值。
注意
根据最新标准,上面三个属性名的 grid-
前缀已经删除,grid-column-gap
和 grid-row-gap
写成 column-gap
和 row-gap
,grid-gap
写成 gap
。
间隙距离可以用任何长度单位包括百分比来表示,但不能使用 fr
单位。
grid-template-areas
定义好网格后(行、列、间距),需要设定网格中放置元素的规则。
注意
可以使用 网格线 放置元素内容,但是该方式不太直观,因此推荐使用网格区域放置元素。
.container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
grid-template-areas:
'a b c'
'd e f'
'g h i';
}
上面代码先划分出 9 个单元格,然后将其定名为 a
到 i
的九个区域,分别对应这九个单元格。
如果某些区域不需要利用,则使用"点" .
表示。
grid-template-areas:
'a . c'
'd . f'
'g . i';
上面代码中,中间一列为点,表示没有用到该单元格,或者该单元格不属于任何区域。
下面看一个实例,太强大了有没有。
<div class="container">
<header>This is my lovely blog</header>
<article>
<h1>My article</h1>
<p>
Duis felis orci, pulvinar id metus ut, rutrum luctus orci. Cras porttitor
imperdiet nunc, at ultricies tellus laoreet sit amet. Sed auctor cursus
massa at porta. Integer ligula ipsum, tristique sit amet orci vel, viverra
egestas ligula. Curabitur vehicula tellus neque, ac ornare ex malesuada
et. In vitae convallis lacus. Aliquam erat volutpat. Suspendisse ac
imperdiet turpis. Aenean finibus sollicitudin eros pharetra congue. Duis
ornare egestas augue ut luctus. Proin blandit quam nec lacus varius
commodo et a urna. Ut id ornare felis, eget fermentum sapien.
</p>
<p>
Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
est. Nam id risus quis ante semper consectetur eget aliquam lorem. Vivamus
tristique elit dolor, sed pretium metus suscipit vel. Mauris ultricies
lectus sed lobortis finibus. Vivamus eu urna eget velit cursus viverra
quis vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
</p>
</article>
<aside>
<h2>Other things</h2>
<p>
Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
est.
</p>
</aside>
<footer>Contact me@mysite.com</footer>
</div>
.container {
display: grid;
grid-template-columns: 1fr 3fr;
/* 定义网格区域 */
grid-template-areas:
'header header'
'sidebar content'
'footer footer';
gap: 20px;
}
header,
footer {
border-radius: 5px;
padding: 10px;
background-color: rgb(207, 232, 220);
border: 2px solid rgb(79, 185, 227);
}
aside {
border-right: 1px solid #999;
}
/* 基于网格区域放置元素 */
header {
grid-area: header;
}
article {
grid-area: content;
}
aside {
grid-area: sidebar;
}
footer {
grid-area: footer;
}
/* 基于网格区域放置元素 */
再来看一个更复杂的例子。
<div class="grid">
<div class="item1">One</div>
<div class="item2">Two</div>
</div>
.grid > * {
border-radius: 0.5em;
color: #fff;
padding: 0.5em;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 100px 100px 100px;
grid-template-areas:
'a . . .'
'. b c .'
'. . . d';
gap: 10px;
}
.item1 {
grid-area: a / a / c / c;
background-color: rgb(74 102 112 / 70%);
border: 5px solid rgb(74 102 112 / 100%);
}
.item2 {
grid-area: b / b / d / d;
background-color: rgb(214 162 173 / 70%);
border: 5px solid rgb(214 162 173 / 100%);
}
justify-items
align-items
place-items
justify-items
设置单元格内容水平位置,align-items
设置单元格垂直方向位置。
.container {
justify-items: start | end | center | stretch;
align-items: start | end | center | stretch;
}
这两个属性的写法完全相同,都可以取下面这些值。
start
:对齐单元格的起始边缘。end
:对齐单元格的结束边缘。center
:单元格内部居中。stretch
:拉伸,占满单元格的整个宽度(默认值)。
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
#container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
justify-items: center;
align-items: center;
}
.item {
/* width: 50px;
height: 50px; */
font-size: 2em;
text-align: center;
border: 1px solid black;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
可以使用检查器查看排列状态。
place-items
属性是 align-items
属性和 justify-items
属性的合并简写形式。
place-items: <align-items> <justify-items>;
下面是一个例子。
place-items: start end;
如果省略第二个值,则浏览器认为与第一个值相等。
项目属性
grid-column-start
grid-column-end
grid-row-start
grid-row-end
项目的位置是可以指定的,具体方法就是指定项目的四个边框,分别定位在哪根网格线。
grid-column-start
属性:左边框所在的垂直网格线grid-column-end
属性:右边框所在的垂直网格线grid-row-start
属性:上边框所在的水平网格线grid-row-end
属性:下边框所在的水平网格线
grid-column
属性是 grid-column-start
和 grid-column-end
的合并简写形式,grid-row
属性是 grid-row-start
属性和 grid-row-end
的合并简写形式。
.item {
grid-column: <start-line> / <end-line>;
grid-row: <start-line> / <end-line>;
}
<div class="grid">
<div class="item1">One</div>
<div class="item2">Two</div>
</div>
.grid > * {
border-radius: 0.5em;
color: #fff;
padding: 0.5em;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 100px 100px 100px;
gap: 10px;
}
.item1 {
grid-column: 1 / 4;
grid-row: 1 / 3;
background-color: rgb(74 102 112 / 70%);
border: 5px solid rgb(74 102 112 / 100%);
}
.item2 {
grid-column: 2 / 5;
grid-row: 2 / 4;
background-color: rgb(214 162 173 / 70%);
border: 5px solid rgb(214 162 173 / 100%);
}
提示
项目重叠可以使用 z-index
调整顺序。
grid-area
grid-area
属性指定项目放在哪一个区域。如上面的 基于网格区域放置元素 - 2。
grid-area
属性还可用作 grid-row-start
、grid-column-start
、grid-row-end
、grid-column-end
的合并简写形式,直接指定项目的位置。
.item {
grid-area: <row-start> / <column-start> / <row-end> / <column-end>;
}
下面是一个例子。
<div class="grid">
<div class="item1">One</div>
<div class="item2">Two</div>
</div>
.grid > * {
border-radius: 0.5em;
color: #fff;
padding: 0.5em;
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 100px 100px 100px;
gap: 10px;
}
.item1 {
grid-area: 1 / 1 / 3 / 4;
background-color: rgb(74 102 112 / 70%);
border: 5px solid rgb(74 102 112 / 100%);
}
.item2 {
grid-area: 2 / 2 / 4 / 5;
background-color: rgb(214 162 173 / 70%);
border: 5px solid rgb(214 162 173 / 100%);
}
justify-self
align-self
place-self
justify-self
属性设置单元格内容的水平位置(左中右),跟justify-items
`属性的用法完全一致,但只作用于单个项目。
align-self
属性设置单元格内容的垂直位置(上中下),跟 align-items
属性的用法完全一致,也是只作用于单个项目。
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
}
这两个属性都可以取下面四个值。
start
:对齐单元格的起始边缘。end
:对齐单元格的结束边缘。center
:单元格内部居中。stretch
:拉伸,占满单元格的整个宽度(默认值)。
<div id="container">
<div class="item item-1">1</div>
<div class="item item-2">2</div>
<div class="item item-3">3</div>
<div class="item item-4">4</div>
<div class="item item-5">5</div>
<div class="item item-6">6</div>
<div class="item item-7">7</div>
<div class="item item-8">8</div>
<div class="item item-9">9</div>
</div>
#container {
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
justify-items: center;
align-items: center;
}
.item {
width: 50px;
height: 50px;
font-size: 2em;
text-align: center;
border: 1px solid black;
}
.item-1 {
background-color: #ef342a;
}
.item-2 {
background-color: #f68f26;
}
.item-3 {
background-color: #4ba946;
}
.item-4 {
background-color: #0376c2;
}
.item-5 {
background-color: #c077af;
justify-self: start;
align-self: start;
}
.item-6 {
background-color: #f8d29d;
}
.item-7 {
background-color: #b5a87f;
}
.item-8 {
background-color: #d0e4a9;
}
.item-9 {
background-color: #4dc7ec;
}
place-self
属性是 align-self
属性和 justify-self
属性的合并简写形式。
place-self: <align-self> <justify-self>;
如果省略第二个值,place-self
属性会认为这两个值相等。
参考指南
提示
可以尝试 技能测试:网格布局 查看掌握程度。