【重学CSS】图文并茂!一次性搞懂 Grid 布局,实现“布局自由”~

5,187 阅读36分钟

本文正在参加「金石计划」

flag:每月至少产出三篇高质量文章~

欢迎关注我的另外几篇文章:

image.png

1、CSS Grid 简介

CSS 网格布局(又称 "网格 "或 "CSS网格"),是一种基于二维网格的布局系统,与过去的任何网页布局系统相比,它完全改变了我们设计用户界面的方式。它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样复杂的布局。以前,只能通过复杂的 CSS 框架达到的效果,现在浏览器内置了。

先来看一个以前我们稍觉得有点复杂的前端布局:

而现在使用 CSS Grid ,仅使用极少量的代码就能实现。

在兼容性方面,截至目前,几乎所有的浏览器都对 CSS Grid 提供了原生的支持(浏览器支持情况)。还不用起来更待何时~

image.png

image.png

要使用 CSS Grid,你必须用 display: grid 定义一个容器元素为网格,用 grid-template-columnsgrid-template-rows 设置列和行的大小,然后用 grid-columngrid-row 将其子元素放入网格。

2、几个重要的 CSS Grid 术语

在深入了解网格的概念之前,我们需要了解几个重要的术语。

2.1 容器(Grid Container)

Grid 容器 指的是应用 display: grid 的元素。它是所有网格项目的直接父元素。

下面这个例子中,container 是网格的容器:

<div class="container"> 
    <div class="item item-1"></div> 
    <div class="item item-2"></div> 
    <div class="item item-3"></div>
</div>

2.2 项目(Grid Items)

Grid 项目 指的是网格容器的直接后代元素。

下面这个例子中的 item 元素是网格项目,但 sub-item 不是:

<div class="container">
  <div class="item"> </div>
  <div class="item">
    <p class="sub-item"> </p>
  </div>
  <div class="item"> </div>
</div>

2.3 网格线(Grid Lines)

网格线(Grid Lines) 指的是构成网格结构的分界线。它们可以是垂直的(“列网格线”)或水平的(“行网格线”),并且位于行或列的任一侧。请看下图:

网格线的图示

2.4 单元格(Grid Cell)

单元格(Grid Cell) 指的是相邻两行和相邻两列网格线之间的空间。它是网格的一个 "单元"。

下面是行网格线1和2,列网格线2和3之间的网格单元:

网格单元的图示

2.5 网格轨道(Grid Tracks)

网格轨道(Grid Track) 指的是两条相邻网格线之间的空间。你可以把它们看成是网格的列或行。

下面是第1行和第2行网格线之间的网格轨迹:

网格轨道的图示

2.6 网格区域(Grid Area)

网格区域(Grid Area) 指的是由四条网格线包围的总空间。一个网格区域可以由任何数量的网格单元组成。

下面是行网格线1和3,列网格线2和4之间的网格区域:

网格区域的图示

2.7 网格间距(Gaps)

网格间距(Gaps) 指的是轨道之间的间隙。为了确定尺寸,这些东西就像普通的轨道一样。你不能在缝隙中放置内容,但你可以将网格项目跨越它。

带间隙的网格的图示

3、Grid 容器属性

3.1 display

将该元素定义为一个网格容器,并为其内容建立一个新的网格格式化上下文。

可选的值:

  • grid:生成一个块状的网格
  • inline-grid:生成一个 inline-level 的网格
.container { 
    display: grid | inline-grid; 
}

通过嵌套元素(又称子网格)向下传递网格参数的能力已被移至 CSS网格规范的 Level 2

详细的可看这里:传送门

3.2 grid-template-columns 和 grid-template-rows

容器指定了网格布局以后,接着就要划分行和列。grid-template-columns属性定义每一列的列宽,grid-template-rows属性定义每一行的行高。

可选的值:

  • <track-size>:可以是一个长度、一个百分比,或者使用 fr 单位表示网格中自由空间的一部分
  • <line-name>:一个你选择的任意的名字
.container {
  grid-template-columns: ...  ...;
  /* e.g. 
      1fr 1fr
      minmax(10px, 1fr) 3fr
      repeat(5, 1fr)
      50px auto 100px 1fr
  */
  grid-template-rows: ... ...;
  /* e.g. 
      min-content 1fr min-content
      100px 1fr max-content
  */
}

网格线会从这些分配中自动分配正数(-1是最末一行的备用数)。

你可以选择明确地命名这些行。注意行名的括号语法。

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

Grid with user named lines

请注意,一个行可以有一个以上的名字。例如,这里的第二行将有两个名字:row1-endrow2-start

.container {
  grid-template-rows: [row1-start] 25% [row1-end row2-start] 25% [row2-end];
}

如果你的定义包含重复的部分,你可以使用 repeat() 符号来简化。

.container {
  grid-template-columns: repeat(3, 20px [col-start]);
}

它等同于:

.container {
  grid-template-columns: 20px [col-start] 20px [col-start] 20px [col-start];
}

如果多行同名,可以通过行名和计数来引用它们:

.item {
  grid-column-start: col-start 2;
}

fr 允许你将轨道的大小设置为网格容器自由空间的一部分。例如,这将把每个项目设置为网格容器宽度的三分之一。

.container {
  grid-template-columns: 1fr 1fr 1fr;
}

可用空间指的是非弹性项目之外的空间。在下面的示例中,fr 单位可用的空间指的是除50px之外的空间:

.container {
  grid-template-columns: 1fr 50px 1fr 1fr;
}

3.3 grid-template-areas

通过引用 grid-area 属性指定的网格区域的名称,定义一个网格模板。重复一个网格区域的名称会使内容跨越这些单元格。一个句号表示一个空单元。语法本身提供了一个可视化的网格结构。

可选的值:

  • <grid-area-name>:用grid-area指定的一个网格区域的名称。
  • .:句号表示一个空的网格单元
  • none:没有定义网格区域
.container {
  grid-template-areas: 
    "<grid-area-name> | . | none | ..."
    "...";
}

下面来一个例子:

.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: 50px 50px 50px 50px;
  grid-template-rows: auto;
  grid-template-areas: 
    "header header header header"
    "main main . sidebar"
    "footer footer footer footer";
}

这将创建一个四列宽、三行高的网格。整个顶行将由页眉区域组成。中间一行将由两个主要区域、一个空单元和一个侧边栏区域组成。最后一行是所有的页脚。

Example of grid-template-areas

你声明中的每一行都需要有相同数量的单元格。你可以使用任何数量的相邻句点来声明一个空单元。只要这些句号之间没有空格,它们就代表一个单元格。

请注意,你用这种语法命名的不是线,只是区域。当你使用这种语法时,区域两端的线实际上是被自动命名的。如果你的网格区域的名称是foo,那么该区域的起始行和起始列的名称将是 foo-start,而最后一行和最后一列的名称将是 foo-end。这意味着有些行可能有多个名字,比如上例中最左边的一行,将有三个名字:header-startmain-startfooter-start

3.4 grid-template

这是 grid-template-rows + grid-template-columns + grid-template-areas 的简写。

可选的值:

  • none:将所有三个属性设置为初始值
  • <grid-template-rows> / <grid-template-columns>:将 grid-template-columns 和 grid-template-rows 分别设置为指定值,并将 grid-template-areas 设置为none。
.container {
  grid-template: none | <grid-template-rows> / <grid-template-columns>;
}

它还接受一种更复杂但相当方便的语法来指定所有这三种情况。下面是一个例子。

.container {
  grid-template:
    [row1-start] "header header header" 25px [row1-end]
    [row2-start] "footer footer footer" 25px [row2-end]
    / auto 50px auto;
}

这等价于:

.container {
  grid-template-rows: [row1-start] 25px [row1-end row2-start] 25px [row2-end];
  grid-template-columns: auto 50px auto;
  grid-template-areas: 
    "header header header" 
    "footer footer footer";
}

由于 grid-template 不会重置隐含的网格属性 (grid-auto-columnsgrid-auto-rows, 和 grid-auto-flow),这可能是你在大多数情况下想要做的,所以建议使用 grid 属性而不是 grid-template

还可以这样:

.container {  
    display: grid;  
    grid-template:  
        "head head head" minmax(150px, auto)  
        "sidebar content content" auto  
        "sidebar footer footer" auto / 1fr 1fr 1fr;  
}

3.5 column-gap 和 row-gap

column-gap(老的写法是 grid-column-gap) 和 row-gap(老的写法是 grid-row-gap) 用来指定网格线的大小。你可以把它看作是设置列/行之间的沟槽的宽度。

可选的值:

  • <line-size>:一个长度值
.container {
  /* 新的标准 */
  column-gap: <line-size>;
  row-gap: <line-size>;

  /* 老的写法 */
  grid-column-gap: <line-size>;
  grid-row-gap: <line-size>;
}

例如:

.container {
  grid-template-columns: 100px 50px 100px;
  grid-template-rows: 80px auto 80px; 
  column-gap: 10px;
  row-gap: 15px;
}

Example of grid-column-gap and grid-row-gap

gutters 只在列/行之间创建,而不是在外边缘。

注意:grid-前缀 将被移除,grid-column-gapgrid-row-gap 将被重新命名为 column-gaprow-gap。在 Chrome 68+Safari 11.2 Release 50+Opera 54+ 中已经支持无前缀的属性。

3.6 gap

row-gap 和 column-gap 的简写。

可选的值:

  • <grid-row-gap> <grid-column-gap>:长度值
.container {
  /* 新的标准 */
  gap: <grid-row-gap> <grid-column-gap>;

  /* 老的写法 */
  grid-gap: <grid-row-gap> <grid-column-gap>;
}

例如:

.container {
  grid-template-columns: 100px 50px 100px;
  grid-template-rows: 80px auto 80px; 
  gap: 15px 10px;
}

如果没有指定 row-gap,它将被设置为与 column-gap 相同的值。

注意:grid-前缀 已被废弃(但谁知道呢,可能永远不会从浏览器中移除)。本质上,grid-gap 改名为 gap。在 Chrome 68+Safari 11.2 Release 50+Opera 54+ 中已经支持无前缀的属性。

3.7 justify-items

沿着内联(行)轴对齐网格项目(与沿着块(列)轴对齐的 align-items 相反)。这个值适用于容器内的所有网格项目。

可选的值:

  • start:使项目与单元格的起始边缘齐平。
  • end:对齐项目,使其与单元格的末端边缘齐平
  • center:使项目在其单元格的中心位置对齐
  • stretch:填充整个单元格的宽度(这是默认值)
.container {
  justify-items: start | end | center | stretch;
}

例如:

.container {
  justify-items: start;
}

Example of justify-items set to start

.container {
  justify-items: end;
}

Example of justify-items set to end

.container {
  justify-items: center;
}

Example of justify-items set to center

.container {
  justify-items: stretch;
}

Example of justify-items set to stretch

这种行为也可以通过 justify-self 属性在单个网格项目上设置。

3.8 align-items

沿着块(列)轴对齐网格项目(与justify-items相反,它沿内联(行)轴对齐)。这个值适用于容器内的所有网格项目。

可选的值:

  • stretch - 填充整个单元格的高度(这是默认的)
  • start - 使项目与单元格的起始边缘齐平
  • end - 使项目与单元格的末端边缘齐平
  • center - 使项目在单元格的中心对齐。
  • baseline - 沿着文本基线对齐项目。基线有一些修饰词--第一条基线和最后一条基线,在多行文本的情况下,它们将使用第一行或最后一行的基线。
.container {
  align-items: start | end | center | stretch;
}

下面来看例子:

.container {
  align-items: start;
}

Example of align-items set to start

.container {
  align-items: end;
}

Example of align-items set to end

.container {
  align-items: center;
}

Example of align-items set to center

.container {
  align-items: stretch;
}

Example of align-items set to stretch

这种行为也可以通过 align-self 属性在单个网格项目上设置。

还有修饰关键字 safeunsafe(用法类似于 align-items: safe end)。safe 关键字的意思是 "尝试像这样对齐,但如果这意味着对齐一个项目,使其移动到不可访问的溢出区域,则不能这样做",而 unsafe 将允许将内容移动到不可访问的区域("数据丢失")。

3.9 place-items

place-itemsalign-itemsjustify-items 的简写属性。

可选的值:

  • <align-items> / <justify-items>:第一个值设置 align-items,第二个值设置justify-items。如果省略了第二个值,第一个值将被分配给两个属性。

更多细节,请看 align-itemsjustify-items

这对于超级快速的多方向居中非常有用:

.center {
  display: grid;
  place-items: center;
}

3.10 justify-content

有时,你的网格的总尺寸可能小于其网格容器的尺寸。如果你所有的网格项目都是用非灵活的单位(如 px)来确定大小,就会发生这种情况。在这种情况下,你可以设置网格在网格容器中的对齐方式。这个属性沿内联(行)轴对齐网格(与沿块(列)轴对齐网格的 align-content 相反)。

可选的值:

  • start:使网格与网格容器的起始边缘齐平。
  • end:使网格与网格容器的末端边缘齐平。
  • center:使网格在网格容器的中心对齐。
  • stretch:调整网格项目的大小,让网格填满网格容器的全部宽度
  • space-around:在每个网格项目之间放置一个均匀的空间,在远端有一半的空间。
  • space-between:在每个网格项之间放置一个均匀的空间,远端没有空间
  • space-evenly:在每个网格项目之间放置一个均匀的空间,包括远端。
.container {
  justify-content: start | end | center | stretch | space-around | space-between | space-evenly;    
}

下面来看实例:

.container {
  justify-content: start;
}

Example of justify-content set to start

.container {
  justify-content: end;    
}

Example of justify-content set to end

.container {
  justify-content: center;    
}

Example of justify-content set to center

.container {
  justify-content: stretch;    
}

Example of justify-content set to stretch

.container {
  justify-content: space-around;    
}

Example of justify-content set to space-around

.container {
  justify-content: space-between;    
}

Example of justify-content set to space-between

.container {
  justify-content: space-evenly;    
}

Example of justify-content set to space-evenly

3.11 align-content

有时,你的网格的总尺寸可能小于其网格容器的尺寸。如果你所有的网格项目都是用非灵活的单位(如 px)来确定大小,就会发生这种情况。在这种情况下,你可以设置网格在网格容器中的对齐方式。这个属性使网格沿着块(列)轴对齐(与justify-content相反,它使网格沿着内联(行)轴对齐)。

可选的值:

  • start:使网格与网格容器的起始边缘齐平。
  • end:使网格与网格容器的末端边缘齐平。
  • center:使网格在网格容器的中心对齐。
  • stretch:调整网格项目的大小,让网格填满网格容器的全部高度。
  • space-around:在每个网格项目之间放置一个均匀的空间,在远端有一半的空间。
  • space-between:在每个网格项之间放置一个均匀的空间,远端没有空间。
  • space-evenly:在每个网格项目之间放置一个均匀的空间,包括远端。
.container {
  align-content: start | end | center | stretch | space-around | space-between | space-evenly;    
}

下面来看例子:

.container {
  align-content: start;    
}

Example of align-content set to start

.container {
  align-content: end;    
}

Example of align-content set to end

.container {
  align-content: center;    
}

Example of align-content set to center

.container {
  align-content: stretch;    
}

Example of align-content set to stretch

.container {
  align-content: space-around;    
}

Example of align-content set to space-around

.container {
  align-content: space-between;    
}

Example of align-content set to space-between

.container {
  align-content: space-evenly;    
}

Example of align-content set to space-evenly

3.12 place-content

place-contentalign-contentjustify-content 属性的简写。

可选的值:

  • <align-content> / <justify-content>:第一个值设置 align-content,第二个值设置justify-content。如果省略了第二个值,则第一个值将被分配给两个属性。

Edge 外,所有主要浏览器都支持 place-content 简写属性。

更多细节,请看 align-contentjustify-content

3.13 grid-auto-columns 和 grid-auto-rows

grid-auto-columnsgrid-auto-rows 用来指定任何自动生成的网格轨迹(又称隐式网格轨迹)的大小。当网格中的网格项多于单元格时,或者当一个网格项被放置在显式网格之外时,就会创建隐式轨道。(参见《显式网格和隐式网格的区别》)。

可选的值:

  • <track-size>:可以是一个长度,一个百分比,或者网格中自由空间的一部分(使用fr单位)。
.container {
  grid-auto-columns: <track-size> ...;
  grid-auto-rows: <track-size> ...;
}

为了说明隐性网格轨道是如何被创建的,看看这个:

.container {
  grid-template-columns: 60px 60px;
  grid-template-rows: 90px 90px;
}

Example of 2x2 grid

这里创建了一个 2 x 2 的网格。

但现在想象一下,你用 grid-columngrid-row 来定位你的网格项目,像这样:

.item-a {
  grid-column: 1 / 2;
  grid-row: 2 / 3;
}
.item-b {
  grid-column: 5 / 6;
  grid-row: 2 / 3;
}

Example of implicit tracks

我们告诉 .item-b第5列 开始,在 第6列 结束,但我们从未定义过 第5列第6列。因为我们引用了不存在的行,所以创建了宽度为0的隐式轨道来填补空白。我们可以使用 grid-auto-columnsgrid-auto-rows 来指定这些隐式轨道的宽度。

.container {
  grid-auto-columns: 60px;
}

grid-auto-columns-rows

3.14 grid-auto-flow

如果你有没有明确放置在网格上的网格项目,自动放置算法会启动,自动放置这些项目。这个属性控制了自动摆放算法的工作方式。

可选的值:

  • row:告诉自动放置算法依次填入每一行,必要时添加新行(默认值)
  • column:告诉自动放置算法依次填入每一列,必要时添加新的列
  • dense:告诉自动放置算法,如果较小的项目出现在网格中,则尝试在网格中较早的地方填上洞。
.container {
  grid-auto-flow: row | column | row dense | column dense;
}

请注意,dense 只是改变了你的项目的视觉顺序,可能会导致它们出现失序,这对可访问性不利。

看看下面这个例子:

<section class="container">
  <div class="item-a">item-a</div>
  <div class="item-b">item-b</div>
  <div class="item-c">item-c</div>
  <div class="item-d">item-d</div>
  <div class="item-e">item-e</div>
</section>

定义了一个五列两行的网格,并将网格自动流设置为行(这也是默认的):

.container {
  display: grid;
  grid-template-columns: 60px 60px 60px 60px 60px;
  grid-template-rows: 30px 30px;
  grid-auto-flow: row;
}

当把 items 放在网格上时,你只为其中两个物品指定位置:

.item-a {
  grid-column: 1;
  grid-row: 1 / 3;
}
.item-e {
  grid-column: 5;
  grid-row: 1 / 3;
}

因为我们把 grid-auto-flow 设置为行,所以我们的网格会看起来像这样。注意我们没有放置的三个项目(item-bitem-citem-d)是如何在可用的行间流动的。

Example of grid-auto-flow set to row

如果我们把 grid-auto-flow 设置为 column,那么 item-bitem-citem-d 就会沿着 column 流动。

.container {
  display: grid;
  grid-template-columns: 60px 60px 60px 60px 60px;
  grid-template-rows: 30px 30px;
  grid-auto-flow: column;
}

Example of grid-auto-flow set to column

3.15 grid

gridgrid-template-rows, grid-template-columns, grid-template-areas, grid-auto-rows, grid-auto-columns, 和 grid-auto-flow(注意:你只能在一个网格声明中指定显式或隐式网格属性)属性的缩写。

可选的值:

  • none:将所有子属性设置为初始值。

  • <grid-template>:与 grid-template 速记的作用相同。

  • <grid-template-rows> / [ auto-flow && dense? ] <grid-auto-columns>?:将grid-template-rows 设置为指定的值。如果 auto-flow 关键字在斜线的右边,它将 grid-auto-flow 设置为列。如果另外指定了 dense 关键字,自动放置算法就会使用 "dense "的打包算法。如果省略了 grid-auto-columns,它被设置为自动。

  • [ auto-flow && dense? ] <grid-auto-rows>?/ <grid-template-columns>:将grid-template-columns 设置为指定值。如果 auto-flow 关键字在斜线的左边,它设置 grid-auto-flow 为行。如果另外指定了 dense 关键字,自动排布算法就会使用 "dense "的打包算法。如果 grid-auto-rows 被省略,它被设置为自动。

下面的两个代码块是等价的:

.container {
  grid: 100px 300px / 3fr 1fr;
}

.container {
  grid-template-rows: 100px 300px;
  grid-template-columns: 3fr 1fr;
}

下面的两个代码块是等价的:

.container {
  grid: auto-flow / 200px 1fr;
}

.container {
  grid-auto-flow: row;
  grid-template-columns: 200px 1fr;
}

下面的两个代码块是等价的:

.container {
  grid: auto-flow dense 100px / 1fr 2fr;
}

.container {
  grid-auto-flow: row dense;
  grid-auto-rows: 100px;
  grid-template-columns: 1fr 2fr;
}

下面的两个代码块是等价的:

.container {
  grid: 100px 300px / auto-flow 200px;
}

.container {
  grid-template-rows: 100px 300px;
  grid-auto-flow: column;
  grid-auto-columns: 200px;
}

它也接受一种更复杂但相当方便的语法,可以一次性设置所有的东西。你指定了 grid-template-areasgrid-template-rowsgrid-template-columns,其他所有的子属性都被设置为初始值。你所做的是在各自的网格区域内指定行名和轨道尺寸。用一个例子来描述这个问题是最容易的:

.container {
  grid: [row1-start] "header header header" 1fr [row1-end]
        [row2-start] "footer footer footer" 25px [row2-end]
        / auto 50px auto;
}

上面等价于:

.container {
  grid-template-areas: 
    "header header header"
    "footer footer footer";
  grid-template-rows: [row1-start] 1fr [row1-end row2-start] 25px [row2-end];
  grid-template-columns: auto 50px auto;    
}

4、Grid 项目属性

需要注意的是float, display: inline-block, display: table-cell, vertical-aligncolumn-* 属性对网格 items 没有影响。

4.1 grid-column-start、grid-column-end、grid-row-start 和 grid-row-end

通过引用特定的网格线来确定一个网格 item 在网格中的位置。 grid-column-start/grid-row-start 是项目开始的线,grid-column-end/grid-row-enditem 结束的线。

可选的值:

  • <line>:可以是一个数字,指的是一个有编号的网格线,或者是一个名称,指的是一个有名称的网格线
  • span <number>:该项目将跨越所提供的网格行数
  • span <name>:项目将跨越,直到它到达下一个具有所提供的名称的行。
  • auto:表示自动放置,自动跨度,或者默认跨度为1。
.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;
}

下面来看例子:

.item-a {
  grid-column-start: 2;
  grid-column-end: five;
  grid-row-start: row1-start;
  grid-row-end: 3;
}

Example of grid-row/column-start/end

.item-b {
  grid-column-start: 1;
  grid-column-end: span col4-start;
  grid-row-start: 2;
  grid-row-end: span 2;
}

Example of grid-row/column-start/end

如果没有声明 grid-column-end/grid-row-end,该项目将默认跨越1个轨道。

项目可以相互重叠。你可以使用 z-index 来控制它们的堆叠顺序。

span 关键字

如果你用 grid-columngrid-row 把项目放到它们的父网格上,当项目应该跨越多于一列或一行时,你可以使用 span关键字 来避免指定结束线。

给出一个网格项的以下 CSS 规则,它跨越了 3列2行

.item {
  grid-column: 2 / 5;
  grid-row: 1 / 3;
}

我们可以像这样使用 span关键字 来代替:

.item {
  grid-column: 2 / span 3;
  grid-row: 1 / span 2;
}

可以提供端线,用跨度代替起始线,在这种情况下,跨度的作用是相反的,所以下面也是等同的:

.item {
  grid-column: span 3 / 5;
  grid-row: span 2 / 3;
}

如果多行有相同的名字,你可以像下面的例子那样定义开始和结束行:

.item {
  grid-column: col 2 / col 7;
  grid-row: content 6 / content 10;
}

该项目从 第2行col列 开始,在 第7行 结束,也叫 col。同样地,它开始于 第6行,名为row,结束于 第10行,名为 row

在这里,span关键字 也可以提供帮助,下面的内容是等效的:

.item {
  grid-column: col 2 / span col 5;
  grid-row: content 6 / span content 4;
}

span 也可以和 grid-area 属性一起使用。例如,如果我们希望一个项目被自动放置,但要跨越所提供的行和列的数量:

.item {
  grid-area: span 6 / span 4;
}

4.2 grid-column 和 grid-row

  • grid-row 属性是 grid-row-start 和 grid-row-end 的简写形式
  • grid-column 属性是 grid-column-start 和 grid-column-end 的简写形式

grid-columngrid-row 属性用于定义元素将显示在哪一行或哪一列。

可选的值:

  • <start-line> / <end-line>:每一个都接受所有与长写版本相同的值,包括跨度。

如果指定了两个<grid-line>值,那么斜杠号前的值就被指定为 grid-row-start,斜杠后面的值就被指定为 grid-row-end 的值。

.item {
  grid-column: <start-line> / <end-line> | <start-line> / span <value>;
  grid-row: <start-line> / <end-line> | <start-line> / span <value>;
}

grid-rowgrid-column 允许开发者将一个元素放置在网格容器中的特定单元格中。

grid-row属性定义了一个网格项所跨越的行数,可以使用一个值或一对值来指定。如果只指定一个值,则网格项跨越指定的行数。如果使用一对值,则第一个值表示网格项的起始行,第二个值表示网格项的结束行。可以使用数字、关键字和表达式作为值。

例如,下面的代码将一个元素放置在第一行,并跨越三行:

grid-row: 1 / span 3;

grid-column属性定义了一个网格项所跨越的列数,使用方法与grid-row类似。同样可以使用一个值或一对值来指定。如果只指定一个值,则网格项跨越指定的列数。如果使用一对值,则第一个值表示网格项的起始列,第二个值表示网格项的结束列。

例如,下面的代码将一个元素放置在第一列,并跨越两列:

grid-column: 1 / span 2;

需要注意的是,grid-rowgrid-column属性只能在网格容器中使用。当一个元素被放置在网格容器中时,它会自动成为网格项,而且这些属性就可以用来对其进行定位和布局。

下面来看例子:

.item-c {
  grid-column: 3 / span 2;
  grid-row: third-line / 4;
}

Example of grid-column/grid-row

如果没有声明端线值,项目将默认为跨越1个轨道。

下面看一个demo:

4.3 grid-area

给予一个项目一个名字,以便它可以被用 grid-template-areas 属性创建的模板所引用。另外,这个属性可以作为 grid-row-start + grid-column-start + grid-row-end + grid-column-end 的一个更简短的缩写。

可选的值:

  • <name>:你选择的名称
  • <row-start> / <column-start> / <row-end> / <column-end>:可以是数字或命名的行。
.item {
  grid-area: <name> | <row-start> / <column-start> / <row-end> / <column-end>;
}

下面来看例子:

.item-d {
  grid-area: header;
}

grid-row-start + grid-column-start + grid-row-end + grid-column-end的简写:

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

Example of grid-area

4.4 justify-self

沿着内联(行)轴对齐一个单元格内的网格项(与沿着块(列)轴对齐的 align-self 相反)。这个值适用于单个单元格内的网格项。

可选的值:

  • start:使网格项与单元格的起始边缘齐平。
  • end:将网格项目与单元格的末端边缘对齐。
  • center:使网格项目在单元格的中心对齐。
  • stretch:填充整个单元格的宽度(这是默认的)。
.item {
  justify-self: start | end | center | stretch;
}

下面来看例子:

.item-a {
  justify-self: start;
}

Example of justify-self set to start

.item-a {
  justify-self: end;
}

alt="Example

.item-a {
  justify-self: center;
}

Example of justify-self set to center

.item-a {
  justify-self: stretch;
}

Example of justify-self set to stretch

要为网格中的所有项目设置对齐方式,也可以通过 justify-items 属性在网格容器上设置此行为。

4.5 align-self

将一个单元格内的网格项沿块(列)轴对齐(与 justify-self 相反,它沿内联(行)轴对齐)。这个值适用于单个网格项的内容。

可选的值:

  • start:使网格项与单元格的起始边缘齐平。
  • end:将网格项与单元格的末端边缘齐平
  • center:使网格项目在单元格的中心对齐。
  • stretch:填充整个单元格的高度(这是默认的)。
.item {
  align-self: start | end | center | stretch;
}

下面来看例子:

.item-a {
  align-self: start;
}

Example of align-self set to start

.item-a {
  align-self: end;
}

Example of align-self set to end

.item-a {
  align-self: center;
}

Example of align-self set to center

.item-a {
  align-self: stretch;
}

Example of align-self set to stretch

为了对齐网格中的所有项目,这个行为也可以通过 align-items 属性在网格容器上设置。

4.6 place-self

place-self 在一个声明中同时设置 align-selfjustify-self 属性。

可选的值:

  • auto:布局模式的 "默认 "对齐方式。
  • <align-self> / <justify-self>:第一个值设置align-self,第二个值设置justify-self。如果省略了第二个值,则第一个值将被分配给这两个属性。

下面来看例子:

.item-a {
  place-self: center;
}

place self set to center

.item-a {
  place-self: center stretch;
}

place set set to center stretch

Edge 外,所有主要的浏览器都支持 place-self 简写属性。

5、一些特殊的单位、关键字和函数

5.1 fr(fraction:片段)

我们有现有的长度维度、百分比以及这些新关键字。在CSS网格布局中,还有一种特殊的调整大小的方法,只适用于网格布局 —— 一个新的灵活单位:frfr-unit:w3 规范)。fr是一个分数单位,1fr 代表1部分的可用空间。下面是几个fr单位工作的例子。这些例子中的网格项目是用网格区域放置在网格上的。

fr 的工作方式类似于在 flexbox 中使用 flex: auto。它在项目被布置好后分配空间。因此,如果有多列都希望设置为相同份额的可用空间,那么就可以使用fr

.container {
  display: grid;

  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-template-rows: 100px 200px 100px;

  grid-template-areas:
        "head head2 . side"
        "main main2 . side"
        "footer footer footer footer";
}

这4栏各占相同的空间:

image.png

为什么会出现 fr

首先,让我们看看我们通常是如何在CSS中建立网格的。在下面的例子中,我们要创建一个四列网格,每一列都有相同的宽度:

<div class="grid">
    <div class="column"> </div>
    <div class="column"> </div>
    <div class="column"> </div>
    <div class="column"> </div>
</div>
.grid {
  display: grid;
  grid-template-columns: repeat(4, 25%);
  grid-column-gap: 10px;
}

如果你从未见过 grid-template-columns 属性后面的 repeat() 函数,那么让我向你介绍一下CSS Grid最整洁的功能之一它是一种简写,本质上允许我们更简洁地描述重复的值。我们可以写成 grid-template-columns。25% 25% 25% 25%;,但使用 repeat() 更加简洁,特别是当你有更多的宽度时(比如 minmax()表达式)。它的语法是这样的:

repeat(number of columns/rows, the column width we want);

不过,到目前为止,我们所做的事情其实有几个问题。

首先,为了使用这个整洁的CSS功能,我们不得不做一点点数学运算。我们必须想一想,网格的总宽度(100%)除以我们想要的列数(4),就得出了25%。在这个例子中,数学是非常简单的,所以我们不必担心,但在更复杂的例子中,我们可以完全避免做数学题,让浏览器为我们计算。我们确实有calc()可用,所以我们可以做 repeat(4, calc(100% / 4), 但即使这样也有点奇怪,而且还有另一个问题...

第二个问题是溢出的问题。因为我们把每一列设置为25%,而grid-column-gap10px,这样就把grid元素推得比100%还宽。从上面的代码来看,这并不是你所期望的工作方式,但这就是百分比的工作方式。我们在上面的代码中真正说的是 "将每一列设置为视口宽度的25%,并在它们之间有一个10px的间隙。"这是一个微妙的区别,但它导致了布局上的一个大问题。

我们无意中在这里造成了水平滚动:

这就是fr可以解决问题的地方。在定义网格时,fr 可以像其他CSS长度(如 %pxem)一样使用。让我们快速重构上面的代码以使用这个奇特的新值:

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 10px;
}

这看起来和上面的例子一样,因为在这个例子中,我们将四列中的每一列都设置为一个分数(刚好是1/425%)。但是!X轴上不再有溢出,因为将每一列设置为1fr会自动考虑到这10px,并从每一列的总宽度中减去它。

你想知道,如果我可以坚持使用百分比或像素等单位,我为什么要学习如何使用这种花哨的新CSS长度?好吧,让我们来看看一个更复杂的CSS网格例子,以解释为什么fr是一个更好的选择。在一个新的例子中,假设我们希望我们的导航在左边,然后是一个12列的网格。

image.png

对于许多 UI 来说,这是一个非常典型的场景,因此使用 fr 可以避免我们制作单独的网格 div 或使用 calc。因为如果我们在上面的例子中没有使用 fr 那么我们将不得不使用下面的计算逻辑:

每列的宽度 = ((视口宽度 - nav宽度) / 列数) * 1%

这当然是可行的,只是阅读起来非常痛苦,而且如果我们改变了导航的宽度,那么我们就必须重新进行这种计算。而 fr 将所有这些都整理成一行可读性很好的代码:

.grid {
  display: grid;
  grid-template-columns: 250px repeat(12, 1fr);
  grid-column-gap: 10px;
}

仅仅通过少量的代码,我们就使我们的页面在未来更容易维护,我们也确保了我们的代码对我们后面的开发者来说更容易读懂。

5.1.1 fr 的使用

下面是上面的同一个例子,不同的fr值。注意布局的变化。

.container {
  /* ... */

  grid-template-columns: 1fr 1fr 40px 20%;
  grid-template-rows: 100px 200px 100px;

  /* ... */
}

image.png

在下面的最后一个例子中,侧边栏项目覆盖了2fr,所以它的宽度将与横跨第1和第2列的项目相同。

.container {
  /* ... */

  grid-template-columns: 1fr 1fr 40px 2fr;
  grid-template-rows: 100px 200px 100px;

  /* ... */
}

image.png

你可能会在 CSS Grid 中使用很多 fractional units,比如 1fr。它们本质上意味着 "剩余空间的一部分"。因此,一个声明如:

grid-template-columns: 1fr 3fr;

意味着,宽泛地说,25% 75%。只是这些百分比值比分数单位要牢固得多。例如,如果你给这些基于百分比的列添加了填充物,现在你已经打破了 100% 的宽度(假设是一个内容框模型)。分数单位与其他单位的组合也更加友好,这一点你可以看下面这个例子:

grid-template-columns: 50px min-content 1fr;

5.1.2 fr 混合其他单位使用

正如你在前面的例子中所看到的,你可以将fr值与固定值和百分比值混合。fr值将被划分在其他值所占的空间中。

例如,如果你的网格有4列,如下面的片段,第1列将是300px,第2列80px(800px的10%),第3和第4列将是210px(各占剩余空间的一半)

main {
  width: 800px;
  display: grid;
  grid-template-columns: 300px 10% 1fr 1fr;
  /* 300px 80px 210px 210px */

  grid-template-rows: auto;
}

5.2 sizing 关键字和函数

在确定行和列的大小时,你可以使用你习惯的所有长度,如 px、rem、% 等,还可以使用下面几个在 Box Sizing 规范中定义的关键词:

  • min-content:内容的最小尺寸,min-content(最小内容)这个关键词会让一个轨道尽可能小,而不会让轨道内容溢出来。如果将网格布局改为列轨道都是min-content,意味着它们会变得与轨道中最长的单词一样窄。比如像 "E pluribus unum" 这样的一行文字,最小内容可能是 "pluribus" 这个词的宽度。

  • max-content:内容的最大尺寸,max-content关键字则有相反的效果。轨道将变得足够宽,所有的内容都可以显示在一个长的不间断的字符串中。这可能会导致溢出,因为字符串不会被包裹。比如上面的句子,max-content 是整个句子的长度。

  • auto:这个关键词很像fr单位,只是在分配剩余空间时,它们在与fr单位的尺寸之争中 "输 "了。

  • fr:见上文

  • fit-content():函数使用可用的空间,但绝不会小于 min-content,也不会超过 max-contentfit-content() 函数一开始的作用与 max-content 类似。然而,一旦轨道达到你传入该函数的尺寸,内容就开始被包裹。因此,fit-content(10em)将创建一个小于10em的轨道,如果最大内容尺寸小于10em,但绝不会大于10em

  • minmax():函数的作用和它看起来的一样:它为长度能够达到的程度设置一个最小和最大的值。这对于相对单位相结合是很有用的。比如你可能希望一列只能缩到这么远。这非常有用,可能是你想要的

grid-template-columns: minmax(100px, 1fr) 3fr;

下面用一个demo来演示上述几个主要关键字的区别:

在这个demo中,你可能会发现,当使用 auto 时,网格列会拉伸以填充容器。如果网格容器中有额外的空间,自动尺寸的轨道将默认拉伸。

5.3 repeat() 函数和关键字

如果你的定义包含重复的部分,你可以使用 repeat() 来简化事情。你可以将其与 grid-template-columnsgrid-template-rows 属性一起使用,使您的规则在创建大量列或行时更加简洁和易于理解。

例如,我们有一个网格容器:

.container {
  display: grid;
  grid-gap: 10px 15px;

  grid-template-columns: 1fr 2fr 1fr 2fr 1fr 2fr;
  grid-template-rows: 100px auto 20% 100px auto 20px;
}

我们可以使用像 repeat() 这样的符号来简化:

.container {
  display: grid;
  grid-gap: 10px 15px;

  grid-template-columns: repeat(3, 1fr 2fr);
  grid-template-rows: repeat(2, 100px auto 20%);
}

传递给 repeat() 的第一个值是重复次数,第二个值是要重复的网格轨道。

5.3.1 repeat() 与 关键字

看下面这个例子:

grid-template-columns:
  1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr。

/* 简洁的写法 */
grid-template-columns:
  repeat(8, 1fr);

/* 配合 minmaxx() */
grid-template-columns:
  repeat(8, minmax(10px, 1fr))

但是,repeat()在与关键词结合时可以变得更加花哨。

  • auto-fill:在一行中尽可能多地填入可能的列,即使它们是空的。
  • auto-fit:将所有的列装入空间。倾向于扩大列来填充空间,而不是空列。

这承担了所有CSS网格中最著名的片段,也是历史上最伟大的CSS技巧之一。

grid-template-columns:
  repeat(auto-fit, minmax(250px, 1fr))

这些关键词之间的区别:传送门

5.3.2 repeat() 和 named line

你还可以将 repeat()named lines 结合使用:

.container {
  display: grid;
  grid-auto-flow: column dense;

  grid-template-columns: repeat(4, [col] 1fr);
  grid-template-rows: repeat(5, [row] 150px);
}

这等效于以下长格式定义:

.container {
  display: grid;
  grid-auto-flow: column dense;

  grid-template-columns:
    [col] 1fr
    [col] 1fr
    [col] 1fr
    [col] 1fr;
  grid-template-rows:
    [row] 150px
    [row] 150px
    [row] 150px
    [row] 150px
    [row] 150px;
}

重复符号末尾的命名网格线最终与下一个起始名称行共享同一行。例如,有了这个:

.container {
  display: grid;
  grid-auto-flow: column dense;

  grid-template-columns: repeat(4, [col-start] 1fr [col-end]);
  grid-template-rows: repeat(5, [row-start] 150px [row-end]);
}

我们得到这个等效的长格式定义:

.container {
  display: grid;
  grid-auto-flow: column dense;

  grid-template-columns:
    [col-start] 1fr
    [col-end col-start] 1fr
    [col-end col-start] 1fr
    [col-end col-start] 1fr [col-end];

  grid-template-rows:
    [row-start] 150px
    [row-end row-start] 150px
    [row-end row-start] 150px
    [row-end row-start] 150px
    [row-end row-start] 150px [row-end];
}

5.4 minmax() 函数

这个功能意味着你可以为一个轨道设置一个最小和最大的尺寸。这可能是相当有用的。如果我们以上面那个分配剩余空间的fr单元为例,可以用minmax()写成minmax(auto, 1fr)。Grid看的是内容的内在尺寸,然后在给内容足够的空间后分配可用空间。这意味着你可能不会得到每个轨道在网格容器中都有相等份额的所有可用空间。

要强制轨道在网格容器中占有同等份额的空间,减去间隙,使用minmax。用minmax(0, 1fr)代替1fr作为轨道尺寸。这使得轨道的最小尺寸为0,而不是最小内容尺寸。然后,Grid将占用容器中所有可用的尺寸,扣除任何间隙所需的尺寸,并根据你的fr单位将其余部分分出去。

5.5 auto-fill 和 auto-fit

有时候你可能不想指定列轨道的数量,而是想创建尽可能多的适应容器的轨道。你可以用 repeat()auto-fillauto-fit 关键字来实现这一点。来看下面这个demo:

demo中,我们可以得到尽可能多的轨道。然而,这些轨道并不灵活。在有足够的空间容纳另一个200像素的轨道之前,你会在末端得到一个缺口。如果你添加了minmax()函数,你就可以要求尽可能多的轨道,最小尺寸为200像素,最大为1fr。然后,Grid会将200像素的轨道排列出来,不管剩下多少空间都会平均分配给它们。

这创造了一个二维响应式布局,而且不需要任何媒体查询:

auto-fillauto-fit 之间有一个微妙的区别。在下一面一个demo中,使用上面解释的语法来玩一个网格布局,但在网格容器中只有两个网格项。使用auto-fill关键字,你可以看到空轨道已经被创建。将关键字改为auto-fit,轨道就会折叠成0大小。这意味着,灵活的轨道现在会增长,以消耗空间。

auto-fillauto-fit 关键词的作用在很大程度上是相同的。一旦第一个轨道被填满,它们之间就没有任何区别。

5.6 Masonry

CSS网格的一个实验性功能是砌体布局。请注意,有很多关于 CSS砌体 的方法,但大部分都是些花式玩法,要么有很大的弊端,要么不是你想要的。

该规范现在有一个官方的实现,这是在 Firefox 的一个 flag 后面:

.container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: masonry;
}

关于 Masonry,推荐阅读:

5.7 Subgrid

Subgrid 是网格的一个非常有用的功能,它允许网格项拥有自己的网格,该网格从父网格继承网格线:

.parent-grid {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
}
.grid-item {
  grid-column: 2 / 7;

  display: grid;
  grid-template-columns: subgrid;
}
.child-of-grid-item {
  grid-column: 3 / 6;
}

现在只有Firefox支持这个功能,但它真的需要得到普及

了解 display: contents; 也很有用。这与 subgrid 不同,但它有时也是一个有用的工具,方式类似:

<div class="grid-parent">

  <div class="grid-item"></div>
  <div class="grid-item"></div>

  <ul style="display: contents;">
    <li class="grid-item"></li>
    <li class="grid-item"></li>
  </ul>

</div>

6、其他的一些应用

6.1 Fluid columns

流动宽度的 column,在有空间的情况下,可以分成更多或更少的栏目,而且无需媒体查询!

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  /* 这对小屏幕更好,前提是 min() 得到更好的浏览器支持。 */
  /* grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr)); */
  gap: 1rem;
}

6.2 CSS Grid animation

根据 CSS Grid Layout Module Level 1 规范,有5个可实现动画的网格属性。

  • grid-gapgrid-row-gapgrid-column-gap 的长度、百分比或计算。
  • grid-template-columns, grid-template-rows 作为一个简单的长度、百分比或计算的列表,只要唯一的区别是列表中的长度、百分比或计算成分的值。

截至目前,只有(grid-)gap, (grid-)row-gap, (grid-)column-gap 的动画在所有测试的浏览器中实现。

image.png

推荐阅读:animating grids

7、推荐几个 CSS Grid Generator

  1. Grid.Guide
  2. CSS Grid Generator Netlify
  3. Layout it Grid
  4. Grid Layout Generator
  5. Vue Grid Generator
  6. CSS Grid Layout Generator
  7. Griddy
  8. GridIt
  9. Layout Master
  10. Visual Grid Generator
  11. Cssgr.id
  12. Flex Layout Attribute
  13. Bootstrap 4 Interface Builder
  14. CSS layout generator by Braid Design System

end~