关于 CSS Grid 布局的学习

Avatar of Ollie Williams
Ollie Williams 发布

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

以下是 Oliver Williams 的客座文章。 Oliver 一直在使用 CSS Grid 布局,并且在此过程中学到了很多东西。 在本文中,他将围绕他在这段旅程中学到的不同概念进行探讨。 我喜欢这种通过尽可能分离的示例学习关于网格布局的小块知识的想法。 这使得学习 整个内容 不那么令人生畏。

CSS 网格预计将在 2017 年初登陆浏览器。在那之前,您需要启用网格才能使其工作(除了 Firefox Nightly 中默认启用网格)。Chrome Canary 目前拥有最好的实现。 同时,Firefox 有一个非常有价值的附加组件,称为 CSS 网格检查器,它可以显示网格的线条。 它目前是唯一拥有此类工具的浏览器。

在 Chrome 中,在地址栏中输入 `chrome://flags`,找到“启用实验性 Web 平台功能”,然后单击启用。 IE 和 Edge 具有与当前规范不同的旧实现,因此不建议在此时间点使用它们来试验网格。

您不能拥有俄罗斯方块形状的网格区域

您会很快自己解决这个问题。

您的网格区域只能是矩形(如左侧),而不是任意多边形(如右侧)。

Grid 旨在与 flexbox 一起使用,而不是替代它

Grid 和 flexbox 可以以类似的方式起作用。 您可能已经看到有人使用 flexbox 来构建网格系统,但这并不是 flexbox 的设计目的。 值得阅读 Jake Archibald 的博客文章 不要使用 flexbox 进行页面整体布局

一个快速思考的方式是

  • Flexbox 用于一维布局(行或列)。
  • CSS 网格用于二维布局。

或者正如 Rachel Andrews 所说

Flexbox 基本上用于在一维中布局项目——一行或一列。 Grid 用于在二维中布局项目——行和列。

它们也可以组合使用。 您可以将网格项目转换为 flex 容器。 您可以将 flex 项目转换为网格。

让我们举一个有用的例子。 我们希望垂直居中网格项目内的文本,但我们希望背景(无论是颜色、渐变还是图像)覆盖项目的整个网格区域。 我们可以使用 align-items 属性并将值设置为 center,但现在背景没有填充项目的整个区域。 align-items 的默认值为 stretch——一旦您将其设置为任何其他值,它就不再填充空间。 相反,让我们将其保留为 stretch 并将我们的网格项目转换为 flex 容器。

.grid {
  align-items: stretch;
}

.griditem {
  display: flex;
  align-items: center;
}

使用负行号可能非常有用

假设一个当前的 CSS 网格框架具有一个 12 列网格。 在小屏幕上,内容不是减少列数,而是被告知跨越所有 12 列,给人一种成为单个全宽列的印象。

您可以使用网格执行相同的操作

/* For small screens */
.span4, .span6, .spanAll {
  grid-column-end: span 12;
}

/* For large screens */
@media (min-width: 650px) {
  .span4 {
    grid-column-end: span 4;
  }
  .span6 {
    grid-column-end: span 6;
  }
}

这种方法没有错。 但是,使用 CSS 网格,重新定义列数同样容易。 通过使用 -1,您可以确保您的内容始终到达末尾。

/* For small screens */
.span4, .span6, .spanAll {
  grid-column-end: -1;
}

对于大屏幕,您可能希望有 12 列。 对于移动设备,介于 1 到 4 列之间。 使用媒体查询轻松更改 grid-template-columns 值。

.grid {
  grid-template-columns: 1fr 1fr;
}

@media (min-width: 700px) {
  .grid {
    grid-template-columns: repeat(12, 1fr);
  }
}

可能有一些元素我们希望跨越所有屏幕尺寸的整个视口,例如标题、页脚或一些英雄图像。

对于小屏幕,我们可以编写

.wide {
  grid-column: 1 / 3; /* start at 1, end at 3 */
}

但是,一旦我们创建媒体查询,这些元素将仅覆盖 12 列中的前两列。 我们可以在同一个媒体查询中包含新的所需 grid-column-end 值 13,但有一个更简单的方法。 只需将结束值设置为 -1,它将跨越所有列,无论有多少列。 例如

.wide, .hero, .header, .footer {
  grid-column: 1 / -1;
}

查看 CSS GRID (@cssgrid) 在 CodePen 上的笔 使用 -1 简化媒体查询

网格区域创建隐式线名称

命名网格模板区域和网格线编号是将内容放置在网格上的两种方法,但您可以在同一个网格中使用这两种放置方法。 您还可以利用在指定网格区域时创建的隐式线名称。

.grid {
  grid-template-areas: "main main sidebar sidebar";
}

使用此代码,我们刚刚创建了四个隐式线名称:main-start、main-end、sidebar-start 和 sidebar-end。

如果您希望将内容重叠在多个网格区域或某个网格区域的特定子部分,这将非常有用。

查看 CSS-Tricks (@css-tricks) 在 CodePen 上的笔 使用网格区域的隐式线名称

定义网格区域的第二种方法

就像网格区域创建线名称一样,特定的线名称可以创建网格区域。 定义网格区域的语法如下所示

.grid {
  grid-template-areas: 
    "header header header"
    "main main sidebar"
    "footer footer footer";
}

如果您的设计中有大量空白空间,此语法可能会变得有些笨拙(太多的句点!句点代替名称表示空单元格。)您可能不知道还有另一种方法可以定义网格区域。 我们可以根据需要命名网格线。 但是,如果我们遵循 [name-start][name-end] 的命名约定,我们可以创建网格区域。 这是一个示例

.grid {
  display: grid;
  grid-template-columns: 20px 100px [main-start] 1fr [main-end] 100px 20px;
  grid-template-rows: 100px [main-start] 100px [main-end] 100px;
}

.griditem1 {
  background-color: red;
  grid-area: main;
}

查看 CSS GRID (@cssgrid) 在 CodePen 上的笔 定义网格区域的另一种方法

您可能不会使用此方法来布置整个页面内容,但如果您想将 grid-area 布局与基于行号的布局结合起来,了解这一点会很有帮助。

使用 vmin 创建等大小的盒子布局

CSS 网格允许您为行和列使用任何尺寸单位。 是否想要大小相等的盒子,并且希望它们具有响应性? 如果您希望内容随容器调整大小,可以使用视口单位。

.grid {
  grid-template-columns: repeat(5, 20vw);
  grid-template-rows: repeat(5, 20vh);
}

这在台式机和笔记本电脑上可以完美运行,但在屏幕高度超过宽度的手机上,我们的内容会溢出,从而创建水平滚动条。 Dudley Storey 最近写了一篇关于 鲜为人知的 css 单位 vmin 的用处 的博客文章。 此单位将在纵向屏幕上占视口宽度的百分比,并在横向屏幕上占视口高度的百分比。

.gridcontainer {
  display: grid;
  width: 100vw;
  height: 100vh;
  justify-content: center;
  align-content: center;
  grid-template-columns: repeat(5, 20vmin);
  grid-template-rows: repeat(5, 20vmin);
}

现在,我们有了可以适应任何屏幕尺寸的等大小的盒子。

查看 CSS GRID (@cssgrid) 在 CodePen 上的笔 使用 CSS 网格和 vmin 创建盒子布局

绝对定位

当您绝对定位网格项目时,其定位上下文不再是其容器(即整个网格),而是相对于其指定的网格列和网格行的起始和结束线进行定位。像往常一样,position: absolute 将元素从文档流中移除(即其他元素会忽略它)。这使得绝对定位非常有用,因为您可以重叠网格项目而不会破坏网格的自动放置算法。除非您为每个项目显式声明了grid-column-startgrid-row-start 值,否则自动放置会尽量避免项目重叠。

尝试从此示例中的 div 中删除position: absolute;,并思考您需要为多少个项目定义网格列和网格行属性!

查看 CodePen 上 CSS GRID (@cssgrid) 的示例 使用 position: absolute 保留自动放置

顺序与您想象的不同

如果您使用过 flexbox 的order 属性,那么您已经了解了这一点。所有网格项目都具有默认的顺序值0。因此,应用于网格项目的order: 1; 将使其位于其他所有内容之后,而不是之前

您可以使用负数将项目推到前面。

查看 CodePen 上 CSS GRID (@cssgrid) 的示例 顺序值

minmax 的限制

您是否希望列在它们之间平均分配空间,直到达到最大宽度?您可能会认为您可以像这样使用minmax()

.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(1fr, 300px));
}

不幸的是,这行不通。如果 max 的值小于 min,则会被忽略。frminmax() 中作为最小值是无效的。但是,它很容易实现。当auto 用作grid-template-columnsgrid-template-rows 中的值时,会使列或行与其内容一样大。

查看 CodePen 上 CSS GRID (@cssgrid) 的示例 auto 与 fr 的值

然后,我们可以在内容上设置max-width

.grid {
  display: grid;
  grid-template-columns: repeat(3, auto);
}

.item {
  max-width: 300px;
}

查看 CodePen 上 CSS GRID (@cssgrid) 的示例 minmax 的限制

minmax() 的工作方式可能有一些很好的理由,以及我尚未想到的用例。即便如此,我还在 Medium 上写了一篇名为 关于 Grid 的一件事让我讨厌 的文章。

如果命名网格线,事情会变得更容易

您可以采用多种方法。如果您真的喜欢打字,可以为线条指定多个名称。

.grid {
  grid-template-columns: [col1-start] 1fr [col1-end col2-start] 1fr [col2-end];
}

最简单的命名约定利用了网格的自动编号。我们不必输入 [col2],只需输入名称col 后跟一个数字即可。

.griditem1 {
  grid-column-start: col 2;
}

结合 span 关键字,我们可以停止思考行号,而是开始从我们希望内容填充的第一列以及我们希望占据多少列的角度思考,这更加直观。

.grid {
  grid-template-columns: repeat(4, [col] 100px);
}

.griditem1 {
  grid-column: col 2 / span 2;
}

fr 单位消除了对数学的需求

假设我们要将网格分成四列相等的部分。计算百分比很容易。我们只需编写grid-template-columns: 25% 25% 25% 25%

如果我们随后要使用grid-gap 属性怎么办?grid-gap: 10px。我们在列之间有三个间隙,因此我们的网格宽度现在将是 100% + 30px,并且某些内容会溢出屏幕右侧,出现不需要的水平滚动。我们可以使用 calc 来解决我们的问题,但使用 fr 单位要容易得多:grid-template-columns: 1fr 1fr 1fr 1fr

查看 CodePen 上 CSS GRID (@cssgrid) 的示例 fr 单位与百分比

关于 Grid 的第二件事让我讨厌

无法强制自动放置算法留出一些空列或空行。

grid-gap 值提供了一种轻松设置内容间距的方法。虽然我们可以使用grid-row-gapgrid-column-gap 指定不同的值来划分列和行,但这些值必须一致。如果我们希望第一行和第二行之间间隔 10 像素,第二行和第三行之间间隔 50 像素,则 Grid 无法提供任何方法来实现此目的,除非通过创建行并保持它们为空。
您可能在grid-template-areas 语法中看到了句点/点。

grid-template-areas: 
  "header header header"
  "main    main   main"
  "  .       .       ."
  "secondary secondary secondary"
  "footer footer footer";

这本可以提供一种简单的方法来控制自动放置算法,使其停止在这些区域放置项目。不幸的是,它并不像那样工作:此语法只是表示我们不想将第三行转换为命名的网格区域。自动放置的项目仍然会出现在那里。

一些设计建议:您不一定需要 12 列(并且列的大小不必统一)

12 列是网页设计的默认值。Bootstrap 网格使用 12 列,几乎所有其他网格框架也都是如此。这样做的原因很好:12 可以被 3 和 4 整除,这使得我们能够更灵活地在页面上布局内容。我们可以将我们的内容平均分成 12 部分、6 部分、4 部分、3 部分或一半。

虽然有些人喜欢在每个项目中始终使用相同网格带来的熟悉感,但没有必要拥有超出实际需要的列,您应该构建适合您的内容和所需布局的网格,而不是千篇一律的设置。

查看 Gridset 上的示例。Gridset 是一个用于制作网格的有用工具,但是一旦原生 CSS 网格模块上线,您就不再需要这样的工具了。它确实展示了一些设计精良的网格。

我冒昧地使用 CSS 网格重制了一个。

查看 CodePen 上 CSS GRID (@cssgrid) 的示例 使用 CSS 网格模块进行文本布局