Grid 网格布局
Grid 与 flex 的区别
讲到布局,我们就会想到 flex 布局,甚至有人认为竟然有 flex 布局了,似乎没有必要去了解 Grid 布局。但 flex 布局和 Grid 布局有实质的区别,那就是 flex 布局是一维布局,Grid 布局是二维布局。flex 布局一次只能处理一个维度上的元素布局,一行或者一列。Grid 布局是将容器划分成了“行”和“列”,产生了一个个的网格,我们可以将网格元素放在与这些行和列相关的位置上,从而达到我们布局的目的。
Grid布局中的概念
网格容器
我们通过在元素上声明 display:grid 或 display:inline-grid 来创建一个网格容器。一旦我们这样做,这个元素的所有直系子元素将成为网格元素。
.wrapper {
/* 块级元素 */
display: grid;
/*
行内元素
display: inline-grid;
*/
}
网格轨道
grid-template-columns 和 grid-template-rows 属性来定义网格中的列和行。容器内部的水平区域称为行,垂直区域称为列。
如下面的代码,就会生成一个2行3列的效果。
<div class="grid-box-1">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
.grid-box-1 {
border: 1px solid #999;
width: 300px;
height: 200px;
display: grid;
margin: 20px;
grid-template-columns: 1fr 1fr 1fr; /* 指定 3 列*/
grid-template-rows: 1fr 1fr; /* 指定 2行 */
}
.grid-box-1 > div {
background-color: bisque;
border-radius: 4px;
border: 1px solid #ccc;
}
效果:
网格单元
一个网格单元是在一个网格元素中最小的单位, 从概念上来讲其实它和表格的一个单元格很像。上图中 1、2、3、4… 都是一个个的网格单元
网格线
设置网格轨道时,Grid会为我们创建带编号的网格线来让我们来定位每一个网格元素。还是拿上面的代码做例子,他的网格线编号会是下图中的顺序(负数表示从后往前的编号,不含隐式网格对应的网格线)。
有了网格线,我们可以通过子元素的一些属性来操作网格元素,从而实现网格元素占多行多列的效果。
操作行线
- grid-row-start 表示行线开始位置
- grid-row-end 表示行线结束位置
- grid-row start 和 end 综合写法
操作列线
- grid-column-start 表示列线开始位置
- grid-column-end 表示列线开始位置
- grid-column start 和 end 综合写法
上面属性的值如果是数字,那么代表的是位置的起始。例如 grid-row-start:1 和 grid-row-end: 4 的意思是行线中的第一条线到第四条线。
如果是 span xx (xx 是数字),那么意思是占几行
也可以通过grid-area来实现行和列的一起编写。顺序是row-start / column-start / row-end / column-end。
.grid-box-3 > div:nth-of-type(1) {
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 2; /* 如果只占一行或一列,grid-xx-end属性可以不用写 */
/* 等同于下面的代码 */
grid-area: 1 / 1 / 2 / 4; /* 这里的顺序是:row-start / column-start / row-end / column-end */
}
.grid-box-3 > div:nth-of-type(2) {
grid-row: 2 / 4; /* grid-row 是 grid-row-start 和 grid-row-end的缩写 */
}
.grid-box-3 > div:nth-of-type(3) {
grid-column: 2 / span 2; /* span表示占据几行,这里表示从2开始,占据2行,也就是网格线2到4 */
}
.grid-box-3 > div:nth-of-type(6) {
grid-column: 1 / span 3;
}
效果:
容器属性
grid-template-columns 属性和 grid-template-rows 属性
grid-template-columns 属性设置列宽,grid-template-rows 属性设置行高
固定的列宽和行高
<div class="grid-box">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
.grid-box > div {
background-color: bisque;
border-radius: 4px;
border: 1px solid #ccc;
}
.grid-box {
border: 1px solid #999;
margin: 20px;
width: 300px;
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 30px 30px;
}
效果:
repeat() 函数
repeat() 函数可以简化重复的值。该函数接受两个参数,第一个参数是重复的次数,第二个参数是所要重复的值。上面行高都是一样的,我们可以这么去实现,实际效果是一模一样的
.grid-box {
border: 1px solid #999;
margin: 20px;
width: 300px;
display: grid;
grid-template-columns: repeat(3, 100px);
grid-template-rows: repeat(2, 30px);
}
auto-fill
有的时候,父容器的尺寸是不确定的,我们需要把子元素往父容器中逐个填充,这个时候我们可以利用auto-fill。
/* Grid 容器尺寸不固定,自动适配子元素 */
.grid-box {
border: 1px solid #999;
display: grid;
margin: 20px;
grid-template-columns: repeat(auto-fill, 50px);
}
.grid-box > div {
height: 50px;
background-color: bisque;
border-radius: 4px;
border: 1px solid #ccc;
}
效果:
minmax
像上面的例子,有时候一行并不能刚刚好填满
如果不希望后面有空白呢,这个时候就需要子节点有适当的宽度适配。子节点不再是固定宽度,而是通过 minmax 函数产生一个长度范围,表示长度就在这个范围之中都可以应用到网格项目中。它接受两个参数,分别为最小值和最大值。
.grid-box-6 {
grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)) ;
}
fr 关键字
fr 单位代表网格容器中可用空间的一等份。
grid-template-columns: 200px 1fr 2fr
表示第一个列宽设置为 200px,后面剩余的宽度分为两部分,宽度分别为剩余宽度的 1/3 和 2/3。
auto 关键字
由浏览器决定长度。通过 auto 关键字,我们可以轻易实现三列或者两列布局。
grid-template-columns: 100px auto 100px
表示第一第三列为 100px,中间由浏览器决定长度。
间隔属性
grid-row-gap grid-column-gap 分别设置行间距和列间距。grid-gap 属性是两者的简写形式。
.grid-box-4 {
/* 等同于 gap: 6px 2px; */
grid-column-gap: 2px;
grid-row-gap: 6px;
}
效果:
TIP
现在好像是改成column-gap,row-gap和 gap了。gap的顺序是row-gap column-gap。
网格模板区域
Grid布局提供了一个模板区域的设置方法。通过 grid-template-areas 设置区域,然后网格单元通过属性 grid-area 来占位相应区域。其中 . 表示1fr的空白。
.grid-box-8 {
border: 1px solid #999;
width: 400px;
height: 120px;
margin: 20px;
display: grid;
grid-template-columns: repeat(9, 1fr);
grid-template-areas:
"hd hd hd hd hd hd hd hd hd"
"sd sd sd main main main main . ."
"sd sd sd ft ft ft ft ft ft";
}
.grid-box-8 > div:nth-of-type(1){
grid-area: hd;
}
.grid-box-8 > div:nth-of-type(2){
grid-area: ft;
}
.grid-box-8 > div:nth-of-type(3) {
grid-area: main;
}
.grid-box-8 > div:nth-of-type(4) {
grid-area: sd;
}
效果:
grid-auto-flow 属性
grid-auto-flow是控制自动布局算法怎样运作的属性,它能精确指定在网格中被自动布局的元素怎样排列。它有3个属性值:
- column 先把一列排满,再填如第二列
- row(默认) 先填满一行,再填如第二行
- dense。指定自动布局算法使用一种“稠密”堆积算法,如果后面出现了稍小的元素,则会试图去填充网格中前面留下的空白。这样做会填上稍大元素留下的空白,但同时也可能导致原来出现的次序被打乱。
这样我们就可以利用grid-auto-flow: dense来解决空白问题。(不能完美解决,只能让空白变小。)
比如下面这个例子:
.grid-box-9 {
border: 1px solid #999;
margin: 20px;
width: 200px;
display: grid;
gap: 2px;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 50px;
/* grid-auto-flow: row dense; */
}
.grid-box-9 > div:nth-of-type(1){
grid-column-end: span 3;
}
.grid-box-9 > div:nth-of-type(2n){
grid-column-end: span 2;
grid-row-end: span 2;
}
row:
dense:
justify-items 属性、align-items 属性以及 place-items 属性
justify-items 属性设置单元格内容的水平位置(左中右),align-items 属性设置单元格的垂直位置(上中下)
grid 的 justify-items align-items 跟 flex 的差不多。它们都有如下属性:
.container {
justify-items: start | end | center | stretch;
align-items: start | end | center | stretch;
}
place-items:justify-items和 align-items两个的缩写。先align-items再justify-items。
justify-content 属性、align-content 属性以及 place-content 属性
justify-content 属性是整个内容区域在容器里面的水平位置(左中右),align-content 属性是整个内容区域的垂直位置(上中下)。
.container {
justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
align-content: start | end | center | stretch | space-around | space-between | space-evenly;
}
- space-around - 每个项目两侧的间隔相等。所以,项目之间的间隔比项目与容器边框的间隔大一倍
- space-between - 项目与项目的间隔相等,项目与容器边框之间没有间隔
- space-evenly - 项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔
- stretch - 项目大小没有指定时,拉伸占据整个网格容器
grid-auto-columns 属性和 grid-auto-rows 属性
在讲 grid-auto-columns 属性和 grid-auto-rows 属性之前,先来看看隐式和显示网格的概念
隐式和显示网格:显式网格包含了你在 grid-template-columns 和 grid-template-rows 属性中定义的行和列。如果你在网格定义之外又放了一些东西,或者因为内容的数量而需要的更多网格轨道的时候,网格将会在隐式网格中创建行和列
<div class="grid-box-2">
<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>10</div>
<div>11</div>
</div>
/* 隐式网格 */
.grid-box-2 {
border: 1px solid #999;
width: 300px;
height: 200px;
display: grid;
margin: 20px;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
.grid-box-2 > div {
background-color: bisque;
border-radius: 4px;
border: 1px solid #ccc;
}
效果:
上面的代码中,如果Grid的子节点超过了6个,父节点的高度是200,那么超出指定网格轨道的子节点就是隐式网格,隐式网格不会根据网格轨道的样式来进行设置。比如例子中就是,隐式网格的高度是内容的高度,父节点剩余的空间会分配给两个指定的网格轨道,轨道1:1比例分配。 当然,我们也可以设置隐式网格的样式,通过grid-auto-rows和grid-auto-columns可以对隐式网格的行列进行设置。如下面的例子,设置了隐式网格轨道的行高为40,那么显示网格高度就是60((200-40*2) / 2)。
.grid-box-2 {
grid-auto-rows: 40px; /* 添加隐式网格的行高30px */
}
效果:
justify-self 属性、align-self 属性以及 place-self 属性
justify-self 和 align-self属性设置单元格内容的水平和垂直位置(左中右/上中下),跟 justify-items 和 align-items 属性的用法完全一致,但只作用于单个项目
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
}
例如
.grid-box > div {
background-color: bisque;
border-radius: 4px;
border: 1px solid #ccc;
}
.grid-box {
border: 1px solid #999;
margin: 20px;
width: 300px;
display: grid;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 30px 30px;
}
.grid-box >div:nth-of-type(3) {
justify-self: right;
}
效果:
网格线的命名
虽然我们可以通过指定网格线来确定网格区域,但是网格线还是太不直观了。接下来我们讲一讲怎么通过对网格线命名来解决这个问题。使用Chrome Dev Tools布局查看,可以看到命名的网格线名字。
.grid-box-7 {
width: 300px;
display: grid;
margin: 20px;
grid-template-columns: [main-start] 1fr [content-start] 1fr [content-end] 1fr [main-end];
grid-template-rows: [main-start] 40px [content-start] 40px [content-end] 40px [main-end];
}
.grid-box-7 > div:nth-of-type(1) {
grid-column-start: main-start;
grid-column-end: main-end;
grid-row-start: main-start;
grid-row-end: content-start;
}
.grid-box-7 > div:nth-of-type(2) {
grid-column: main-start / content-start;
grid-row: content-start / main-end;
}
效果:
网格单元优先级
有时候,网格单元会有重叠部分,导致有一方被挡住了。那么我们可以通过 z-index 来设置优先级显示。
.grid-box-5 > div:nth-of-type(1) {
z-index: 2;
}