Skip to content

Grid 网格布局

Grid 与 flex 的区别

讲到布局,我们就会想到 flex 布局,甚至有人认为竟然有 flex 布局了,似乎没有必要去了解 Grid 布局。但 flex 布局和 Grid 布局有实质的区别,那就是 flex 布局是一维布局,Grid 布局是二维布局。flex 布局一次只能处理一个维度上的元素布局,一行或者一列。Grid 布局是将容器划分成了“行”和“列”,产生了一个个的网格,我们可以将网格元素放在与这些行和列相关的位置上,从而达到我们布局的目的。

Grid布局中的概念

网格容器

我们通过在元素上声明 display:griddisplay:inline-grid 来创建一个网格容器。一旦我们这样做,这个元素的所有直系子元素将成为网格元素。

css
.wrapper {
     /* 块级元素 */
    display: grid; 
    /* 
        行内元素
    display: inline-grid; 
    */  
}

网格轨道

grid-template-columnsgrid-template-rows 属性来定义网格中的列和行。容器内部的水平区域称为行,垂直区域称为列。

如下面的代码,就会生成一个2行3列的效果。

html
<div class="grid-box-1">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
</div>
css
.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;
}

效果:
grid

网格单元

一个网格单元是在一个网格元素中最小的单位, 从概念上来讲其实它和表格的一个单元格很像。上图中 1、2、3、4… 都是一个个的网格单元

网格线

设置网格轨道时,Grid会为我们创建带编号的网格线来让我们来定位每一个网格元素。还是拿上面的代码做例子,他的网格线编号会是下图中的顺序(负数表示从后往前的编号,不含隐式网格对应的网格线)。

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。

css
.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

容器属性

grid-template-columns 属性和 grid-template-rows 属性

grid-template-columns 属性设置列宽,grid-template-rows 属性设置行高

固定的列宽和行高

html
<div class="grid-box">
    <div>1</div>
    <div>2</div>
    <div>3</div>
    <div>4</div>
    <div>5</div>
    <div>6</div>
</div>
css
.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

repeat() 函数

repeat() 函数可以简化重复的值。该函数接受两个参数,第一个参数是重复的次数,第二个参数是所要重复的值。上面行高都是一样的,我们可以这么去实现,实际效果是一模一样的

css
.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。

css
/* 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;
}

效果:
grid

minmax

像上面的例子,有时候一行并不能刚刚好填满 grid

如果不希望后面有空白呢,这个时候就需要子节点有适当的宽度适配。子节点不再是固定宽度,而是通过 minmax 函数产生一个长度范围,表示长度就在这个范围之中都可以应用到网格项目中。它接受两个参数,分别为最小值和最大值。

css
.grid-box-6 {
    grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)) ;
}

fr 关键字

fr 单位代表网格容器中可用空间的一等份。

css
grid-template-columns: 200px 1fr 2fr

表示第一个列宽设置为 200px,后面剩余的宽度分为两部分,宽度分别为剩余宽度的 1/3 和 2/3。

auto 关键字

由浏览器决定长度。通过 auto 关键字,我们可以轻易实现三列或者两列布局。

css
grid-template-columns: 100px auto 100px

表示第一第三列为 100px,中间由浏览器决定长度。

间隔属性

grid-row-gap grid-column-gap 分别设置行间距和列间距。grid-gap 属性是两者的简写形式。

css
.grid-box-4 {
    /* 等同于 gap: 6px 2px; */
    grid-column-gap: 2px;
    grid-row-gap: 6px;
}

效果:
grid

TIP

现在好像是改成column-gap,row-gap和 gap了。gap的顺序是row-gap column-gap。

网格模板区域

Grid布局提供了一个模板区域的设置方法。通过 grid-template-areas 设置区域,然后网格单元通过属性 grid-area 来占位相应区域。其中 . 表示1fr的空白。

css
.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

grid-auto-flow 属性

grid-auto-flow是控制自动布局算法怎样运作的属性,它能精确指定在网格中被自动布局的元素怎样排列。它有3个属性值:

  • column 先把一列排满,再填如第二列
  • row(默认) 先填满一行,再填如第二行
  • dense。指定自动布局算法使用一种“稠密”堆积算法,如果后面出现了稍小的元素,则会试图去填充网格中前面留下的空白。这样做会填上稍大元素留下的空白,但同时也可能导致原来出现的次序被打乱。

这样我们就可以利用grid-auto-flow: dense来解决空白问题。(不能完美解决,只能让空白变小。)

比如下面这个例子:

css
.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:
grid

dense:
grid

justify-items 属性、align-items 属性以及 place-items 属性

justify-items 属性设置单元格内容的水平位置(左中右),align-items 属性设置单元格的垂直位置(上中下)

grid 的 justify-items align-items 跟 flex 的差不多。它们都有如下属性:

css
.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 属性是整个内容区域的垂直位置(上中下)。

css
.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 属性中定义的行和列。如果你在网格定义之外又放了一些东西,或者因为内容的数量而需要的更多网格轨道的时候,网格将会在隐式网格中创建行和列

html
<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>
css
/* 隐式网格 */
.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

上面的代码中,如果Grid的子节点超过了6个,父节点的高度是200,那么超出指定网格轨道的子节点就是隐式网格,隐式网格不会根据网格轨道的样式来进行设置。比如例子中就是,隐式网格的高度是内容的高度,父节点剩余的空间会分配给两个指定的网格轨道,轨道1:1比例分配。 当然,我们也可以设置隐式网格的样式,通过grid-auto-rows和grid-auto-columns可以对隐式网格的行列进行设置。如下面的例子,设置了隐式网格轨道的行高为40,那么显示网格高度就是60((200-40*2) / 2)。

css
.grid-box-2 {
    grid-auto-rows: 40px; /* 添加隐式网格的行高30px */
}

效果:
grid

justify-self 属性、align-self 属性以及 place-self 属性

justify-self 和 align-self属性设置单元格内容的水平和垂直位置(左中右/上中下),跟 justify-items 和 align-items 属性的用法完全一致,但只作用于单个项目

css
.item {
    justify-self: start | end | center | stretch;
    align-self: start | end | center | stretch;
}

例如

css
.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;
}

效果:
grid

网格线的命名

虽然我们可以通过指定网格线来确定网格区域,但是网格线还是太不直观了。接下来我们讲一讲怎么通过对网格线命名来解决这个问题。使用Chrome Dev Tools布局查看,可以看到命名的网格线名字。

css
.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;
}

效果:
grid

网格单元优先级

有时候,网格单元会有重叠部分,导致有一方被挡住了。那么我们可以通过 z-index 来设置优先级显示。

css
.grid-box-5 > div:nth-of-type(1) {
    z-index: 2;
}