Image placeholder

从零开始学 CSS Grid 布局

Image placeholder
F2EX 2018-04-11

CSS Grid 布局是 CSS 中最强大的布局系统。这是一个二维系统,这意味着它可以同时处理列和行,不像 flexbox ,它主要是一维系统。你可以通过 Grid 布局将 CSS 规则应用于父元素(成为网格容器)和该元素的子元素(网格元素)。

前言

CSS Grid 布局(又名“网格”)是一个二维的基于网格的布局系统,其目的在于完全改变我们基于网格设计的用户界面的方式。CSS 一直用来布局我们的网页,但是它从来都没有那么方便。最开始我们使用表格来布局,然后浮动,定位和内嵌块,但所有这些方法本质上是 css hack,并且遗漏了很多重要的功能(例如垂直居中)。Flexbox 的出现改变了许多,但是它的目的是为了更简单的一维布局,而不是复杂的二维布局(Flexbox 和 Grid 实际上可以同时应用)。Grid 是第一个专门为解决布局问题而创建的 CSS 模块,只要我们一直在制作网站,我们就一直在为这种布局问题而喋喋不休。

基本用法和浏览器支持

要使用 Grid 布局,首先使用 display:grid 将容器元素定义为网格,并使用 grid-template-columnsgrid-template-rows,然后使用 grid-columngrid-row 将其子元素放置到网格中。与 flexbox 类似,Grid 项目的源顺序无关紧要。你可以用 CSS 以任意顺序放置它们,这使得使用媒体查询重新排列网格变得非常容易。想象一下,定义整个页面的布局,然后完全重新整理它,以适应不同的屏幕宽度,只需要几行 CSS 。Grid 是有史以来最强大的 CSS 模块之一。

截至2017年3月,许多浏览器都提供了对 CSS Grid 的原生支持:Chrome(包括Android),Firefox,Safari(包括iOS)和Opera。 另外,IE 10 和 11 也支持它,不过使用一个过时的语法。Edge 也已经宣布支持。

下表的浏览器支持数据来自 Caniuse 。数字表示浏览器支持该属性的版本(及以上版本)。

桌面端浏览器
ChromeOperaFirefoxIEEdgeSafari
57445211*1610.1
手机端/平板端浏览器
iOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox
10.3NoNo626257

在生产环境中使用 Grid 只是时间问题。但现在是学习的时候了。

重要术语

在深入了解 Grid 概念之前,理解术语很重要。由于这里所涉及的术语在概念上都是相似的,如果你不先记住它们的网格规范定义的含义,很容易将它们混淆。不过不用担心,内容不多。

Grid Container

应用了 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>
Grid Item

网格容器的子集。下面例子中的 item 元素是网格项目,但是 sub-item 不是。

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

组成网格结构的分界线。它们可以是垂直的(“列网格线”)或水平的(“行网格线”),并且位于行或列的任一侧。下面例子中的黄线是列网格线。

Grid Track

两条相邻网格线之间的空间。你可以将它们想象成网格的列或行。下面的例子是第二行和第三行网格线之间的网格。

Grid Cell

两个相邻行和两个相邻列网格线之间的空间。这是网格的一个单元。下面的例子是行网格线1和2之间的网格单元,也是列网格线2和3之间的网格单元。

Grid Area

四个网格线包围的总的网格区域。网格区域可以由任意数量的网格单元组成。下面的例子是行网格线1和3之间的网格区域,也是列网格线1和3之间的网格区域。

Grid 属性目录

Grid Container (网格容器) 的属性[父级属性]:
Grid Items (网格项目) 的属性[子集属性]:

Grid Container (网格容器[父级属性])详解

display

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

值:

  • grid – 生成一个块级网格
  • inline-grid – 生成一个内联网格
  • subgrid – 如果你的网格容器本身就是一个嵌套网格,那么你可以使用这个属性来表示你想从它的父节点取得它的行/列的大小,而不是指定它自己的大小。
.container {
  display: grid | inline-grid | subgrid;
}

注意: column, float, clear, 和 vertical-align 对网格容器没有影响。

grid-template-columns
grid-template-rows

定义网格的列和行。

值:

  • <track-size> – 可以是长度,百分比或一部分自动空间(使用 fr 单位)。
  • <line-name> – 你使用的任意名称。
.container {
  grid-template-columns:  ... |   ...;
  grid-template-rows:  ... |   ...;
}

示例:

在网格的列或行设置空白区域时,网格线会自动分配数字名称:

.container {
  grid-template-columns: 40px 50px auto 50px 40px;
  grid-template-rows: 25% 100px auto;
}

当然,你也可以自己命名这些网格线。请注意名称的括号语法:

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

注意,一行可以有多个名称。例如,下面的例子中第二行有两个名字:row1-end 和 row2-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]) 5%;
}

上面的写法等同于:

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

fr 单位允许你将轨道的大小设置为网格容器可用空间的一小部分。例如,下面的例子将每个项目设置为网格容器宽度的三分之一:

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

可用空间是在任何非灵活项目之后计算的。在下面的例子中,fr 单位可用的空间总量不包括 50px :

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

通过引用 grid-area 属性指定的网格区域的名称来定义网格模板。重复网格区域的名称将导致内容超出这些单元格。一个“点”表示一个空单元。语法本身提供了网格结构的可视化。

值:

  • <grid-area-name> – 使用 grid-area 属性指定网格区域的名称
  • . – 一个“点”表示一个空的单元格
  • none – 没有定义网格区域
.container {
  grid-template-areas: 
    " | . | none | ..."
    "...";
}

示例:

.item-a {
  grid-area: header;
}
.item-b {
  grid-area: main;
}
.item-c {
  grid-area: sidebar;
}
.item-d {
  grid-area: footer;
}

.container {
  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";
}

上面的代码创建了一个四列三行的网格。整个顶行由 header 区域组成。中间一排由两个 main 区域,一个空单元格和一个 sidebar 区域。最后一行全部由 footer 区域组成。

声明中的每一行都需要有相同数量的单元格。

注意,这个语法只是用来命名区域。当你使用这种语法时,区域两端的网格线实际上是自动命名的。例如网格区域的名称是 foo ,则区域的起始行和起始列的名称将为 foo-start ,并且其最后一行和最后一列的名称为 foo-end 。这意味着某些行可能有多个名称,例如上面示例中最左边的一列网格线,它将有三个名称:header-start,main-start 和 footer-start。

grid-template

在单个声明中设置网格模板行,网格模板列和网格模板区域的简写。

值:

  • none – 将所有三个属性设置为其初始值
  • subgrid – 将 grid-template-rowsgrid-template-columns 设置为 subgrid ,并将 grid-template-areas 设置为其初始值
  • <grid-template-rows>/<grid-template-columns> – 将 grid-template-columnsgrid-template-rows 分别设置为指定的值, 并将 grid-template-areas 设置为 none
.container {
  grid-template: none | subgrid | <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-columns , grid-auto-rows , 和 grid-auto-flow),因此建议使用 grid 而不是 grid-template

grid-column-gap
grid-row-gap

指定网格线的大小。你可以将其想象为列/行之间的间隔宽度。

值:

  • <line-size>
.container {
  grid-column-gap: <line-size>;
  grid-row-gap: <line-size>;
}

示例:

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

列/行之间的间隔宽度仅在列/行之间创建,而不是在外部边缘上创建。

grid-gap

网格行间隔宽度和列间隔宽度的简写。

值:

  • <grid-row-gap> <grid-column-gap>
.container {
  grid-gap:  ;
}

示例:

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

如果未指定 grid-row-gap ,则会将其设置为与 grid-column-gap 相同的值。

justify-items

沿着横向轴对齐网格内的内容(而不是沿着纵轴对齐)。该值适用于容器内的所有网格项目。

值:

  • start – 将内容与网格区域的左端对齐
  • end – 将内容与网格区域的右端对齐
  • center – 将内容与网格区域中心对齐
  • stretch – 填充网格区域的整个宽度(这是默认值)
.container {
  justify-items: start | end | center | stretch;
}

示例:

.container {
  justify-items: start;
}
.container{
  justify-items: end;
}
.container {
  justify-items: center;
}
.container {
  justify-items: stretch;
}


这种行为也可以通过 justify-self 属性在单个网格内容上设置。
align-items
沿着纵向轴对齐网格内的内容(而不是横向对齐)。 该值适用于容器内的所有网格项目。 值:
  • start – 将内容与网格区域的顶部对齐
  • end – 将内容与网格区域的底部对齐
  • center – 将内容与网格区域中心对齐(垂直对齐)
  • stretch – 填充网格区域的整个高度(这是默认值)
.container {
  align-items: start | end | center | stretch;
}

示例:

.container {
  align-items: start;
}
.container {
  align-items: end;
}
.container {
  align-items: center;
}
.container {
  align-items: stretch;
}

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

justify-content

有时你的网格内项目的大小可能小于其网格容器的大小。如果你的所有网格项目都使用像 px 这样的非灵活单位进行大小设置,在这种情况下,你可以设置网格容器内的网格项目的对齐方式。此属性沿着横向轴对齐网格(而不是纵向对齐)。

值:

  • 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;
}
.container {
  justify-content: end;
}
.container {
  justify-content: center;
}
.container {
  justify-content: stretch;
}
.container {
  justify-content: space-around;	
}
.container {
  justify-content: space-between;	
}
.container {
  justify-content: space-evenly;	
}
align-content

align-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;	
}
.container {
  align-content: end;	
}
.container {
  align-content: center;	
}
.container {
  align-content: stretch;	
}
.container {
  align-content: space-around;	
}
.container {
  align-content: space-between;	
}
.container {
  align-content: space-evenly;	
}
grid-auto-columns
grid-auto-rows

值:

  • <track-size>
.container {
  grid-auto-columns: <track-size> ...;
  grid-auto-rows: <track-size> ...;
}

示例:

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

上面的例子创建了一个 2X2 的网格。

但现在想象一下,使用网格的列和行来定位你的网格容器内的元素,如下所示:

.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-columnsgrid-auto-rows 来指定这些隐式网格线的宽度:

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

通过控制自动布局算法的运作原理,精确指定自动布局的元素在网格中排列的方向。

值:

  • row – 指定自动布局算法按照通过逐行填充来排列元素,在必要时增加新行。如果既没有指定 row 也没有 column,则默认为 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>

定义一个包含五列和两行的网格,并将 grid-auto-flow 设置为 row(这也是默认值):

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

要在网格上固定项目,只需要指定一个点(位置):

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

因为我们将 grid-auto-flow 设置为 row ,所以我们的网格看起来像这样。注意我们没有明确的三个项目(item-bitem-citem-d)是如何布局可用的行的:

如果我们将 grid-auto-flow 设置为 column ,则item-bitem-citem-d则向下布局:

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

grid 是一个 CSS 简写属性,可以用来设置以下属性:

显式网格属性 grid-template-rowsgrid-template-columnsgrid-template-areas

隐式网格属性 grid-auto-rowsgrid-auto-columnsgrid-auto-flow

间距属性 grid-column-gapgrid-row-gap

值:

  • none – 将所有子属性设置为其初始值。
  • <grid-template-rows> / <grid-template-columns> – 将 grid-template-rowsgrid-template-columns 分别设置为指定值,将所有其他子属性设置为其初始值。
  • <grid-auto-flow> [<grid-auto-rows> [ / <grid-auto-columns>] ] – 分别接受所有与 grid-auto-flowgrid-auto-rowsgrid-auto-columns 相同的值。 如果省略 grid-auto-columns ,则将其设置为 grid-auto-rows 指定的值。如果两者均被省略,则它们被设置为它们的初始值。
.container {
    grid: none |  /  |  [ [/ ]];
}

例子:

以下两个代码块是等效的:

.container {
  grid: 200px auto / 1fr auto 1fr;
}
.container {
  grid-template-rows: 200px auto;
  grid-template-columns: 1fr auto 1fr;
  grid-template-areas: none;
}

以下两个代码块也是等效的:

.container {
  grid: column 1fr / auto;
}
.container {
  grid-auto-flow: column;
  grid-auto-rows: 1fr;
  grid-auto-columns: auto;
}

它也接受更复杂但相当方便的语法来一次设置所有内容。你可以指定 grid-template-areas, grid-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;    
}

Grid Items (网格项目[子集属性])详解

grid-column-start
grid-column-end
grid-row-start
grid-row-end

通过参考特定网格线来确定网格内项目的位置。 grid-column-start / grid-row-start 是项目开始的列/行,grid-column-end / grid-row-end 是项目结束的列/行。

值:

  • <line> – 可以是一个数字来引用一个编号的网格线,或者一个名称来引用一个命名的网格线。
  • span <number>
  • 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
}

示例:

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

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

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

grid-column
grid-row

grid-column-start + grid-column-end, 和 grid-row-start + grid-row-end 的缩写。

值:

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

示例:

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

如果没有声明结束行值,则该项目默认跨越1个轨道。

grid-area

CSS 属性 grid-area 是一种对于 grid-row-startgrid-column-startgrid-row-endgrid-column-end 的简写,通过基线(line),跨度(span)或没有(自动)的网格放置在 grid row 中指定一个网格项的大小和位置,继而确定 grid area 的边界。

值:

  • <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
}
justify-self

沿着行轴对齐网格内的内容(而不是沿着列轴对齐)。 此值适用于单个网格项目内的内容。

值:

  • start – 将内容与网格区域的左端对齐
  • end – 将内容与网格区域的右端对齐
  • center – 将内容与网格区域的中心对齐
  • stretch – 填充网格区域的整个宽度(这是默认值)
.item {
  justify-self: start | end | center | stretch;
}

示例:

.item-a {
  justify-self: start;
}
.item-a {
  justify-self: end;
}
.item-a {
  justify-self: center;
}
.item-a {
  justify-self: stretch;
}

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

align-self

沿着列轴对齐网格内的内容(而不是沿着行轴对齐)。此值适用于单个网格项目内的内容。

值:

  • start – 将内容与网格区域的顶端对齐
  • end – 将内容与网格区域的底端对齐
  • center – 将内容与网格区域的中心对齐
  • stretch – 填充网格区域的整个高度(这是默认值)
.item {
  align-self: start | end | center | stretch;
}

示例:

.item-a {
  align-self: start;
}
.item-a {
  align-self: end;
}
.item-a {
  align-self: center;
}
.item-a {
  align-self: stretch;
}

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

原文:https://css-tricks.com/snippets/css/complete-guide-grid/


2021-06-06