写在前面的话
本文为译文,因为笔者也是最近开始尝试翻译文章,如有翻译地不合理或者不正确的地方,欢迎指出。原文地址:CSS Grid: illustrated introduction。
正文开始
我还记得第一次学习CSS知识的时候,我是如何兴奋地学习使用float和inline展示元素在想要的布局位置。我想知道如果当时有一个二维布局系统,我会作何反应。事实上,即使到现在我还是感到兴奋,因为我们编写CSS的方式以及我们编写标记语言的方式改变了一切。使用CSS网格,使得我们构建响应式的、动态的、源码顺序独立的布局比之前更加容易。
注意,源码顺序独立只是根据字面翻译,我理解的含义是编写的html标签的顺序跟页面上展示标签的顺序无关,我们可以通过CSS 网格样式任意修改元素在页面上的展示顺序。
在本文中,我们将学习使用所有的CSS网格知识来构建简单的和一些不太容易的布局。我们将定义一切CSS网格知识,然后接着将挖掘得更深一点,看看我们可以使用CSS网格实现什么样的布局。话虽如此,如果你已经准备好学习一种新的布局方式,吃下红色药丸,我会告诉你兔子洞到底有多深。
1.在我们开始之前
在我们开始学习之前,我想提出一些或许你也可能有的一些顾虑,同时确保我们熟悉CSS网格的基础知识和术语。
Q & A
Q:CSS网格会替代flex-box布局吗?
A:当然,CSS网格不会替代flex-box,这两种布局是针对不同的使用场景。事实上,它们还可以很好地搭配使用。我们可以在CSS网格布局中使用flex布局,反之亦然。
Q:CSS网格布局和flex-box布局有什么不同?
A:它们有很多不同点,最主要的区别就是flex-box是一种一维的布局系统,而CSS网格是一种二维的布局系统。让我们看看下图:
Q:为什么不使用Bootstrap呢?
A:我认为最好的回答是引用Jen Simmons的表述:
使用CSS Grid越多,使我更加确信,在其上添加抽象层没有任何好处,CSS网格就是是嵌入浏览器的CSS布局框架。
Q:CSS 网格为用于生产环境准备好了吗?
A:这取决于你的网站,你的网站是否要兼容IE,Opera mini,Blackberry browser,Baidu mobile等浏览器?如果回答是否定,你可以直接用于生产环境。如果回答是肯定,那么你可以在支持它的浏览器(不需要添加前缀的浏览器占比:91.61%)使用它通过编写@supportsCSS规则:
@supports (display: grid) {
div {
display: grid;
}
}
基础知识
基本上,一个网格布局中可以分解为两个元素:容器(grid container)和项目(grid-items)。
从图二中我们可以看到,网格容器就是一个行和列的集合。一行就是两根连续的水平线之间的空间,一列就是两根连续的竖线之间的空间。一行被称为一个轨道(track),对于列也一样。因此网格轨道就是两根平行的网格线之间的空间。
每一个轨道可以有一个或者多个网格单元。cell就是最小的、最基本的网格单元,它是四根相交的网格线之间的空间。如果我们把多个网格单元组合起来就形成了一个网格区域,值得一提的是网格区域必须是一个矩形区域,例如我们不能产生一个T形状的网格区域。
网格线从1开始到任何编号你可以显示或者隐式的定义,最后的网格线编号可以被设为-1,而在它之前的网格线编号为-2,依此类推,这个在稍后的例子中会很方便。在图二中列网格线的编号是从1增加6(或者是从-6到-1),行的网格线编号是从1增加到5(或者是从-5到-1)。
网格线的编号可以被显示的定义如果你在CSS样式中明确设置,它也可以被隐式地设置如果它是被浏览器动态地生成。
最后要补充地是,网格单元可以通过空格和间距分开,这些间距被称为“槽”(gutters),但是我们一般把它们当成间距。
2.CSS网格的基本属性
好了,这样我们可以开始实现一些网格布局。首先我们讨论网格容器可以使用的所有属性,然后再介绍一下关于网格项(item)的一些属性。
在本节中,让我们考虑如下模板:
<div class="grid-container">
<div class="grid-item">grid item 1</div>
<div class="grid-item">grid item 2</div>
<div class="grid-item">grid item 3</div>
<div class="grid-item">grid item 4</div>
<div class="grid-item">grid item 5</div>
<div class="grid-item">grid item 6</div>
<div class="grid-item">grid item 7</div>
<div class="grid-item">grid item 8</div>
<div class="grid-item">grid item 9</div>
</div>
网格容器
Display
CSS网格是使用display属性的grid值定义的 ,因此,使用上面的模板定义网格布局,我们应该这样做:
.grid-containter {
display: grid;
}
列 & 行
我们可以通过使用grid-template-rows和grid-template-columns属性定义网格的行和列:
.grid-container {
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr auto 2fr;
}
或者我们可以使用grid-template属性首先定义grid-template-rows然后定义grid-template-columns(使用斜杆分隔):
.grid-container {
grid-template: 1fr auto 2fr / 1fr 1fr 1fr 1fr;
}
顺便提一下,fr是一个分数单位,因此1fr就是可用空间的一个部分。
Repeat function
repeat函数表示轨道(track)列表的一个重复片段,因此我们可以得到和上面一样的模板通过如下代码:
.grid-container {
grid-template: 1fr auto 2fr / repeat(4, 1fr);
}
阅读文档repeat 可以了解怎么使用auto-fit和auto-fill动态地添加轨道(track)。
Minmax function
minmax函数定义了一个尺寸的范围,该范围大于或者等于最小值,小于或者等于最大值,我们可以将该函数和repeat一起使用:
.grid-container {
grid-columns: repeat(3, minmax(100px, 1fr));
}
间距
我们可以使用row-gap给行与行之间设置间距,同样也可以使用column-gap给列与列设置间距:
.grid-container {
row-gap: 5px;
column-gap: 10px;
}
我们也可以使用gap属性先定义行间距然后定义列间距:
.grid-container {
gap: 5px 10px;
}
如果row-gap和column-gap值是一样的,我们只需要指定一个值。
网格项
指定一个网格项在网格布局中的开始和结束位置,我们基本上使用四个属性,让我们先看下它们的定义。
| 属性 | 定义 |
|---|---|
| grid-row-start | grid-row-start属性通过提供一个行、一个span或者什么都不提供来指定网格行在网格中的开始位置(自动) |
| grid-row-end | grid-row-end属性通过提供一个行、一个span或者什么都不提供来指定网格行在网格中的结束位置(自动) |
| grid-column-start | grid-column-start属性通过提供一个行、一个span或者什么都不提供来指定网格列在网格中的开始位置(自动) |
| grid-column-end | grid-column-end属性通过提供一个行、一个span或者什么都不提供来指定网格列在网格中的结束位置(自动) |
或者我们也可以使用这些属性的简写形式:
| 属性 | 定义 |
|---|---|
| grid-row | grid-row属性是grid-row-start和grid-row-end的简写属性,用于指定网格行中网格项的大小和位置 |
| grid-column | grid-column属性是grid-column-start和grid-column-end的简写属性,用来指定网格列中网格项的大小和位置 |
基本模板间距
考虑一下我们在本节开始时使用的标记,假设我们想要第三个网格项占用四个单元格而不是一个(我们希望它跨越两个网格列和两个网格行),效果就像图三一样,我们该怎么做了?
我们可以像这样实现:
// Grid container
.grid-container {
display: grid;
gap: 10px;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 1fr);
}
// Grid item (third)
.grid-container .grid-item:nth-child(3) {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 1;
grid-row-end: 3;
// or
grid-column: 1 / 3;
grid-row: 1 / 3;
// or
grid-column: 1 / span 2;
grid-row: 1 / span 2;
// or
grid-column: -5 / span 2; // because we have 4 columns
grid-row: -4 / span 2; // because we have 3 rows
}
注意,第三个网格项其实就是图三中的第一个,这与CSS网格可以(第一次)拥有源码顺序独立性有关。当我们讨论
grid-auto-flow的时候,我们还会讲到这个知识点。
如果你想玩它,探索不同的解决方案,点击这里。
3.高级模板
还有一些更高级的属性可以帮助您按照自己的需要调整模板。在这小节,我们将学习这些属性并知道如何在CSS中使用它们。
在这个小节,我们考虑如下模板:
<div class="grid-container">
<div class="grid-item header">Header</div>
<div class="grid-item content">Content</div>
<div class="grid-item navbar">Navbar</div>
<div class="grid-item meta">Meta</div>
<div class="grid-item footer">Footer</div>
</div>
使用我们前面学过的知识,我们可以使用如下的CSS样式使得它看起来像一个基本的网站布局:
.grid-container {
grid-template: repeat(6, 1fr) / repeat(12, 1fr);// rows then columns
}
.grid-container .header {
grid-column: 1 / -1;
grid-row: 1 / 2;
}
.grid-container .navbar {
grid-column: 1 / 2;
grid-row: 2 / -1;
}
.grid-container .content {
grid-column: 2 / -1;
grid-row: 2 / -2;
}
.grid-container .footer {
grid-column: 2 / -1;
grid-row: -2 / -1;
}
.grid-container .meta {
grid-column: -3 / -1;
grid-row: 2 / 4;
}
现在假设我们想要导航条(在右边)宽一点,目前,它跨越了一列,但是我们想要它跨越两列。为了实现这个效果,我们需要改变.navbar的位置,同时也要改变.content和.footer的位置,因为当前.navbar是从列1到列2,而.footer和.content是从列2一直到最后。
如果每次都要修改元素的位置,那就太单调了。如果有一种办法来告诉CSS网格自动为我们去做这件事,那就太好了。当然,不止有一种方法可以实现,至少有两种方法。
命名行
第一种解决方案就是命名特定的行,然后我们可以用它的别名而不是它的编号来引用它,让我们尝试实现它。
.grid-container {
grid-template-rows: repeat(6, 1fr);
grid-template-columns: 1fr 1fr [content-start navbar-end] repeat(10, 1fr);
}
在上面的代码中,我们使用包含别名的简单方括号为第三行命名(单一的行可以有多个别名)。然后我们修改前面提到的元素的CSS代码:
.grid-container .navbar {
grid-column: 1 / navbar-end;
grid-row: 2 / -1;
}
.grid-container .content {
grid-column: content-start / -1;
grid-row: 2 / -2;
}
.grid-container .footer {
grid-column: content-start / -1;
grid-row: -2 / -1;
}
实现的效果像下面这样:
尝试对其它的行实现相同的效果(前面的:header-row-end / content-row-start)可以点击下面的代码片段
元素的模板区域
第二种解决方案是使用模板区域,grid-template-areas属性用来指定命名的网格区域。这个属性有个奇怪的CSS语法,但我们像这样使用它:
.grid-container {
grid-template-areas:
'h h h h h h h h h h h h'
'n n c c c c c c c c c c'
'n n c c c c c c c c c c'
'n n c c c c c c c c c c'
'n n c c c c c c c c c c'
'n n f f f f f f f f f f';
}
.grid-container .navbar {
grid-area: n;
}
.grid-container .content {
grid-area: c;
}
.grid-container .footer {
grid-area: f;
}
.grid-container .header {
grid-area: h;
}
.grid-container .meta {
grid-column: -3 / -1;
grid-row: 2 / 4;
}
我们使用grid-template-areas定义网格容器区域,然后使用grid-area将网格项放置到需要的网格区域。值得注意的是,所有的区域必须是矩形。
注意,我们没有为
.meta元素使用grid-area,这是因为,目前没有可以使用这种方式来叠加元素的方法,至少我不知道。
你可以继续玩它,可用的代码点击这里。
4.隐式行和网格流
考虑如下代码:
<div class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
</div>
<style>
.grid-container {
grid-template-columns: repeat(3, minmax(100px, 1fr));
grid-template-rows: 80px;
}
.grid-container .grid-item:nth-child(2) {
grid-row: span 2;
}
.grid-container .grid-item:nth-child(3) {
grid-column: span 3;
}
</style>
我们有一个三列的网格,我们想让第二个网格项跨越两行,第三个跨越三列,结果如下图:
这看起来很糟糕,所以这里发生了什么?首先,第二个元素比一个元素稍微高了一点,因为我们想要它高一倍,但是看起来根本不像是高了一倍的高度。同样,从3到6的网格项也没有第一个高。
隐式行
这与我们显示的设置第一行CSS样式有关:grid-template-rows: 80px,而其它的行被隐式地创建了,因此,第二行几乎是不可见的,因为它是空的,而其它行的大小取决于它们内容所需要的。
我们可以像下面代码这样使用grid-auto-rows属性设置隐式创建的行高度来修复这个问题:
.grid-container {
grid-template-columns: repeat(4, minmax(100px, 1fr));
grid-template-rows: 80px;
grid-auto-rows: 100px;
}
这样看起来就像下图一样:
这样看起来好一点了,但是我们仍然可以做得更好。注意这些空白的空间,为什么我们没有使用它们放置网格4,5,6项,为了做到这样,我们可以使用grid-auto-flow。
网格流
grid-auto-flow属性控制自动放置算法的工作方式,准确地指定自动放置的网格项如何流入网格,它可以有多个参数(你可以点击这里了解更多),但是在这里,我们关心的只有一个:dense。这个值告诉浏览器将网格项放置在任何足够大的空间:
.grid-container {
grid-auto-flow: dense; // default is row
}
这样我们的网格看起来就很漂亮了:
结论
讲到这里,我们需要处理大量的信息,但是有了这些知识,我们涵盖了大量的CSS网格属性,因此我们可以愉快地在应用中使用CSS网格布局。这篇文章是一系列文章中的第一篇,在下一篇中,我们将使用网格实现三个实际的例子,因此跟上节奏。
我希望你一如既往地从文章中学到了有用的知识,快乐编程!