前言
无论作为一个合格的web/移动端开发者,或者一个UI/UX工程师,精通css中的Grid布局可以让你设计出各种各样的布局。 让我们先用一张图来看看它能做什么事情... 如果你觉得你可以轻松做到,那你可以看看哪里可以优化的更简洁一些。
为什么需要Grid布局
CSS Grid布局可以说是CSS中最强大的布局系统。首先它是一个二维的,这意味着它可以同时处理列和行,不像flex布局很大程度上是一个一维系统。我们可以使用它来灵活排版出各种各样的设计,这在以前通过flex或者float是很难或甚至不可能的。
文章适合人群
如果你不了解css grid 或者想深入探索grid的奥秘,这篇文章非常适合你。
学习感悟
CSS Grid在有些人看起来有点令人生畏的新语法和布局思想,但其实它本质相当简单,可以分解成几个强大的概念,当你把它们结合在一起使用时,你会很惊它的强大之处,并且相信会永远改变你在web中创建布局的方式。
1. 学Grid之前需要掌握的术语
在正式讲解grid的之前,我觉得很有必要澄清一些关键的概念和术语,因为有些相似的概念如果不特殊说明一下你很容易让自己混乱,我在刚接触的时候也是这样,为了减少大家走弯路,不了解grid的朋友们建议看完。我在下面的讲解中也会反复提到这些概念!
1. Grid Container(网格容器)
每当我们声明display: grid的地方这个就是grid container。它作为所有直接grid items的容器,换句话说item的parent是container。 例如container class中有3个item。
<div class="container">
<div class="item item-1"> </div>
<div class="item item-2"> </div>
<div class="item item-3"> </div>
</div>
.container {
display: grid
}
2. Grid Item(网格项目)
凡是网格容器中直接的children称之为grid item。 这里需要注意:直系的children才属于grid item, 而children的children不属于grid item。 下面这个例子中item作为container的grid item但是sub-item却不是grid item
<div class="container">
<div class="item"> </div>
<div class="item">
<p class="sub-item"> </p>
</div>
<div class="item"> </div>
</div>
3. Grid Line(网格线)
构成网格结构的分隔线称为grid line。它们可以是垂直的(“列网格线”)或水平的(“行网格线”),位于行或列的任何一边都可以称为grid line。这里的黄线是一个列网格线的例子。
4. Grid Cell(网格单元)
由网格线组成的密闭空间称之为grid cell。通常来说,我们用grid cell来表示最小的单元。也就是两条连续的行线和两个连续的列线构成的空间。下面这个例子中的grid cell是由row 1 row 2 和col 2 col 3构成的。
5. Grid Track(网格行踪)
由连续两条相邻的grid line构成的区域称为grid track。它可以是一整行或一整列,下面这个例子是由row 2 和 row 3构成的
6. Grid Area(网格区域)
我们可以把grid area看作由4条grid line组成的封闭区域,grid area可以由任何数量的grid cell来组成。例如黄色的grid area 由row grid line 1, 3 和 column grid line 1, 3组成。
2. Grid 常用性质总揽
Grid看似复杂,但我们总用到的属性其实不多,首先我先罗列一下所有常用的属性,大家可以在学之前对照一下自己哪一个不太熟悉仔细去看,跳过自己掌握的部分。
| Grid Container常用属性 | Grid Items常用属性 |
|---|---|
| display | grid-column-start |
| grid-template-columns | grid-column-end |
| grid-template-rows | grid-row-start |
| grid-template-areas | grid-row-end |
| grid-template | grid-column |
| grid-column-gap | grid-row |
| grid-row-gap | grid-area |
| grid-gap | justify-self |
| justify-items | align-self |
| align-items | place-self |
| place-items | |
| justify-content | |
| place-content | |
| grid-auto-columns | |
| grid-auto-rows | |
| grid-auto-flow | |
| grid |
3. Grid父类的属性 (Grid Container)详解
3.1 display
grid的类型有两种,第一种是block-level的,第二种是inline-level的
- grid:生成block-level (块级元素) 的grid
- inline-grid: 生成inline-level (行内元素) 的grid
简单总结来说,grid container可以看作整个网格的外区域,里面的items构成一个个具体的网格。两者结合在一起构成了整体的网格解构。指定一个容器采用网格布局如下:
.container {
display: grid | inline-grid;
}
<div class="container">
<div class="item1"></div>
<div class="item2"></div>
<div class="item3"></div>
<div class="item4"></div>
<div>
3.2 grid-template-columns / grid-template-rows
如果你只有5分钟时间来学习网格,那我认为你最需要学习的就是这一部分,这是我们写出一个grid布局的灵魂。 思考一下:通过哪些属性可以完全确定一个表格呢 (不考虑表格内容)?
- 行数,列数
- 行宽,列宽
那无非
grid-template-columns和grid-template-rows就是用来帮助我们定义grid整体结构的。语法定义如下:
.container {
grid-template-columns: [line-name] <track-size> [line-name] <track-size> ... [line-name];
grid-template-rows: [line-name] <track-size> [line-name] <track-size> ... [line-name];
}
- track-size: 相邻两条行线或列线的距离,可以是长度,百分比(%),或者比例(
frunit)。不可以省略 - line-name: 我们可以给每一条行线或者列线命名,这方便我们在需要的地方引用。注:可以省略
根据下面这个图为例,我们来定义一下行和列的宽度。 例如每行从左到右依次长度为:40px, 50px, auto, 50px, 40px 每个auto = (total length - fix length) / auto的个数。
.container {
grid-template-columns: 40px 50px auto 50px 40px;
grid-template-rows: 25% 100px auto;
}
同时,我们还可以在规定长度的同时给每条线命名,方便以后的引用(语法: [name])。上面这张图的每条线对应的名字初始化如下。
.container {
grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px [end];
grid-template-rows: [row1-start] 25% [row1-end] 100px [third-line] auto [last-line];
}
我们还可以给每条线多个名称,只需要在名称和名称之间用空格分开即可。例如:
.container {
grid-template-rows: [row1-start] 25% [row1-end row2-start] 25% [row2-end];
}
下面我总结了一些常用的小技巧
- Tips 1: 学会使用
repeat()。有时候重复写同样的值非常麻烦,尤其网格很多时。我们可以使用repeat()函数来简化重复操作。
.container {
grid-template-columns: repeat(3, 20px);
}
// 等于
.container {
grid-template-columns: 20px 20px 20px;
}
- Tips 2: 学会使用
fr控制比例。很多时候我们只想定义每部分占总空间的比例,而不想给出具体的长度,因为这对不同屏幕的大小适配十分有帮助。例如,定义三列按照1:2:1来划分。
.container {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
}
fr 之间穿插着px也是允许的。 计算公式:1 fr = (total length - fixed length) / total fr
.container {
display: grid;
grid-template-columns: 1fr 50px 1fr 1fr;
}
- Tips 3:
auto-fill关键字。有些时候我们不确定容器的大小,但是每个单元格的长度是固定的,这时候可以使用auto-fill关键字帮助我们自动填充来实现灵活布局。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
}
auto-fill
<div class="container grid-container--fill">
<div class="grid-element">
1
</div>
<div class="grid-element">
2
</div>
<div class="grid-element">
3
</div>
<div class="grid-element">
4
</div>
<div class="grid-element">
5
</div>
<div class="grid-element">
6
</div>
<div class="grid-element">
7
</div>
</div>
-
Tips 4: 学会使用
minmax()函数。minmax(<minValue>, <maxValue>)函数通过用户指定的一个长度范围,表示长度就在这个范围之中。它接受两个参数,分别为最小值和最大值。 下面这个例子表示每个100px <= 列宽 <= 1fr。这种结构在平时布局的时候十分常见,希望大家可以掌握它。
.container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
3.3 grid-template-areas
grid-template-areas 用来定义每个格子出现在哪个区域。
- 通过引用grid-area属性指定的网格区域的名称来定义网格模板。
- 重复网格区域的名称会导致内容跨越这些单元格。
.表示一个空单元格。
假设我们有header , main , sidebar 和footer 四个组件,我们想要的效果如下:
整个顶部行将由标题区域组成。中间的行将由两个主要区域、一个空单元格和一个侧边栏区域组成。最后一行都是页脚。我们按照这个排版来grid-template-areas 即可:
.item-a {
grid-area: header;
}
.item-b {
grid-area: main;
}
.item-c {
grid-area: sidebar;
}
.item-d {
grid-area: footer;
}
.container {
display: grid;
grid-template-columns: repeat(4, 100px);
grid-template-rows: auto;
grid-template-areas:
"header header header header"
"main main . sidebar"
"footer footer footer footer";
}
注意⚠️:当我们用grid-area命名时,区域两端的grid line实际上会自动命名而不需要手动定义。假如grid-area 的名称是foo,那么该区域的起始行和起始列的名称将是foo-start,它的最后一行和最后一列的名称将是foo-end。这意味着有些行可能有多个名称,例如上面示例中最左边的行,它将有三个名称:header-start、main-start和footer-start。
3.4 grid-template
grid-template 可以帮我们把grid-template-rows, grid-template-columns, 和grid-template-areas整合到一句声明里。具体定义语法如下:
grid-template: <grid-template-rows> / <grid-template-columns>;
例如,我们想定义如下的结构,可以通过一句话来完成而不需要声明三句。
.container {
display: grid;
grid-template:
"one one one" 40px
"two three three" 40px
"two three three" 40px / 1fr 1fr 1fr;
}
3.5 column-gap, row-gap, gap
column-gap 和row-gap 用来指定网格线的大小。你可以把它想象成设置行与行或列与列之间宽度。
.container {
column-gap: <line-size>;
row-gap: <line-size>;
}
例如:我们想指定每列之间的宽度为10px 并且每行之间的宽度为15px 。我们可以进行如下操作
.container {
grid-template-columns: 100px 100px 100px;
grid-template-rows: 80px 80px 80px;
column-gap: 10px;
row-gap: 15px;
}
gap 可以帮助我们同时指定row-gap和col-gap, 语法: gap: <row-gap> <column-gap> 如果grid-gap省略了第二个值,浏览器认为第二个值等于第一个值。
3.6 justify-items, align-items, place-items
justify-items 用于设置单元格内容的水平位置(左中右)。
align-items属性设置单元格内容的垂直位置(上中下)。
place-items是align-items属性和justify-items属性的合并简写形式。
注意:此值应用于容器内的所有的网格项目(items)
- start: 元素位于单元格容器的开头。
- end: 元素位于单元格容器的结尾。
- center: 元素位于单元格容器的中心。
- stretch: 元素在单元格内被拉伸以适应容器。
.container {
justify-items: start | end | center | stretch;
align-items: start | end | center | stretch;
}
如果你想具体控制某一个单元格的排列方式,可以使用align-self 进行定制化。
3.7 justify-content, align-content, place-content
justify-content用于控制整个内容区域在容器里面的水平位置(左中右)
align-content用于控制整个内容区域的垂直位置(上中下)。
place-content 同时设置整体内容在水平和竖直方向的位置。
注意⚠️:只有子容器的 size 小于当前容器的size才有效
参数作用:
- start: 整体内容对齐容器的起始边框。
- end: 整体对齐容器的结束边框。
- center: 整体内容在齐容器内居中。
- stretch: 整体内容在让容器内被拉伸以适应容器。
- space-around: 每个项目两侧的间隔相等,项目之间的间隔比项目与容器边框的间隔大一倍。
- space-between: 项目与项目的间隔相等,项目与容器边框之间没有间隔。
- space-evenly: 项目与项目的间隔相等,项目与容器边框之间也是同样长度的间隔。
.container {
justify-content: start | end | center | stretch | space-around | space-between | space-evenly;
align-content: start | end | center | stretch | space-around | space-between | space-evenly;
place-content: <align-content> <justify-content>;
}
3.8 grid-auto-columns, grid-auto-rows
grid-auto-columns 和grid-auto-rows 用来指定这些隐式轨迹的宽度。
应用情景: 当网格中的网格项多于单元格时,或者当一个网格项被置于显式网格之外时,就会自动创建格子来满足需求。例如:网格只有2列,但是某一个项目指定在第5列。这时,浏览器会自动生成多余的网格,以便放置项目。
.container {
display: grid;
grid-template-columns: 60px 60px;
grid-template-rows: 90px 90px;
grid-auto-columns: 60px;
}
.item-a {
grid-column: 1 / 2;
grid-row: 2 / 3;
}
.item-b {
grid-column: 5 / 6;
grid-row: 2 / 3;
}
这个例子中我们告诉.item-b从第5列开始到第6列结束,但是我们从来没有定义过第5列或第6列。因为我们引用了不存在的列,所以会创建宽度为0的隐式跟踪来填补空白。我们可以使用grid-auto-columns和grid-auto-rows来指定这些隐式轨迹的宽度。
3.9 grid-auto-flow
grid-auto-flow 属性用于控制自动放置算法的工作方式。如果网格项没有指定放置位置,自动放置算法将自动放置这些项。
设定好自动放置属性后,容器的子元素会按照顺序,自动放置在每一个网格。默认的放置顺序是"先行后列",即先填满第一行,再开始放入第二行,即下图数字的顺序。
- row: 按照每一行来填充,当前行无法容纳后增添新的行,可以理解为'先行后列'(默认值)
- column: 按照每一列来填充,当前列无法容纳后增添新的列,可以理解为'先列后行'
- dense:如果后面出现更小的项目,就先填上网格中的缺口。
.container {
grid-auto-flow: row | column | row dense | column dense;
}
例如:我们有5个项目编号为1-5依次罗列。不同的排列效果如下
4. Grid子类属性(Grid Items) 详解
提示:以下属性作用在grid item上不会产生影响:float, display: inline-block, display: table-cell, vertical-align和column-*
4.1 grid-column-start, gird-column-end, grid-column, grid-row-start, grid-row-end, grid-row
grid-column-start/end 和 grid-row-start/end 用来通过指定网格线的开始和结束位置来决定网格项目(grid item)的具体位置。我们可以想象一个矩形又四个点构成,我们当确定了这4个点的位置才能最终确定矩形的位置和大小。
grid-row-start 和grid-column-start 是行和列的网格线的起始位置(可以理解为网格的左上顶点),grid-row-end 和grid-column-end 是行和列在网格线的终止位置(可以理解为网格的右下顶点)。
- 如果没有声明
grid-column-end/grid-row-end,默认情况下该项将跨越1个单元格长度。 - 项目可以相互重叠。你可以使用z-index来控制它们的堆叠顺序。
grid-column属性是grid-column-start和grid-column-end的合并简写形式,grid-row属性是grid-row-start属性和grid-row-end的合并简写形式。
具体定义如下:
<line>: 代表第几条网格线,行从左->右,列从上->下。span<number>: 该项目会跨越单元格的数量。例如,span 2代表从起始位置开始跨越两个单元格。span<name>: 该项目一直跨越直至下一条网格线的名字为auto: 自动放置、自动跨度或默认跨度
.item {
grid-column-start: <number> | <name> | span <number> | span <name> | auto;
grid-column-end: <number> | <name> | span <number> | span <name> | auto;
grid-row-start: <number> | <name> | span <number> | span <name> | auto;
grid-row-end: <number> | <name> | span <number> | span <name> | auto;
grid-column: <start-line> / <end-line> | <start-line> / span <value>;
grid-row: <start-line> / <end-line> | <start-line> / span <value>;
}
例1:item-a 起始行名字为row1-start 结束行为第3条行线,起始列为第2条行线,结束列名字为five 。
这里看起来很乱,但是为了帮助大家熟悉各种使用语法,实际我们写代码的时候只需要其中1中表达即可。
.item-a {
grid-column-start: 2;
grid-column-end: five;
grid-row-start: row1-start;
grid-row-end: 3;
}
===========
.item-a {
grid-column: 2 / five;
grid-row: row1-start / 3;
}
例2:item-b 起始行是第2条行线,结束行跨越两个单元格也就是第4条行线,起始列为第1条行线,结束列名字为col4-start 。
.item-b {
grid-column-start: 1;
grid-column-end: span col4-start;
grid-row-start: 2;
grid-row-end: span 2;
}
===========
.item-b {
grid-column: 1 / span col4-start;
grid-row: 2 / span 2;
}
4.2 grid-area
grid-area为项提供一个名称,以便使用grid-template-areas属性创建的模板可以引用该项目。或者,可以将此属性用作grid-row-start + grid-column-start + grid-row-end + grid-column-end的简写。
.item {
grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
}
例1:为item 命名,以便使用grid-template-areas属性创建的模板。
.item-a {
grid-area: header;
}
.item-b {
grid-area: main;
}
.item-c {
grid-area: sidebar;
}
.item-d {
grid-area: footer;
}
.container {
display: grid;
grid-template-columns: repeat(4, 100px);
grid-template-rows: auto;
grid-template-areas:
"header header header header"
"main main . sidebar"
"footer footer footer footer";
}
例2:grid-row-start + grid-column-start + grid-row-end + grid-column-end的缩写
.item-d {
grid-area: 1 / col4-start / last-line / 6;
}
4.3 justify-self, align-self, place-self
justify-self属性设置单元格内容的水平位置(左中右),跟justify-items属性的用法完全一致,但只作用于单个项目。
align-self属性设置单元格内容的垂直位置(上中下),跟align-items属性的用法完全一致,也是只作用于单个项目。
place-self 属性是align-self属性和justify-self属性的合并简写形式。如果省略第二个值,会认为这两个值相等。
具体定义如下:
- start: 元素位于单元格容器的开头。
- end: 元素位于单元格容器的结尾。
- center: 元素位于单元格容器的中心。
- stretch: 元素在单元格内被拉伸以适应容器。
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
place-self: <align-self> <justify-self>;
}
justify-self exampleEnter a caption for this image (optional)