[译] Grid 布局完全指南

2,135
原文链接: segmentfault.com

简介

CSS 栅格布局 (亦称 "Grid"),是一个基于栅格的二维布局系统,旨在彻底改变基于网格用户界面的设计。CSS 一直以来并没有把布局做的足够好。刚开始,我们使用 tables,后来是 floats,positioning 和 inline-block,这些本质上是一些 hacks 而且许多重要功能尚未解决(例如垂直居中)。Flexbox 可以做到这些,但是它主要用来一些简单的一维布局,并不适合复杂的二维布局(当然 Flexbox 与 Grid 可以一并使用)。Grid 是第一个为了解决布局问题的 CSS 模块,只要我们做过网页,就会遇到这些问题。

有两件事情在激励着我创作这篇指南,首先是 Rachel Andrew 那本非常不错的书 Get Ready for CSS Grid Layout.,清晰透彻地介绍了 Grid,它是本篇文章的基础。我强烈建议你去购买并且阅读它。另一件事是 Chris Coyier 的文章 A Complete Guide to Flexbox,是关于 flexbox 的首选资源。它帮助了很多人,当你 Google "flexbox" 的时候,第一眼便能够看见它。或许你已经注意到我的文章与它有很大相似之处,但我们有什么理由不借鉴它呢?

我会把 Grid 在最新版本规范上的概念呈现出来。因此,我将不会照顾过期的 IE 语法。当规范成熟时,我将尽可能去定期更新。

基础与浏览器支持

开始 Grid 是简单的,你仅仅需要在容器(container)元素上定义一个栅格使用 display: grid,并通过 grid-template-columnsgrid-template-rows 设置行与列。通过设置 grid-columngrid-row 把子元素置于栅格中。与 flexbox 类似,栅格项目(items)的顺序是无关紧要的,你可以通过 CSS 来控制顺序。当使用媒体查询时,改变它们的顺序是极其简单的。假设你设计好了网页的布局,但你需要适应不同的屏幕宽度,这仅仅需要几行代码,Grid 是最为有效的模块。

关于 Grid 一件非常重要的事情是你还不能够在生产环境中使用它。它目前仅仅是一个 W3C工作草案,而且不能够被任何浏览器默认支持。虽然IE10 与 IE11 能够支持它,但使用了过期语法旧的实现。为了现在能够体验 Grid,最好的方法是使用 Chrome, Opera 或者 Firefox,并且开启特定的标志。在 Chrome 中,导航到 chrome://flags 并且开启 “experimental web platform features”。在 Opera 中同样如此(opera://flags)。在 Firefox 中,开启标志 layout.css.grid.enabled(about:config)。

这有一张浏览器支持表格,我将保持更新。

Chrome 29+ (Behind flag)
Safari Not supported
Firefox 40+ (Behind flag)
Opera 28+ (Behind flag)
IE 10+ (Old syntax)
Android Not supported
iOS Not supported

译者注:现在有些最新浏览器的最新版本已经能够支持,可以查看 caniuse 网站。

你在生产环境中使用它仅仅是一个时间问题。但是,学习正在当下!

重要术语

在深入了解 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 Container)

display

定义该元素为栅格项目,并且为它的内容建立一个新的栅格格式上下文(grid formatting context)

译者注:还记得 BFC 与 IFC 吗?

属性值:

  • grid 生成块状栅格

  • inline-grid 生成行间栅格

  • subgrid 如果你的栅格容器本身是一个栅格项目的话(例如:嵌套栅格),你可以根据它的父元素而不是它自己,指定行列大小。

.container{
  display: grid | inline-grid | subgrid;
}

注意:columnfloatclearvertical-align 在栅格容器上无效。

grid-template-columns
grid-template-rows

通过空格分隔的值定义栅格的行与列。值代表轨迹大小(track size),它们中间的间隙代表栅格线。

属性值:

  • <track-size> 可以是长度,百分比,或者栅格中的空白空间(使用 fr

  • <line-name> 任意名字,任君选择

.container{
  grid-template-columns: <track-size> ... | <line-name> <track-size> ...;
  grid-template-rows: <track-size> ... | <line-name> <track-size> ...;
}

示例:

当你在轨迹值之间预留空格时,栅格线会被自动分配为数值名字。

.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: "<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{
  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 组成。

你声明的每行都需要有相同数量的栅格格子。

你可以使用任意数量无空格分割的相邻句点去表示单个空白栅格格子。

译者注:grid-template-areas: "first . last"grid-template-areas: "first ...... last" 等价。

注意,这种语法仅仅能命名区域,而无法命名栅格线。实际上,当你使用这种语法的时候,栅格区域两端的栅格线已被自动命名。如果你的栅格区域叫 foo,栅格区域开始的行与列将被命名为 foo-start,而结束的行与列将被命名为 foo-end。这意味着一些栅格线会有很多名字,比如上述例子的最左边的栅格线将会有三个名字:header-start, main-start 和 footer-start。

grid-template

grid-template-columnsgrid-template-rowsgrid-template-areas 的简写。

属性值:

  • none 设置这三个属性为初始属性

  • subgrid 设置 grid-template-rowsgrid-template-columnssubgridgrid-template-areas 为初始值。

  • <grid-template-columns> / <grid-template-rows> 设置 grid-template-columnsgrid-template-rows 为各自指定的值。而 grid-template-areasnone

.container{
  grid-template: none | subgrid | <grid-template-columns> / <grid-template-rows>;
}

另外,也有一个比较复杂但是方便的语法指定三个属性,示例如下

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

与以下代码是等价的:

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

因为 grid-template 无法 隐式 重置属性(grid-auto-columnsgrid-auto-rowsgrid-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-column-gapgrid-row-gap 的简写。

属性值:

  • <grid-row-gap> <grid-column-gap> 长度值

.container{
  grid-gap: <grid-row-gap> <grid-column-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

使栅格项目中的内容与 轴对齐(相应地,align-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

使栅格项目中的内容与 轴对齐(相应地,justify-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 给所有的栅格项目设置了固定大小。本例中,你可以设置栅格容器中栅格的对齐。这个属性会使栅格与 轴对齐(相应地,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{
  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

有时候,栅格的总大小小于栅格容器的大小,比如你使用 px 给所有的栅格项目设置了固定大小。本例中,你可以设置栅格容器中栅格的对齐。这个属性会使栅格与 轴对齐(相应地,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

指定自动生成的栅格轨迹的大小(亦称隐式栅格轨迹)。当你显式定位行与列的时候(通过 grid-template-rows / grid-template-columns),隐式栅格轨迹会在定义的栅格外被创建。

属性值:

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

本示例建造了 2 * 2 的栅格。

你使用 [grid-column](#prop-grid-column) 与 grid-row 去定位你的项目如下:

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

我们告知 .item-b 在 5-6 列间,但我们从未定义第五列或者第六列。因为我们引用的栅格线不存在,宽度为0的隐式栅格轨迹将会创建去填充空白。我们可以使用 grid-auto-columnsgrid-auto-rows 去指定这些轨迹的宽。

译者注:经译者测试,并非以宽度为0的 implicit track 去填充。w3c auto-tracks 上表明 grid-auto-columns 的默认值为 auto,则超过的列将会平分空白空间。

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

grid-auto-flow

如果你的栅格项目没有显式地在栅格中设置位置,自动放置算法便会生效。这个属性控制自动放置算法的的运作。

属性值:

  • row 自动放置算法将按行依次排列,按需添加新行。

  • column 自动放置算法将按列依次排列,按需添加新列。

  • dense 如果较小的项目出现靠后时,自动防止算法将尽可能早地填充栅格的空白格子

.container{
  grid-auto-flow: row | column | row dense | column dense
}

注意 dense 可能使你的项目次序颠倒。

示例:

考虑以下 html:

<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 (默认属性便是 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-template-rowsgrid-template-columnsgrid-template-areasgrid-auto-rowsgrid-auto-columns,与 [grid-auto-flow](#prop-grid-auto-flow)。它也可以设置 grid-column-gapgrid-row-gap为默认值,即使并没有在 grid 中明确设置。

属性值:

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

  • <grid-template-rows> / <grid-template-columns> 仅仅设置这两个属性值,其它子属性值为初始值。

  • <grid-auto-flow> [<grid-auto-rows> [ / <grid-auto-columns>] ] 如果 grid-auto-columns 属性值确实,则采用 grid-auto-rows的值。如果属性值均缺失,则采用默认值。

.container{
    grid: none | <grid-template-rows> / <grid-template-columns> | <grid-auto-flow> [<grid-auto-rows> [/ <grid-auto-columns>]];
}

以下两种写法是等价的:

.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-areasgrid-auto-rowsgrid-auto-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,项目默认横跨一个轨迹。

项目可能会互相重叠,你可以使用 z-index 控制它们的层叠顺序(stacking order)。

grid-column
grid-row

各自表示grid-column-start + grid-column-endgrid-row-start + grid-row-end的缩写。

属性值:

  • <start-line> / <end-line> 接收 grid-column-start 同样的属性值,包括 span

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

Example:

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

如果没有指定 end line,项目将默认跨越一个轨迹。

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
}

justify-self

使栅格项目中的内容与 轴对齐(相应地,align-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

使栅格项目中的内容与 轴对齐(相应地,justify-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 属性。