【css 系列】Grid 入门到精通完整指南

2,892 阅读14分钟

前言

无论作为一个合格的web/移动端开发者,或者一个UI/UX工程师,精通css中的Grid布局可以让你设计出各种各样的布局。 让我们先用一张图来看看它能做什么事情... 如果你觉得你可以轻松做到,那你可以看看哪里可以优化的更简洁一些。

img

为什么需要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。这里的黄线是一个列网格线的例子。

img

4. Grid Cell(网格单元)

由网格线组成的密闭空间称之为grid cell。通常来说,我们用grid cell来表示最小的单元。也就是两条连续的行线和两个连续的列线构成的空间。下面这个例子中的grid cell是由row 1 row 2 和col 2 col 3构成的。 img

5. Grid Track(网格行踪)

由连续两条相邻的grid line构成的区域称为grid track。它可以是一整行或一整列,下面这个例子是由row 2 和 row 3构成的
img

6. Grid Area(网格区域)

我们可以把grid area看作由4条grid line组成的封闭区域,grid area可以由任何数量的grid cell来组成。例如黄色的grid area 由row grid line 1, 3 和 column grid line 1, 3组成。 img

2. Grid 常用性质总揽

Grid看似复杂,但我们总用到的属性其实不多,首先我先罗列一下所有常用的属性,大家可以在学之前对照一下自己哪一个不太熟悉仔细去看,跳过自己掌握的部分。

Grid Container常用属性Grid Items常用属性
displaygrid-column-start
grid-template-columnsgrid-column-end
grid-template-rowsgrid-row-start
grid-template-areasgrid-row-end
grid-templategrid-column
grid-column-gapgrid-row
grid-row-gapgrid-area
grid-gapjustify-self
justify-itemsalign-self
align-itemsplace-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-columnsgrid-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: 相邻两条行线或列线的距离,可以是长度,百分比(%),或者比例(fr unit)。不可以省略
  • 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;
}

img

同时,我们还可以在规定长度的同时给每条线命名,方便以后的引用(语法: [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;
}

img

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>

img

  • 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 , sidebarfooter 四个组件,我们想要的效果如下: img

整个顶部行将由标题区域组成。中间的行将由两个主要区域、一个空单元格和一个侧边栏区域组成。最后一行都是页脚。我们按照这个排版来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>;

例如,我们想定义如下的结构,可以通过一句话来完成而不需要声明三句。

img

.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-gaprow-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;
}

img 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-itemsalign-items属性和justify-items属性的合并简写形式。 注意:此值应用于容器内的所有的网格项目(items)

  • start: 元素位于单元格容器的开头。
  • end: 元素位于单元格容器的结尾。
  • center: 元素位于单元格容器的中心。
  • stretch: 元素在单元格内被拉伸以适应容器。
.container {
  justify-items: start | end | center | stretch;
  align-items: start | end | center | stretch;
}

img

如果你想具体控制某一个单元格的排列方式,可以使用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>;
}

img

img

3.8 grid-auto-columns, grid-auto-rows

grid-auto-columnsgrid-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;
}

img

这个例子中我们告诉.item-b从第5列开始到第6列结束,但是我们从来没有定义过第5列或第6列。因为我们引用了不存在的列,所以会创建宽度为0的隐式跟踪来填补空白。我们可以使用grid-auto-columnsgrid-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依次罗列。不同的排列效果如下

img

4. Grid子类属性(Grid Items) 详解

提示:以下属性作用在grid item上不会产生影响:float, display: inline-block, display: table-cell, vertical-aligncolumn-*

4.1 grid-column-start, gird-column-end, grid-column, grid-row-start, grid-row-end, grid-row

grid-column-start/endgrid-row-start/end 用来通过指定网格线的开始和结束位置来决定网格项目(grid item)的具体位置。我们可以想象一个矩形又四个点构成,我们当确定了这4个点的位置才能最终确定矩形的位置和大小。

grid-row-startgrid-column-start 是行和列的网格线的起始位置(可以理解为网格的左上顶点),grid-row-endgrid-column-end 是行和列在网格线的终止位置(可以理解为网格的右下顶点)。

  • 如果没有声明grid-column-end/grid-row-end,默认情况下该项将跨越1个单元格长度。
  • 项目可以相互重叠。你可以使用z-index来控制它们的堆叠顺序。

grid-column属性是grid-column-startgrid-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;
}

img

例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;
}

img

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";
}

img

例2:grid-row-start + grid-column-start + grid-row-end + grid-column-end的缩写

.item-d {
  grid-area: 1 / col4-start / last-line / 6;
}

img

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>;
}

img

justify-self exampleEnter a caption for this image (optional) img

img

参考文献

CSS Grid Layout Module Level

Auto-Sizing Columns in CSS Grid

CSS align-items 属性 | 菜鸟教程CSS

CSS Grid 网格布局教程