[译] 理解 CSS 网格布局:创建一个网格容器

823 阅读11分钟

原文链接:Understanding CSS Grid: Creating A Grid Container,by Rachel Andrew

本系列介绍 CSS 网格布局,分三篇发布。本篇是这个系列的第一篇。

读完本篇文章,你将学到:

  • 使用 display: griddisplay: inline-grid 创建网格容器。
  • 使用 grid-template-columnsgrid-template-rows 设置行和列。
  • 使用 grid-auto-columsgrid-auto-rows 属性设置隐式轨道(implicit tracks)的尺寸。

创建容器

与弹性布局类似的是,使用网格布局前,需要先创建网格容器。网格容器可以使用 display: grid 创建,使用此种方式创建的网格布局元素表现为块级元素特性,会自动生出一个 网格格式化上下文(grid formatting context),它的所有直接子元素将自动变成 网格项目

但你不会立即看到效果,不是因为布局没有起作用。而是因为在你没有为网格设置列的情况下,默认是一列布局的,其中网格项目一个叠着一个自上而下排列,表现的像是块状元素。

“像是块状元素”可能让你有点懵,为了证明网格项目不管元素类型为何,都表现的像是块状元素。这里我举个例子:

<div class="grid">
  <div>Item one</div>
  <div>Item Two</div>
  A string of text with a <span>span element</span> in the middle.
</div>
This string of text follows the grid.

<style>
.grid { display: grid; }
</style>

.grid  放置的不都是块状元素 div,这里的 A string of text with a <span>span element</span> in the middle. 猛看起来就是一行。

我们来看看实际效果(demo):

image.png

发现 <span> 两边裸露的文本都各自独立,各站一行了。使用 Firefox Grid Inspector 查看,发现有 5 行。

image.png

可见,网格项目不管元素类型(即使是裸露的文本节点)为何,在网格格式化上下文的作用下,都会表现为块状元素的特征。

当然,你还可以使用 display: inline-grid 创建网格。那 gridinline-grid 区别在哪呢?这类似于 inlineinline-block 的关系。inline-grid 网格外在表现的像是个行内元素,可以跟其他行内元素排在一行,内在则表现的是个块状元素,可以设置宽高等属性。

还是上面的例子,我们将 CSS 稍作修改:

.grid {display: inline-grid; }

来看看结果(demo):

image.png

能跟后面的文本排在一行了。

行和列

为了让网格像网格,我们需要用 grid-template-columns 和 grid-template-rows 设置行和列。这两个属性接收的值称为 track-list。看看规范里是怎么说的:

这两个属性接收的是用空格分隔的 track list,由 line names 和 track sizing functions 组成。grid-template-columns 指定的是网格列的 track list,grid-template-rows 则是指定网格行的 track list。

下面展示了一些有效的 track-list 取值:

grid-template-columns: 100px 100px 200px;创建一个三列的网格:第一列 100px,第二列 100px,第三列 200px
grid-template-columns: min-content max-content fit-content(10em);创建一个三列的网格:第一列 min-content,第二列 max-content,第三列不超过 10em(前提是本列轨道的 max-content 是大于 10em 的;否则表现的是不超过 max-content)。
grid-template-columns: 1fr 1fr 1fr;使用 fr 单位创建一个三列网格。每列网格平分可用空间(available space),各自分得可用空间的 1/3。
grid-template-columns: repeat(2, 10em 1fr);使用 repeat() 函数创建一个 4 列网格。等同于 10em 1fr 10em 1fr,也就是 10em 1fr 重复两遍的结果。
grid-template-columns: repeat(auto-fill, 200px);在网格中,用尽可能多的 200px 宽的列。如果有剩余空间的话,就会留下一个空白间隙。
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));在网格中,用尽可能多的 200px 宽的列填充容器。如果有剩余空间,再将剩余空间均分给每个创建的列。
grid-template-columns: [full-start] 1fr [content-start] 3fr [content-end] 1fr [full-end];创建一个三列的网格:第一和第三列各分得可用空间的 1/5,中间一列分得可用空间的 3/5。我们通过在使用方括号里放置名称的形式,为网格线命名

大家可以看到,有很多种创建 track list 的方式。接下来,我们来看看这些值是如何起作用的。而且,还提供了一些小技巧,讲讲为什么要这样用。

使用长度单位

你可以使用长度或百分比单位创建轨道。如果所有的轨道尺寸加起来小于当前的容器的可用尺寸的,那么网格项目默认会在容器左边(在像英文这样的语言排版规则下)排列,多余的空间则留在右面。这是因为 align-contentjustify-content 属性默认值为 startdemo)。

image.png

当然,你还可以使用像 min-contentmax-content 关键字和 fit-content() 函数。

min-content 是在元素内容不发生溢出情况下的最小尺寸,用在列上之后,就等于列内最长的那个单词或最大的固定尺寸(fixed-size)元素的尺寸。

max-content 则是元素内容在完全不折行的情况下的最大尺寸,用在列上之后,就等于列中文本在完全不折行情况下的展开尺寸。使用时要注意可能会发生的溢出问题。

fit-content() 要待传入一个值后才能使用。这个值表示轨道能增长到的最大尺寸,轨道在不折行、自由延伸到这个临界值的时候,就停止继续增长了。所以,结果表现是,轨道长度总是小于传入的值的(前提是:这个传入值是介于 min-contentmax-content 之间的)。

举个例子:

<div class="grid">
  <div>Item One</div>
  <div>Item Two Item Two</div>
  <div>Item Three Item Three Item Three</div>
  <div>Item Four</div>
</div>

<style>
.grid {
  display: grid;
  grid-template-columns: min-content max-content fit-content(10em);
}
</style>

效果如下(demo):

image.png

第三列在达到 10em 的尺寸后,便不再增长。

如果所有轨道占用的空间比容器本身的空间还大,就会发生溢出。特别在使用百分比单位设置轨道尺寸时,一定要注意百分比总值加起来不要超过 100%,否则会溢出的。

fr 单位

在 Grid 中可以使用另一种方式,帮助你避免使用百分比值时,去手动计算——fr 单位。它不是长度单位,因此不能在 calc() 中使用,它是用来表示当前网格容器中可用空间的。

1fr 1fr 1fr 这个 track list 举例:可用空间被均分成三等份,每个轨道获得其中的 1/3。而对 2fr 1fr 1fr 来说,第二列和第三列都得到了 1/4 的可用空间,第一列则得到 1/2 的可用空间。

<div class="grid">
  <div>Item One</div>
  <div>Item Two Item Two</div>
  <div>Item Three Item Three Item Three</div>
  <div>Item Four</div>
</div>

<style>
.grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
}
</style>

效果(demo):

image.png

这里需要注意的是,分配给 fr 单位的是 可用空间(avaible space),而非整个容器空间。如果网格轨道中包含固定尺寸(fixed-size)的元素或不折行长单词的话,会优先将这些元素布局好,然后剩下的空间(即可用空间)才是留给 fr 分配的。

再举一个例子——我把第三个网格项目里的单词 ItemThree 间的空格去掉了,这将导致出现一个非常长的单词。这个时候布局的话,就不得不要先考虑它了。

GIF.gif

第三个项目的 1fr 已经不再起作用了,宽度完全由内容撑开。之后第一、第二个项目分配剩下来的可用空间,第一个项目占 2/3,第二个项目占 1/3。

还可以将 fr 混合其他长度单位一起使用,某些场景里还是很有用的。比如,我们有一个三列组件,两边两列的尺寸是固定的,中间一列则是自动充满余下的空间。

<div class="grid">
  <div>Fixed</div>
  <div>Flexible</div>
  <div>Fixed</div>
</div>

<style>
.grid {
  display: grid;
  grid-template-columns: 100px 1fr 100px;
}
</style>

效果(demo):

image.png

或者你还可以设置一个轨道的尺寸为 fit-content(300px),其他的呢是 1fr。这会让第一个轨道的不大于 300px(只占据需要的空间),而使用了 fr 的轨道则会自动扩充余下的空间。

比如,我们在第一个轨道了放了一张 max-width: 100% 图片,那么这张图片最宽只能到 300px。使用这种方式可以创建一个非常灵活的组件布局。

.grid {
  grid-template-columns: fit-content(300px) 1fr;
}

效果(demo):

GIF.gif

repeat() 函数

repeat() 函数可以帮你避免重复写一样的值。比如,下面两行代码的作用是一样的:

grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(12, 1fr);

repeat() 的第一个参数表示重复的次数,第二个参数是 track list。track list 可以使用多个值。

还可以把 repeat() 函数作为 track list 的一部分使用。例如,下面的代码会创建一个 1fr 的轨道,三个 200px 的轨道以及一个 1fr 轨道。

grid-template-columns: 1fr repeat(3,200px) 1fr

除了可以使用固定的表示重复次数的数值,你还可以在 repeat() 中使用 auto-fillauto-fit 这两个关键字。使用其中之一替代元素写的固定数值,网格容器一行里将会尽可能多(至少保每个网格项目 200px 宽)的布局网格项目。

代码:

<div class="grid">
  <div>Item One</div>
  <div>Item Two</div>
  <div>Item Three</div>
  <div>Item Four</div>
</div>

<style>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, 200px);
  width: 500px;
}
</style>

效果(demo):

image.png

这里的网格容器使用了固定长度(500px),两个轨道都是 200px,不能将容器完全划分,因此右面余下了一些剩余空间。

当然,我们可以用 minmax() 这个函数,函数的第一个参数表示能接受的最小值,第二个参数则表示最大值。我们将最小尺寸设置成 200px,最大尺寸则设置成 1fr,这样如果有剩余空间,也会给填满。

代码:

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  width: 500px;
}

效果(demo):

image.png

前面我提到了两个关键字:auto-fillauto-fit。如果你的内容超过了一行显示的,那么使用这两个关键字将看不见区别。区别是在只有一行内容的时候。

使用 auto-fill 的时候,即使网格容器中已经没有可排版的内容了,但依旧会生成新的轨道。

<div class="grid">
  <div>Item One</div>
</div>

<style>
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  width: 500px;
}
</style>

效果(demo):
image.png
而改用 auto-fit 的话,那个空的轨道会被压缩(collapsed):

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  width: 500px;
}

效果(demo):

image.png

使用 Firefox Grid Inspector 查看,可以看见(箭头所指的方向)最后一根列线的编号还是 3,不过空间收缩为 0 、和第二列线重合了。有三根列线,说明我们是可以放两个轨道的,只不过因为第二个没有,就没有给空间、被第一个挤去用了。

image.png

命名网格线

最后的例子我们来看看命名网格线的使用。使用网格布局的时候,默认每个网格线都有个编号的,我们还可以为网格线命名。网格的名称是定义在一对方括号里的,每一条网格线可以取多个名字,名字之间使用空格分隔。下面举一个例子:下面的这个 track list,每条网格线起了两个名字。

grid-template-columns: [main-start sidebar-start] 1fr [sidebar-end content-start] 4fr [content-end main-end]

你可以为网格线取任何名字,除了 span,因为 span 是保留字,可以在排布网格项目时使用。

在这个系列的下一篇,我将会讨论如何基于网格线排布网格项目,以及如何使用命名网格线。

显示和隐式网格

使用 grid-template-columns 和 grid-template-rows 属性设置那块网格区域,称为 显式网格(explicit grid)。因为这块网格区域里的轨道尺寸被显式设置了的。

如果网格内容很多,超出了删除两个属性所设置的区域之外,那么这块区域称为 隐式网格(implicit grid)。在这块区域里排布的网格项目默认按照本身的大小渲染的。如何控制这些超出范围之外的行和列呢——使用 grid-auto-rowsgrid-auto-columns 属性。比如,希望所有隐式网格列至少 200px 宽,如果内容更多的话,就按照实际内容自然显示,那么你就可以使用下面的声明:

grid-auto-rows: minmax(200px, auto)

如果是想让奇数行保持自然高,偶数行保持 100px 或者其他的尺寸,则可以试试多值语法。

grid-auto-rows: auto 100px

效果(demo):

image.png

(正文完)


广告时间(长期有效)

我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。

(完)