CSS网格布局(Grid)完全教程

流过昼夜 提交于 2019-12-02 15:03:49

本文译自《A Complete Guide to Grid》。由于译者水平有限,如有错误,请不吝指正。文中所有图的版权归原作者所有

导语:CSS网格布局是目前CSS中可用的最强大的布局系统。它是二维系统,即可以处理行和列,不像flexbox只是一维系统。我们通过为父元素(变为grid container)和子元素(变为grid items)应用CSS规则来使用Grid布局。

一、介绍

CSS网格布局(亦称Grid)是一种基于网格的二维布局系统,主要致力于改变我们在页面中基于网格设计页面的方式。我们通常使用CSS布局网页,但有时并不好用。一开始我们使用table,之后用浮动、定位和行内块级元素,但是所有这些方法都需要兼容,并且未实现一些重要功能(如垂直居中)。弹性盒子可以提供帮助,但它主要面向一维布局,不能处理复杂的两维布局(弹性盒子和网格布局其实配合得不错)。只要我们写网页,我们不得不考虑那些布局问题的hack实现,而网格布局是首个特地为解决这种问题而创造出来的模块。

我写这篇教程主要是因为受到两件事的影响。一个是Rachel Andrew的那本了不起的书——Get Ready for CSS Grid Layout。这本书清晰透彻的介绍了Grid,也是本文的基础。我非常提倡你能买本读一下。另一个是Chris Coyier的A Complete Guide to Flexbox,它指引我使用flexbox来写布局。这篇文章帮助了很多人,从在google中搜索‘flexbox’该文排在第一位就能看出。你会看到两篇文章有很多相似之处,因为为什么我不借鉴好的东西中呢?

我写这篇教程的目的是介绍目前存在的最新版本中Grid的概念。我不会覆盖过时的IE语法,而且会随着版本的成熟定期更新教程。

二、基础和浏览器支持

首先我们要使用display:grid定义一个网格布局容器,用grid-template-columnsgrid-template-rows设置列和行的尺寸,然后按grid-columngrid-row把子元素放进容器中。同flexbox类似,网格项的顺序并不重要,可以按任意顺序放置它们。这也使得使用媒体查询调整网格非常简单。想像一下,定义整个页面的布局,然后只需几行CSS代码就能将其重排为适应另一个尺寸的屏幕。Grid真是有史以来引入的最强大的CSS模块。

截至2017年三月,Chrome(包括安卓端),Firefox、Safari(包括iOS端)和Opera在内的很多浏览器都支持原生的无前缀的CSS网格布局。IE10和11只部分支持,但使用的是过时的语法。Edge也已经宣告支持。

#桌面端
Chrome Opera Firefox IE Edge Safari
57 44 52 11* 16 10.1
#移动端、平板
iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
10.3 No No 56 61 55


除了微软,浏览器厂商都在严格规范Grid直到规范成熟。这是一件好事,因为意味着我们不必学习多种语法。

在生产环境中使用Grid只是时间问题。是时候学它了。

三、重要术语

在我们正式学习Grid之前先了解一下其术语。由于这里的概念很类似,所以如果不先记住Grid规范定义的含义的话很容易弄混淆。但不用担心,术语并不是很多。

Grid Container
定义了display: grid的元素就成了网格容器。它是网格项的直接父元素。在下面的例子中,container就是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元素是grid items,但是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

网格线是组成网格结构的分割线。它们要么是垂直的(column grid lines),要么是水平的(row grid lines),来分割行或列。下面是一个列网格线的例子。

这里写图片描述

Grid Track

网格轨迹是两个相邻的网格线之间的空间。你可以把它想成网格的列或行。下面是一个在第二条和第三条行网格线的网格轨迹的例子。

这里写图片描述

Grid Cell

网格单元是由两个相邻的行网格线或列网格线围成的区域。它是网格的一个独立单元。下面是一个处于行网格线1、2和列网格线2、3之间的网格单元的例子。

这里写图片描述

Grid Area

网格区域是四条网格线围成的区域。网格区域可能由任意数量的网格单元构成。下面是一个处于行网格线1、3和列网格线1、3之间的网格区域的例子。

这里写图片描述

四、网格布局属性表

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

注意:每一行可能有不止一个名字。如下所示,第二行有row-1end和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。

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

您可以使用任意数量的相邻句点表示一个单独的空单元格。只要句点之间没有间隔它们就表示一个单独的单元。

要注意,该语法命名的是区域,而非行。不过使用该语法定义区域后,该区域两端都会自动获得命名。如,定义某个网格区域为foo,则该区域起始行和起始列的网格线将叫做foo-start,终止行和列将叫做foo-end。这也意味着一些网格线或有好几个名字。比如上例中最左边的网格线有三个名字:header-start、main-start和footer-start。

#grid-template

同时设置grid-template-rowsgrid-template-columnsgrid-template-areas的简写。

取值:

  • none - 设置这三个属性为最初的值
  • subgrid - 设置grid-template-rowsgrid-template-columnssubgridgrid-template-areas为起始值
  • <grid-template-rows>/<grid-template-columns> - 设置这两个属性为指定值,设置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-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-row-gapgrid-column-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-item相反)。该值应用到网格容器的所有网格项。

取值:

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

有时候网格的总尺寸比网格容器要小,比如当网格项目用诸如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;
}

.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和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,但我们定义的网格并没有列线5和6。因为我们参考的线并不存在,所以宽度为0的隐式的轨迹会被创建来填充缺口。我们可以使用grid-auto-columns和grid-auto-rows来指定这些隐式轨迹的宽度:

.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>

定义一个2行5列的网格,并设置grid-auto-flowrow(这也是默认值)。

.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-flowrow,我们的网格看起来如下图所示。注意到有3个项目(item-b、item-c和item-d)虽然并没有显示放置,但也流动到了可用行的位置。

要是设置grid-auto-flowcolumn,item-b、item-c和item-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-columnsgrid-auto-flow等属性的简写。grid-column-gapgrid-row-gap`会被设置为初始值,尽管在该属性中没有显示设置。

取值:

  • 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-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 - 表示自动布局,自动跨度或默认跨度为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
}

.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-endgrid-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

该属性可以引用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属性来完成。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!