2022年给你一份快速学习 CSS grid 布局的小抄

298 阅读4分钟

前言

已经2022年了,相信各位开发同学都已经听说过CSS的grid布局了吧。

什么是Grid布局

Grid布局是一种二维的布局系统,简单来说就是在的维度上进行布局,因此我们主要会碰到行(row)列(column)间距(gutter或者gap) 这几个概念。

下图来自MDN,可以看到对这些概念的标注。 来自MDN的图片

下图是目前CSS grid布局的支持情况,可以看到高达95.81%的浏览器已经支持了这一布局。 image.png

跟着例子🌰快速尝试

构造4行3列(目标:构造grid布局、了解grid-template-columns和grid-template-rows、知道auto的用途)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 3列,第1列和第3列有固定宽度,中间的第2列自动占满剩余宽度 */
      grid-template-columns: 80px auto 100px;
      /* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
      grid-template-rows: 25% 100px auto 60px;
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

效果如下图所示:列方向上分为3列,第1列和第3列有固定宽度,中间的第2列自动占满剩余宽度,行方向上4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px。 image.png

自动构造等宽的多列(目标:了解repeat、fr单位)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 自动循环生成3列 */
      grid-template-columns: repeat(3, 1fr);
      /* 可以试试这一句看看有什么效果 */
      /* grid-template-columns: repeat(3, 1fr 1fr); */
      /* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
      grid-template-rows: 25% 100px auto 60px;
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

效果如下图所示:repeat(3, 1fr)加粗的3指示把第二个参数重复3次。fr(fraction) 是grid布局里的一种单位,用于指示当前单元在grid容器内的可用空间中所占的分数,可以看到目前3列是等宽的。注意,repeat的第二个参数可以是多个空格分割的数据,如repeat(3, 1fr 1fr),这样会形成3*2即6列

image.png

设置了列数,不设置行数,会自动进行排布(目标:了解grid可以自动计算行数、grid-auto-rows属性、minmax函数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 自动循环生成3列 */
      grid-template-columns: repeat(3, 1fr 1fr);
      /* grid-auto-rows可以设置高度,可以设置具体数值,也可以用minmax设置上下限 */
      grid-auto-rows: minmax(100px, auto);
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child" style="color:red;">
    <div>......................................................................................</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
    <div>...</div>
  </div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
<!--  <div class="child">...</div>-->
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

这里有11个子元素,设置了4列,可以看到自动生成了2行。

grid-auto-rows可以设置高度,可以设置具体数值,也可以用minmax设置上下限,这里由于设置了行的最小高度为100px,因此第1行第1列的子元素的高度在超过父元素高度400px-第2行高度100px=300px之后,溢出了

第1行第1列的子元素的宽度过大,会导致挤压右边的列

image.png

固定宽度、auto和fr单位混用(目标:了解宽度分配模式)

  • pxfr混用
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 第1列占用固定宽度,另外两列自动分割剩余空间 */
      grid-template-columns: 100px 1fr 1fr;
      /* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
      grid-template-rows: 25% 100px auto 60px;
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

效果如下图所示:第1列占用固定宽度,另外两列由于采用了fr单位,因此会自动分割剩余空间。 image.png

  • autofr混用,且fr之和大于1
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 第2列设置为auto,表现为包裹内容,另外2列自动按比例分配剩余空间 */
      grid-template-columns: 1fr auto 1fr;
      /* 也可以是 */
      /* grid-template-columns: 0.5fr auto 0.5fr; */
      /* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
      grid-template-rows: 25% 100px auto 60px;
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

效果如下图所示:第2列设置为auto,表现为包裹内容,其宽度为14.65px(下面计算会用到这个数据),另外2列自动按比例分配剩余空间。 image.png

  • autofr混用,且fr之和小于1
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 第1列和第3列之和小于1 */
      grid-template-columns: 0.4fr auto 0.5fr;
      /* 4行,第1行设置为25%,实际渲染高度正好为父元素高度400px的25%即100px */
      grid-template-rows: 25% 100px auto 60px;
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

效果如下图所示:第1列和第3列之和小于1,此时fr的自动计算是这样的,(容器宽度 - auto列内容的真实宽度)* fr值,在此处可以得到(800-14.65)*0.4=314.14,这里的14.65是从上面一个例子得到的。 image.png

不预设列数,自动根据子元素宽度进行填充

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      /* 自动根据子元素宽度填充 */
      grid-template-columns: repeat(auto-fill, 150px);
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
  <div class="child">...</div>
</div>
</body>
</html>

可以看到repeat的第一个参数使用了auto-fill,意思是自动填充尽可能多的列,由于每一列的宽度是150px,容器宽度为800px,因此最后面剩余了50px无法填充,只能自动换行

image.png

指定单元格位置、合并单元格(目标:学会grid-column和grid-row属性指定单元格位置,同时可以进行“合并单元格”的操作)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      grid-template-columns: repeat(auto-fill, 150px);
    }

    .child {
      border: 1px solid black;
    }

    .merge1 {
      grid-column: 2 / 4;
      grid-row: 1 / 3;
    }

    .merge2 {
      grid-column: 4;
      grid-row: 2 / 4;
    }

    .merge3 {
      grid-column: -3 / -1;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child merge1">
    child1 merge1
  </div>
  <div class="child merge2">
    child2 merge2
  </div>
  <div class="child merge3">
    child3
  </div>
  <div class="child">
    child4
  </div>
  <div class="child">
    child5
  </div>
  <div class="child">child6</div>
  <div class="child">child7</div>
  <div class="child">child8</div>
  <div class="child">child9</div>
  <div class="child">child10</div>
  <div class="child">child11</div>
  <div class="child">child12</div>
</div>
</body>
</html>

第1个子元素设置了grid-column: 2 / 4;其中的2代表上方的列分割线2,4代表上方的列分割线4,因此其占据了2-3列。注意,分割线可以用负数进行标记,因此child3元素设置了grid-column: -3 / -1;,即占据了最后两列。

image.png

通过命名单元格进行定位(目标:学会grid-template-areas)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      grid-template-columns: 1fr 3fr;
      grid-template-areas:
      "header header"
      "sidebar content"
      "footer footer";
    }

    .child {
      border: 1px solid black;
    }

    .merge1 {
      grid-area: header;
    }

    .merge2 {
      grid-area: content;
    }

    .merge3 {
      grid-area: sidebar;
    }

    .merge4 {
      grid-area: footer;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child merge1">
    child1
  </div>
  <div class="child merge2">
    child2
  </div>
  <div class="child merge3">
    child3
  </div>
  <div class="child merge4">
    child4
  </div>
</div>
</body>
</html>

利用grid-template-areas属性定义不同区域的名称,然后在子元素中利用grid-area来指定需要定位到的单元格名称。

image.png

利用Grid布局构建12/16/24列布局系统

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      grid-template-columns: repeat(12, minmax(0,1fr));
    }

    .child {
      border: 1px solid black;
    }

    .merge1 {
      grid-column: 1 / 13;
      grid-row: 1;
    }

    .merge2 {
      grid-column: 4 / 13;
      grid-row: 2;
    }

    .merge3 {
      grid-column: 1 / 4;
      grid-row: 2;
    }

    .merge4 {
      grid-column: 1 / 13;
      grid-row: 3;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child merge1">
    child1
  </div>
  <div class="child merge2">
    child2
  </div>
  <div class="child merge3">
    child3
  </div>
  <div class="child merge4">
    child4
  </div>
</div>
</body>
</html>

过去实现类似布局,通常最方便的做法是使用框架的栅格系统,现在利用Grid布局也可以轻松实现类似的功能。

image.png

选择Grid布局还是Flex布局?

考虑如下的Flex布局

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Flex</title>
  <style>
    .parent {
      width: 450px;
      display: flex;
      flex-wrap: wrap;
    }

    .child {
      height: 100px;
      /* Three values: flex-grow | flex-shrink | flex-basis */
      flex: 1 1 150px;
    }
  </style>
</head>
<body>
<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>
</body>
</html>

可以看到,Flex容器内部分成了2行,但是Flex是基于行进行分配空间的,因此第2行两个子元素的宽度各占据了一半。

image.png

下图是第4个子元素的宽度

image.png

观察下面的Grid布局

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .container {
      width: 800px;
      height: 400px;
      /* 定义当前元素使用grid布局系统 */
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      row-gap: 20px;
    }

    .child {
      border: 1px solid black;
    }
  </style>
</head>
<body>
<div class="container">
  <div class="child">
    child1
  </div>
  <div class="child">
    child2
  </div>
  <div class="child">
    child3
  </div>
  <div class="child">
    child4
  </div>
  <div class="child">
    child5
  </div>
</div>
</body>
</html>

可以看到,第2行的子元素的宽度可以与第1行的一样,同时可以很方便地设置行间距row-gap

image.png

  • 如果需要控制行或者列,选择Flex布局
  • 如果需要控制行列,选择Grid布局

参考