面向完全初学者的 CSS 网格课程
先决条件
- 对 HTML 有基本的了解
- 对 CSS 的基本了解
- Flexbox 的实际应用知识(我将从我的 Flexbox 教程中借用许多概念)
本课的目标
在本课结束时,您将:
- 能够向别人解释什么是 CSS Grid
- 了解 CSS Grid 的基本语法和模型
- 了解 CSS Grid 和 Flexbox(上一课)之间的关系
- 了解何时使用 Flexbox 以及何时使用 CSS Grid 进行 CSS 布局(是的,它们可以一起使用!)
- 能够使用 CSS Grid 和 Flexbox构建圣杯布局(见下文)
什么是“fullstackroadmap”系列?
这是我的全栈开发者系列教程的一部分,你将从从未编写过任何代码开始,逐步掌握如何将你的第一个全栈 Web 应用部署到互联网。 点击此链接,即可概览本系列教程的全部内容。
- 系列目录
- Github 存储库- 您可以在其中找到我们在本系列中编写的所有代码
- YouTube 播放列表
- 系列概述
- 100 天代码挑战- 我强烈建议您在阅读本系列时接受这一挑战!
请在 Twitter 上标记我@zg_dev并通过#100DaysOfCode分享本系列!
让我们开始吧——什么是 CSS Grid?
CSS Grid 是一个二维布局系统。换句话说,你可以控制元素在 x 轴和 y 轴上的布局,这是对Flexbox的补充,我们在本系列的上一课中学习过。
这就是为什么我们生活中需要 CSS Grid:
- 2009 年之前(Flexbox/Grid 出现之前),前端开发者使用一些“hacky”的解决方案来创建复杂的布局。这包括那些令人讨厌的
float
和clear
属性以及一些繁琐的数学计算。 - 从 2009 年到 2017 年(Flexbox 存在,Grid 不存在),CSS 布局比以前更容易了,但仍然没有针对“高级”布局(例如Holy Grail 布局)的好的解决方案。你可以用 Flexbox 来实现,但这需要一些“技巧”和相当多的创造力。
- 从 2017 年开始,我们将使用 CSS Grid 来实现高级、复杂的二维布局,并使用 Flexbox 来实现更简单的布局。两者兼得。
但下一个显而易见的问题是......我们如何知道何时使用 CSS Grid 与 Flexbox?
何时使用 CSS Grid 和 Flexbox
CSS Grid 和 Flexbox 可以协同工作。它们都是 CSS3 规范的一部分,虽然它们在布局方面有类似的功能,但它们的初衷并非相互竞争。
在以下情况下使用网格:
- 您需要完全控制行和列(例如:整页布局、图库项目)
- 您想要明确定义布局,而不管内容大小如何(例如,如果您使用 Flexbox,并且由于大量高度和宽度而变得非常复杂,您可能应该使用 Grid)
在以下情况下使用 Flexbox:
- 您的内容是一行或一列(例如:水平导航栏、垂直侧边栏)
- 您希望项目的大小决定布局
我们将在本教程的后面部分介绍一个示例,以便您可以看到它的实际效果。
在我们开始之前还有一件事...
所有浏览器都支持 CSS Grid 吗?
对于学习前端 Web 开发的初学者来说,是的。截至 2017 年 3 月,所有主流浏览器都已内置对 CSS Grid 的支持,您不必为此担心。
对于业内人士来说,如果必须支持 Internet Explorer(愿上帝保佑你)和其他一些老旧浏览器,答案是“不”。你需要“后备”样式。好在有了媒体查询,这很简单。
@supports (display: grid) {
/* Put your CSS Grid styles here */
}
如果浏览器支持 CSS Grid,则您放入该块的所有内容都将适用。
网格容器 vs. 网格项目
如果这个标题听起来很熟悉,那就好了。如果不熟悉,请先阅读我的 Flexbox 教程,然后再继续阅读。这篇文章很大程度上依赖于我们在那里学到的概念,我将在这里略过它们。
就像我们有“Flex 容器”和“Flex 项目”一样,有了 CSS Grid,我们就有了“Grid 容器”和“Grid 项目”。
看看下面的 HTML 和 CSS。
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
</div>
.grid-container {
display: grid;
}
与 Flexbox 一样,我们通过设置display
CSS 属性来激活 CSS Grid。
通过对网格设置进行一些调整以及在网格项上添加一些额外的颜色样式,您可以创建如下所示的内容:
作为本教程其余部分的预览,请注意以下事项。
- 上面的图片只是我可以使用的许多示例之一。在我们的示例中,我尽量简化,并将网格设计成完美的正方形(3行,3列,高度和宽度均相等)。
- 虚线很重要。这些是网格的边界。
- 使用 CSS Grid,我们将行和列称为“轨道”
理解 CSS 网格的关键在于知道如何操作每个网格项的大小和位置。为此,我们提供了几个容器级属性和几个网格项级属性。
在你惊慌失措之前
您将要看到的是一长串可用于 CSS 网格的 CSS 属性。乍一看,它们可能令人望而生畏,但如果您看过我的Flexbox 教程,您就具备了理解这些内容所需的基础。我们将添加一些新概念,但接下来的大部分内容都是在您已知知识的基础上“层层叠加”的。我相信,随着我们深入探讨,您即将感受到的那种不知所措很快就会消退。
网格容器与网格项目属性
与我之前的 Flexbox 教程一致,这里有两份 CSS Grid 的速查表,它们真的帮了我大忙。我学习的时候,一直把它们放在一边。我建议你也这样做几周。
与之前的教程一致,下面是 CSS 网格属性的 MDN 文档链接。我将在整篇文章中详细解释每个属性。
网格容器属性
在要转换为网格的 HTML“容器”上设置这些属性。不用记住这些,我们稍后会学习它们的工作原理。
激活网格
- display: grid - 在 HTML 元素上激活 CSS 网格系统(其他有效值包括
inline-grid
和subgrid
,但我们不会在这里介绍这些)
浆纱
- grid-template-rows - 定义网格行的大小
- grid-template-columns - 定义网格列的大小
- row-gap - 定义行与行之间的水平“间距”的大小
- 列间隙- 定义列之间的垂直“间距”的大小
- grid-auto-rows - 当您没有明确设置行的大小(通过
grid-template-rows
)时,大小由该值决定。 - grid-auto-columns - 当您没有明确设置列的大小(通过
grid-template-columns
)时,其大小由该值决定。
结盟
- grid-auto-flow - 设置用于放置未明确放置的网格项的算法
- grid-template-areas - 如果没有示例的话,这个属性有点难以解释。我将在本教程的后面部分介绍。
- justify-items - 将项目沿网格区域内的水平轴对齐
- align-items - 在网格区域内沿垂直轴对齐项目
- justify-content - 沿水平轴对齐网格项“组”(类似于我们在 Flexbox 中使用它的方式
flex-direction: row
) - align-content - 沿垂直轴对齐网格项“组”(与我们使用 Flexbox 的方式略有不同)
网格项属性
浆纱
- grid-row-start - 对于给定的网格项,定义垂直起点
- grid-row-end - 对于给定的网格项,定义垂直结束点
- grid-column-start - 对于给定的网格项,定义水平结束点
- grid-column-end - 对于给定的网格项,定义水平结束点
结盟
- justify-self - 将单个网格项在水平轴上对齐
- align-self - 将单个网格项在垂直轴上对齐
这是所有这些属性在一个地方的视觉效果。
简写网格属性
和 Flexbox 一样,Grid 也有一些简写属性,可以将上述一些属性组合在一起。还记得吗?
/* Both rules below are equivalent. */
.flex-container {
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
}
.flex-container {
/* Shorthand property for Flexbox!
Equivalent to:
flex: <flex-grow> <flex-shrink> <flex-basis> */
flex: 0 1 auto;
}
我们可以用 CSS Grid 实现类似的功能。我不建议你先熟悉上面的常规属性,然后再使用这些属性。不过,它们还是可以的:
- grid - 所有简写属性的简写。它几乎一次性定义了所有内容(行、列、区域、流向)。
- grid-template - 类似
grid
,但功能略逊一筹。定义网格的行、列和区域。 - 间隙(网格间隙) - 一次性定义所有“网格间隙”
- grid-area - 一次性定义网格项的整个区域
- grid-row - 的子集
grid-area
,它定义了网格项的行大小 - grid-column - 的子集
grid-area
,它定义了网格项的列大小
如果您像我一样需要直观地看到这些内容,这里有一个思维导图可供参考。
第一部分:构建网格
虽然我相信我们能够顺利地理解这个主题,但这需要一系列有条不紊的步骤。请仔细阅读,不要跳过任何步骤。这些步骤都遵循特定的顺序。
首先,让我们构建一个用于探索 CSS 网格的“游乐场”。在下面的 Codepen 中,我设置的唯一与网格相关的属性是:
.grid-container {
display: grid;
}
Grid 本身已激活,但我们尚未设置任何属性。请阅读下方 Codepen 中的 CSS 注释,熟悉一下。
从视觉上看,这就是我们要处理的事情。
用“网格语言”来说,我们这样做了。由于所有这些属性都默认为这些值,您可以删除下面的所有内容,并实现与上面相同的布局。
.grid-container {
display: grid;
grid-template-rows: auto auto auto;
grid-template-columns: auto;
align-items: stretch;
justify-items: stretch;
}
.gi-1 {
grid-row-start: 1;
grid-row-end: 2;
grid-column-start: 1;
grid-column-end: 2;
}
.gi-2 {
grid-row-start: 2;
grid-row-end: 3;
grid-column-start: 1;
grid-column-end: 2;
}
.gi-3 {
grid-row-start: 3;
grid-row-end: 4;
grid-column-start: 1;
grid-column-end: 2;
}
网格模板
这里有很多内容需要解释。让我们从这两个属性开始:
grid-template-rows
grid-template-columns
仅这两个属性就足以定义我们的“网格线”,并最终定义我们的“网格”的结构。
如果我们不显式设置这些属性,就会得到一种称为“隐式网格”的东西,它的宽度始终为 1 列,行数取决于网格项的数量。在我们的例子中,网格宽度为 1 列 x 3 行(因为我们有 3 个网格项)。
为了真正了解这一点,让我们暂时移除网格项,并查看一些示例。我们从简单的开始:
.grid-container {
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 2fr 1fr;
}
当您看到 时fr
,这意味着“小数单位”,其工作方式与我们的Flexbox 教程flex-grow
中的和flex-shrink
值相同(再次强调,如果您还没有阅读过,请在继续之前阅读它)。
在上面的例子中,我们将其定义grid-template-rows
为3个相等的部分。我们分配了3个“小数单位”(1fr + 1fr + 1fr),因此每行将获得可用网格空间的33.33%。我们将其定义grid-template-columns
为2个部分,但它们并不相等。总共分配了3个fr单位,但其中2个分配给第一列,1个分配给第二列。如果每个单位等于33.33%(1/3),则第一列的宽度为66.66%,第二列的宽度为33.33%。这是我们刚刚定义的网格:
你会注意到,我们有两列,但有三条“线”代表边界。这类似于栅栏柱数学问题。因此,我将它们重命名为CL-n
和RL-n
,分别是“列线 #n”和“行线 #n”的缩写。
这里还有一个例子。
.grid-container {
display: grid;
grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}
我们定义了8行8列,宽度和高度相等(即一个完美的“正方形”网格)。它看起来是这样的。
在继续之前,让我们先用这个repeat
函数简化一下 CSS。在本教程中,我不会过多地讲解“简写”的网格属性,但这个函数很简单,可以节省一些输入。与其输入一堆1fr
单位来定义网格,不如这样写:
.grid-container {
display: grid;
grid-template-rows: repeat(8, 1fr);
grid-template-columns: repeat(8, 1fr);
}
在网格上定位项目
让我们继续上面的 8x8 网格示例,并添加一些网格项。默认情况下,当我们添加 3 个网格项(没有宽度/高度,也没有其他网格属性)时,我们会得到以下结果。
在 CSS 网格术语中,“网格区域”表示网格项所占的空间。我们没有明确指示网格项的放置位置,因此每个网格项将占用一个“单元格”。以下是更详细的图示:
上面的视觉效果是“默认”的,但如果我想把这三个项目放在网格上的特定位置怎么办?也许是这样的:
在 CSS Grid 出现之前,实现这种布局需要付出巨大的努力。有了 Grid,一切都变得简单。要放置元素,你只需要从行/列的角度指定它们开始/结束的“线”。让我们来分解一下:
- 网格项目 1
- 从 RL-3 开始,到 RL-4 结束
- 始于 CL-3,止于 CL-4
- 网格项目 2
- 始于 RL-6,止于 RL-7
- 始于 CL-6,止于 CL-7
- 网格项目 3
- 始于 RL-8,止于 RL-9
- 始于 CL-8,止于 CL-9
让我们将这些信息转化为 CSS。以下所有属性都是网格项(而非容器)的属性。
.gi-1 {
grid-row-start: 3;
grid-row-end: 4;
grid-column-start: 3;
grid-column-end: 3;
}
.gi-2 {
grid-row-start: 6;
grid-row-end: 7;
grid-column-start: 6;
grid-column-end: 7;
}
.gi-3 {
grid-row-start: 8;
grid-row-end: 9;
grid-column-start: 8;
grid-column-end: 9;
}
当然,你也可以使用grid-row
和grid-column
属性来简化操作,但正如我之前提到的,最好还是使用普通属性,直到你真正熟悉 CSS Grid 为止!结果如下:
调整网格上项目的大小
默认情况下,我们的网格项会自然地填充一个网格“单元格”的空间。如果你查看上面的任何 Codepen,你都会看到我们网格的以下规格:
- Grid 容器宽 310px,高 310px,边框为 5px。因此,总可用空间宽 300px,高 300px(当 时
box-sizing: border-box
) - 网格为 8 x 8
- 每个网格“单元”宽 37.5px,高 37.5px(300px / 8)
我们可以通过两种方式调整它们的大小:
- 设置每个的明确宽度和高度
- 更改
grid-row-end
和grid-column-end
属性
选项 1 并不是很好,因为它会带来一系列我们还没有准备好的麻烦。
让我们尝试选项 2 并复制此操作:
虽然这看起来很困难,但我们可以使用与上面放置网格项相同的方法!这是 CSS。
.gi-1 {
grid-row-start: 3;
grid-row-end: 4;
grid-column-start: 3;
grid-column-end: 6;
}
.gi-2 {
grid-row-start: 6;
grid-row-end: 7;
grid-column-start: 6;
grid-column-end: 7;
}
.gi-3 {
grid-row-start: 8;
grid-row-end: 9;
grid-column-start: 8;
grid-column-end: 9;
}
结果如下:
回顾:设置网格
至此,您已经了解了设置 CSS 网格容器以及在其中放置和调整项目大小的基础知识。正如您在整个教程中所见,其结构相对简单。
<div class="grid-container">
<div class="grid-item gi-1">1</div>
<div class="grid-item gi-2">2</div>
<div class="grid-item gi-3">3</div>
</div>
.grid-container {
/* Generic CSS properties */
width: 310px;
height: 310px;
border: 5px solid #212226;
/* Grid container properties */
display: grid;
grid-template-rows: repeat(8, 1fr);
grid-template-columns: repeat(8, 1fr);
}
.gi-1 {
grid-row-start: 3;
grid-row-end: 4;
grid-column-start: 3;
grid-column-end: 6;
}
.gi-2 {
grid-row-start: 5;
grid-row-end: 7;
grid-column-start: 6;
grid-column-end: 7;
}
.gi-3 {
grid-row-start: 8;
grid-row-end: 9;
grid-column-start: 1;
grid-column-end: 9;
}
虽然这能让你入门,但还有很多内容需要讨论,当然还有一些我故意省略的细节。我们稍后会再讨论其中一些属性。现在,我们先讨论如何在网格容器内以及在其“网格区域”内对齐网格项。
基本 CSS 网格对齐
如果你仔细阅读了本教程,你可能会问,为什么我们要讨论“对齐”。我们不是刚刚讨论过对齐吗?
虽然我们讨论了网格项的位置和大小,但我们没有讨论它们的对齐方式,对齐方式可以通过两种方式实现:
- 在项目的“网格区域”内对齐
- 网格容器内的对齐
“网格区域”对齐
我们简要介绍了“网格区域”的含义,但这还不足以让您安心地结束实验。让我们回到 8x8 网格,定义一个我们想要实验的“网格区域”。
由于我们的网格尺寸为 8fr x 8fr,因此这个“网格区域”的尺寸为 4fr x 4fr。我们可以使用以下 HTML 和 CSS 代码在此空间中放置一个网格项:
<div class="grid-container">
<div class="grid-item gi-1">1</div>
</div>
.grid-container {
width: 310px;
height: 310px;
border: 5px solid #212226;
display: grid;
grid-template-rows: repeat(8, 1fr);
grid-template-columns: repeat(8, 1fr);
}
.gi-1 {
grid-row-start: 2;
grid-row-end: 6;
grid-column-start: 2;
grid-column-end: 6;
}
下面是一些样式:
我们从前面知道,单个网格“单元格”的总尺寸是 37.5px 宽和高。因此,此处的“网格区域”是 150px 宽和高(37.5 * 4),并且我们的网格项会进行拉伸以适应这些尺寸。
但是如果我们明确设置网格项的尺寸呢?让我们将此网格项设置为 120px:
.gi-1 {
width: 120px;
height: 120px;
}
当我们这样做时,我们的“网格区域”将保持不变,但现在我们的网格项不会填充 100%。
现在我们在“网格区域”内有了一个空白区域。那么,可以将我的网格项居中放置在“网格区域”内吗?当然可以!方法如下:
.grid-container {
justify-items: center; /* horizontal */
align-items: center; /* vertical */
}
以下是我们刚刚所做的:
看看我们的网格项现在是如何在“网格区域”居中的?这是代码——我建议在 CSS 中打开和关闭这两个 CSS 属性,以直观地了解它的作用。
看起来与 Flexbox Alignment 类似……?
在 Flexbox 中,我们使用以下两个 CSS 属性来对齐其 flex 容器内的项目:
justify-content
- 将项目对齐到“主轴”上align-items
- 将项目对齐在“横轴”上
使用 CSS Grid,如果我们想在“网格区域”内对齐网格项,我们使用以下属性:
justify-items
- 在网格区域内沿水平方向对齐项目align-items
- 在网格区域内沿垂直方向对齐项目
CSS Grid 也具有justify-content
和align-content
属性,但我们还没有准备好探索这些属性。
为了更清楚地说明这一点,我们在公式中添加另一个网格项。这次,它将占用 2fr x 2fr 个“网格区域”单位。这意味着“网格区域”的尺寸将是 75px 宽和 75px 高(37.5 * 2)。我们将第二个网格项的宽和高设置为 45px(任意值,小于 75px 即可),以便它有一些“空白空间”,这样我们就可以看到这些对齐属性的效果了。以下是我们正在处理的情况(尚未设置任何对齐属性):
让我们一步一步来。首先,我们将设置网格项在其“网格区域”内的垂直对齐方式。
.grid-container {
align-items: center; /* vertical */
}
以下是我们得到的结果:
现在,我们将使用 设置水平对齐justify-items
。
.grid-container {
align-items: center; /* vertical */
justify-items: center; /* horizontal */
}
如您所见,我们的网格项现在位于其“网格区域”的中心。
相关代码如下:
在本教程中,我将为您提供可视化效果,帮助您了解正在发生的事情,但如果没有它们,我们如何验证这些事情呢?
很高兴你问到这个问题——如果你打开 Google Chrome 开发者工具(其他浏览器如 Firefox 也可以),你会看到如下内容:
请注意我们的项目如何完美地位于其“网格区域”的中心。
对齐整个网格
到目前为止,我们一直在“完美条件”下工作:
.grid-container {
width: 310px;
height: 310px;
border: 5px solid #212226;
display: grid;
grid-template-rows: repeat(8, 1fr);
grid-template-columns: repeat(8, 1fr);
}
我们的网格容器 HTML 元素有 300px 的“可用空间”,这些空间被网格本身均匀填充(因为我们的grid-template
规则)。repeat(8, 1fr)
规则的意思是:“我想要 8 行 8 列,并且我希望每个网格单元均匀地划分 300px 的可用空间”。因此,在我们目前的示例中,每个网格单元的宽度和高度均为 37.5px(300 / 8)。
如果我们将规则改为这样会怎样?
.grid-container {
width: 310px;
height: 310px;
border: 5px solid #212226;
display: grid;
grid-template-rows: repeat(8, 25px);
grid-template-columns: repeat(8, 25px);
}
我们的网格容器可用空间为 300px,但现在我们的网格宽度和高度均为 200px(25px * 8)。我们的网格比网格容器小。以下是默认状态下的视觉效果。
让我们重新放上网格项。由于我们减小了网格的尺寸,所以我们需要减小网格项的尺寸。记住:
- 网格项 1 的“网格面积”等于 4x4,相当于 100x100 像素(25 * 4 = 100)
- 网格项 2 的“网格面积”等于 2x2,即 50x50 像素(25 * 2 = 50)
为了确保网格项小于它们的“网格区域”,我们将它们分别设为 50x50 和 25x25 像素(在每种情况下恰好是它们的“网格区域”大小的一半)。
到目前为止,我们的 CSS 看起来是这样的:
.grid-container {
width: 310px;
height: 310px;
border: 5px solid #212226;
display: grid;
grid-template-rows: repeat(8, 25px);
grid-template-columns: repeat(8, 25px);
}
.gi-1 {
color: #1E4040;
background-color: #71D99E;
grid-row-start: 2;
grid-row-end: 6;
grid-column-start: 2;
grid-column-end: 6;
width: 50px;
height: 50px;
}
.gi-2 {
color: #71D99E;
background-color: #3A8C7D;
grid-row-start: 7;
grid-row-end: 9;
grid-column-start: 2;
grid-column-end: 4;
width: 25px;
height: 25px;
}
让我们来练习一下网格项对齐的技巧。将以下 CSS 添加到网格容器中:
.grid-container {
justify-items: end;
align-items: end;
}
这将重新定位两个项目,使其占据其“网格区域”的右下角。如下所示:
justify-content 和 align-content
我们终于可以看看这两个属性的作用了。再次强调,如果网格尺寸小于网格容器尺寸,这两个属性将使整个网格在网格容器内对齐。让我们继续,通过将这些属性添加到容器中,使网格居中。
.grid-container {
justify-content: center;
align-content: center;
}
以下是我们得到的结果:
在上图中,发生的事情显而易见。整个网格位于网格容器的中心,并且网格项位于其“网格区域”的末尾。一旦我们将其转化为代码,就不那么明显了:
不过,再次强调,您可以打开开发者工具,将鼠标悬停在网格上,以便更清楚地了解情况。开发者工具不会高亮显示我们之前提到的“网格区域”,所以我手动添加了它们。由于我们将网格项定义为网格区域大小的完美倍数,因此它们可以完美地容纳在网格单元格中。
更多网格对齐示例
对我来说,为每种可能的对齐组合创建视觉效果会非常繁琐。这就是备忘单的用途。不过,我想再给你几个例子,以便更好地理解这些网格对齐属性。
示例 1
示例 2
示例 3
CSS 网格基础知识总结
至此,我已经向你展示了 CSS 网格的基础知识。你还可以用它做很多事情,但核心概念我们已经掌握了。
在继续本教程之前,您应该能够轻松地解释如何在 CSS 网格系统上下文中使用以下 CSS 属性。
- 容器属性
display: grid
- 激活网格grid-template-rows
/grid-template-columns
- 定义网格中的行数/列数justify-content
/align-content
- 对齐整个网格(如果它小于其容器)justify-items
/align-items
- 将网格项在其“网格区域”(在项目级别定义)内对齐
- 网格项属性
grid-row-start
/grid-row-end
- 定义项目的水平“网格区域”grid-column-start
/grid-column-end
- 定义项目的垂直“网格区域”
快速 CSS 网格挑战
凭借您目前的知识,您应该能够从本教程一开始就创建设计:
这是一个入门 Codepen(只需点击“fork”按钮即可将其复制到您的帐户)以及解决方案:
- 入门 - https://codepen.io/zg_dev/pen/JjENBKX
- 解决方案 - https://codepen.io/zg_dev/pen/gOgWjMo
- 解决方案(使用简写网格属性) - https://codepen.io/zg_dev/pen/WNRjKEL
中级 CSS 网格
本教程的剩余部分将探讨 CSS 网格的属性以及如何充分利用该系统。虽然以下内容并非有效使用 CSS 网格的必要条件,但我强烈建议您全部阅读。我并不打算涵盖 CSS 网格的所有细节,而是指出我们迄今为止尚未涉及的最有用的部分。
CSS 网格“间距”
在网页上创建网格时,您很可能需要在网格项之间留出一些间距。我们可以使用 padding、margin 甚至一些 Flexbox 技巧来创建这种间距,但有了 CSS 网格的强大功能,就无需再这样做了。我们称之为“gutter”(间距),使用row-gap
和column-gap
属性可以轻松实现。
让我们从基本的 3x3 网格开始。
以下是创建此功能所需的 CSS Grid 属性:
<div class="grid-container">
<div class="grid-item gi-1">1</div>
<div class="grid-item gi-2">2</div>
<div class="grid-item gi-3">3</div>
<div class="grid-item gi-4">4</div>
<div class="grid-item gi-5">5</div>
<div class="grid-item gi-6">6</div>
<div class="grid-item gi-7">7</div>
<div class="grid-item gi-8">8</div>
<div class="grid-item gi-9">9</div>
</div>
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
}
让我们添加一些排水沟:
.grid-container {
row-gap: 10px;
column-gap: 10px;
}
是的,就是这么简单。只需添加所需的“间距”宽度,您的网格项就会神奇地以响应式的方式自行分隔开来。
为了巩固之前的一些概念,让我们缩小网格的尺寸,并将其置于容器的中心。记住,网格容器内有 300px 的可用空间,因此,如果我们将每个网格“单元格”的宽/高设置为 50px,则网格的总尺寸现在为 50 + 50 + 50 = 150px。完成后,我们可以使用justify-content
和align-content
使网格在网格容器中居中。以下是我为实现此目的所做的更改:
.grid-container {
/* REMOVE THIS
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
*/
/* ADD THIS */
grid-template-rows: repeat(3, 50px);
grid-template-columns: repeat(3, 50px);
justify-content: center;
align-content: center;
}
CSS 网格自动流
您还记得我们在 Flexbox 教程中讨论过的flex-direction
属性吗?
如果将值设置为row
,则弹性项目将水平对齐。如果设置为column
,则弹性项目将垂直对齐。
使用 CSS Grid,我们没有“主轴”和“横轴”的概念,但我们有能力改变网格元素的自然流动。
假设您有一个包含以下 HTML 和 CSS 的基本网格:
<div class="grid-container">
<div class="grid-item gi-1">1</div>
<div class="grid-item gi-2">2</div>
<div class="grid-item gi-3">3</div>
<div class="grid-item gi-4">4</div>
</div>
.grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
}
由于我们尚未明确定义每个网格项在网格上的位置,您希望它们位于何处?
默认情况下,它们在网格上水平对齐,如果项目过多,则会溢出到下一行。在本例中,我们在网格中定义了 3 列和 4 个项目,因此前三列将水平对齐,最后一个项目将换行到下一行。
如果我们愿意,可以明确地将每个网格项垂直堆叠,但这需要大量工作!如果你想更改这些网格项的“流向”,你只需要 属性grid-auto-flow
,它的作用类似于 Flexbox 的flex-direction属性。最常见的是,你会使用row
(默认值)或column
。让我们将其更改为column
,看看会发生什么。
.grid-container {
grid-auto-flow: column;
}
就这样,我们的网格看起来完全不同了!
你也可以将其设置为dense
,这样就会在网格中查找空白处,并将较小的网格项放入其中(即使它们的顺序不正确)。我不会在这里介绍这一点,但你可以随意尝试一下,因为它可能是一个图片网格的良好用例。
显式网格与隐式网格
假设我们有这个网格:
<div class="grid-container">
<div class="grid-item gi-1">1</div>
<div class="grid-item gi-2">2</div>
<div class="grid-item gi-3">3</div>
<div class="grid-item gi-4">4</div>
</div>
.grid-container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
}
我们没有明确地放置或调整网格项的大小,这意味着它们默认会做两件事:
- 沿行方向流动(得益于将
grid-auto-flow
属性的默认值设置为row
) - 每个项目都会拉伸以填充其“网格区域”(由于
justify-items
的默认值为stretch
)
由于我们定义了 3 列 1 行,前 3 个网格项将占据第一行,而第 4 个网格项将……占据第二行?但是等等,我们还没有定义第二行……
第四个网格项是我们所说的隐式网格的一部分。
让我们回顾一下。我们的显式网格是一个 1 x 3(行 x 列)的网格,由以下属性定义:
.grid-container {
display: grid;
grid-template-rows: 1fr; /* Defines 1 row that fills the entire grid space */
grid-template-columns: repeat(3, 1fr); /* Defines 3 columns of equal space */
}
自然地,这些属性可以让 3 个项目完美地适应网格。如果我们有 4 个或更多项目,它们将开始占据隐式网格。默认情况下,“额外”项目将根据grid-auto-flow
属性流动,并占用与其内容大小相等的空间。
我们可以使用grid-auto-rows和grid-auto-columns属性来改变这种行为。与其根据内容大小调整隐式网格的大小,不如给它指定一些尺寸。
.grid-container {
display: grid;
/* Explicit Grid */
grid-template-rows: 1fr;
grid-template-columns: repeat(3, 1fr);
/* Implicit Grid Behavior */
grid-auto-flow: row; /* This is the default, but setting it anyways for demonstration */
grid-auto-rows: 100px;
grid-auto-columns: 50px;
}
这意味着放置在隐式网格上的任何网格项都将具有 100px 的高度(行高)和 50px 的宽度(列宽)。让我们继续将第四个网格项放置在此隐式网格上:
.gi-4 {
grid-row-start: 2;
grid-row-end: 3;
grid-column-start: 4;
grid-column-end: 5;
}
请记住,我们的显式网格如下所示:
在上面的 CSS 中,我们说的是:“将网格项 4 放置在 RL-2 和 RL-3 之间,以及 CL-4 和 CL-5 之间”。但是等等,这些根本不存在?!
当然,它们不存在,但我们仍然可以在上面放置网格项。显式网格会缩小,我们的网格项将根据和属性在隐式网格上放置和调整大小,在本例中分别为 100px 和 50px。因此,我们的第四个网格项将为 100px 高和 50px 宽。如下所示:grid-auto-rows
grid-auto-columns
下面,我更详细地强调了这些概念:
看到这个之后,你可能会问:“我们为什么需要这个?”。
如果你正在构建一个不知道有多少个项目的网格,那么设置grid-auto-rows
和grid-auto-columns
属性是一个好习惯。如果你想要一些灵活性,可以添加如下内容:
.grid-container {
grid-auto-rows: minmax(100px, auto);
grid-auto-columns: minmax(50px, auto);
}
通过这样做,您声明了所有填充隐式网格的网格项的最小宽度和高度分别为 50px 和 100px。如果这些网格项的内容大于这些尺寸,则它们的宽度和高度将设置为自动。
使用 CSS 网格的另一种方法
我们快要完成了。我们唯一还没讲到的属性是grid-template-areas
属性。我把它留到最后讲,因为它不是必须用到的,但它提供了一种定义“网格区域”的简单方法。到目前为止,我们使用以下方法放置网格项:
我们首先定义网格:
.grid-container {
width: 310px;
height: 310px;
border: 5px solid #212226;
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
}
然后,我们使用网格“线”明确放置每个网格项:
.gi-1 {
grid-row-start: 1;
grid-row-end: 2;
grid-column-start: 1;
grid-column-end: 3;
}
.gi-2 {
grid-row-start: 2;
grid-row-end: 3;
grid-column-start: 1;
grid-column-end: 2;
}
.gi-3 {
grid-row-start: 1;
grid-row-end: 4;
grid-column-start: 3;
grid-column-end: 4;
}
利用grid-template-areas
,我们可以用不同的方式实现相同的结果:
.grid-container {
grid-template-areas:
"item1 item1 item3"
"item2 . item3"
". . item3";
}
.gi-1 {
grid-area: item1;
}
.gi-2 {
grid-area: item2;
}
.gi-3 {
grid-area: item3;
}
这里的语法看起来很奇怪,但它是一种更“直观”的 CSS 网格使用方式。在.grid-container
样式中,请注意我们用空格分隔了 3 个引号组。由于我们定义了一个 3x3 网格,因此引号组代表行,而各个项目代表列。此外,.
代表“空白处”。
在每个网格项样式中,我们都定义了该项目的“别名”,如您所见,它在上面使用grid-template-areas
。
让我对此进行格式化,以便更清楚地了解正在发生的事情:
"item1 item1 item3"
"item2 . item3"
" . . item3"
注意到“item3”填满了整个第三列吗?看看上面的 Codepen。
您可以快速了解这里发生的情况,对于某些人来说,这是一种更直观的创建网格的方式。
命名网格线
在上一节中,我们讨论了一种更直观的创建 CSS 网格的方法。本节将提供另一种替代方案。
你已经看到过这个:
出于教育目的,我将网格“线”标记为“CL-n”和“RL-n”以指示“列线 1”和“行线 1”,但当我们将其转换为 CSS 时,它看起来像这样:
.gi-1 {
grid-row-start: 1;
grid-row-end: 2;
grid-column-start: 1;
grid-column-end: 3;
}
如果我们需要一种方法来记住线名,我们可以在网格容器上分配它们:
.grid-container {
display: grid;
grid-template-rows: [RL-1] 1fr [RL-2] 1fr [RL-3] 1fr [RL-4];
grid-template-columns: [CL-1] 1fr [CL-2] 1fr [CL-3] 1fr [CL-4];
}
然后在网格项上使用它们:
.gi-1 {
grid-row-start: RL-1;
grid-row-end: RL-2;
grid-column-start: CL-1;
grid-column-end: CL-3;
}
这是一个演示:
CSS Grid 和 Flexbox 协同工作
我在本教程的开头说过,我们会通过一个例子来展示 Flexbox 和 CSS Grid 如何完美地协同工作。作为复习,以下是在 Flexbox 和 Grid 之间做出选择时可以参考的一些经验法则:
在以下情况下使用网格:
- 您需要完全控制行和列(例如:整页布局、图库项目)
- 您想要明确定义布局,而不管内容大小如何(例如,如果您使用 Flexbox,并且由于大量高度和宽度而变得非常复杂,您可能应该使用 Grid)
在以下情况下使用 Flexbox:
- 您的内容是一行或一列(例如:水平导航栏、垂直侧边栏)
- 您希望项目的大小决定布局
下面,我创建了两个交替使用 CSS Grid 和 Flexbox 的示例。建议花 10 分钟阅读每个示例的 CSS。
图片库
一个简单的图片库,带有“粘性”导航栏和页脚。这种布局以前需要一些“hacky”的 CSS 技巧才能实现,但有了 Grid 和 Flexbox,就相对简单了。具体来说,在阅读代码时,您应该关注以下几个关键部分:
- 注意我是如何“网格套网格”的。这完全没问题。
- 注意,主窗口
grid-container
具有100vh
高度,并将主要内容(图片)的单位设置为1fr
。这意味着导航栏和页脚将获得其明确定义的 70px 和 40px 高度,而图片网格将占据视口(可见屏幕)中的剩余空间。 - 注意,我给
.pictures
网格设置了一项overflow: scroll
设置。这确保了在网格中添加图片不会导致导航栏或页脚在滚动时消失。
“圣杯”布局
这是我们 CSS 网格教程的“最终项目”。在你的 Web 开发生涯中,你可能需要多次构建这个(或类似的)项目,所以我想给你一个模板作为入门!
如果您想和我一起构建它,我制作了一个YouTube 视频向您展示如何操作。
离开之前
感谢阅读!如果你喜欢这篇文章并愿意支持我,你可以做两件事,我会非常感激。
- 在 Twitter 上关注我 @zg_dev
- 订阅下面我的电子邮件列表