--- title: 网格 slug: Learn/CSS/CSS_layout/Grids tags: - CSS - CSS网格 - 初学者 - 学习 - 布局 - 引导 - 教程 - 文章 - 编码脚本 - 网格 - 网格框架 - 网格设计 translation_of: Learn/CSS/CSS_layout/Grids ---
{{LearnSidebar}}
{{PreviousMenu("Learn/CSS/CSS_layout/Flexbox", "Learn/CSS/CSS_layout")}}

CSS网格是一个用于web的二维布局系统。利用网格,你可以把内容按照行与列的格式进行排版。另外,网格还能非常轻松地实现一些复杂的布局。关于使用网格进行页面排版,这篇文章包含了你需要的一切知识。

预备知识: HTML基础 (学习 HTML简介),以及了解CSS如何工作的(学习 CSS简介 和 盒子样式。)
目标: 要了解网格布局系统背后的基本概念,以及如何在一个网页上实现一个网格布局。

译者注:本篇中旧版教程主要讲如何自己编写网格布局,最后过渡到浏览器支持的 CSS Grid Layout。而当前(2019-04-29)大多数浏览器已经支持了 CSS Grid Layout,没必要自己编写了,新版教程仅介绍 CSS Grid Layout 的用法

什么是网格布局?

网格是由一系列水平及垂直的线构成的一种布局模式。根据网格,我们能够将设计元素进行排列,帮助我们设计一系列具有固定位置以及宽度的元素的页面,使我们的网站页面更加统一。

一个网格通常具有许多的列(column)行(row),以及行与行、列与列之间的间隙,这个间隙一般被称为沟槽(gutter)

[临时图; 将很快替换更好的图片。]

注意:任何有设计背景的人似乎都感到惊讶,CSS没有内置的网格系统,而我们似乎使用各种次优方法来创建网格状的设计。正如你将在本文的最后一部分中发现的那样,这将被改变,但是你可能需要知道在未来一段时间内创建网格的现有方法。

在CSS中创建自己的网格

决定好你的设计所需要的网格后,你可以创建一个CSS网格版面并放入各类元素。我们先来看看网格的基础功能,然后尝试做一个简单的网格系统。

下面这个视频提供了一个很好的解释:

{{EmbedYouTube("KOvGeFUHAC0")}}

定义一个网格

一如既往,你可以下载教程文件(你可以在线看到效果)。例子中有一个容器,容器中有一些子项。默认情况下,子项按照正常布局流自顶而下排布。在这篇文章中,我们会从这开始,对这些文件做一些改变,来了解网格是如何工作的。

首先,将容器的{{cssxref("display")}}属性设置为grid来定义一个网络。与弹性盒子一样,将父容器改为网格布局后,他的直接子项会变为网格项。把下面的css规则加到你的文件中。

.container {
    display: grid;
}

与弹性盒子不同的是,在定义网格后,网页并不会马上发生变化。因为display: grid的声明只创建了一个只有一列的网格,所以你的子项还是会像正常布局流那样从上而下一个接一个的排布。

为了让我们的容器看起来更像一个网格,我们要给刚定义的网格加一些列。那就让我们加三个宽度为200px的列。当然,这里可以用任何长度单位,包括百分比。

.container {
    display: grid;
    grid-template-columns: 200px 200px 200px;
}

在规则里加入你的第二个声明。刷新页面后,你会看到子项们排进了新定义的网格中。

{{ EmbedLiveSample('Grid_1', '100%', 400) }}

使用fr单位的灵活网格

除了长度和百分比,我们也可以用fr这个单位来灵活地定义网格的行与列的大小。这个单位表示了可用空间的一个比例,可能有点抽像,看看下面的例子吧。

使用下面的规则来创建3个1fr的列:

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

将窗口调窄(由于示例中设定了{{cssxref("max-width")}},可能需要很窄),你应该能看到每一列的宽度可以会随着可用空间变小而变小。fr 单位按比例划分了可用空间,如果没有理解,可以试着改一下数值,看看会发生什么,比如下面的代码:

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

这个定义里,第一列被分配了2fr可用空间,余下的两列各被分配了1fr的可用空间,这会使得第一列的宽度是第二第三列的两倍。另外,fr可以与一般的长度单位混合使用,比如grid-template-columns: 300px 2fr 1fr,那么第一列宽度是300px,剩下的两列会根据除去300px后的可用空间按比例分配。

{{ EmbedLiveSample('Grid_2', '100%', 400) }}

注意:fr单位分配的是可用空间而非所有空间,所以如果某一格包含的内容变多了,那么整个可用空间就会减少,可用空间是不包括那些已经确定被占用的空间的。

网格间隙

使用 {{cssxref("grid-column-gap")}} 属性来定义列间隙;使用 {{cssxref("grid-row-gap")}} 来定义行间隙;使用 {{cssxref("grid-gap")}} 可以同时设定两者。

.container {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr;
    grid-gap: 20px;
}

间隙距离可以用任何长度单位包括百分比来表示,但不能使用fr单位。

{{ EmbedLiveSample('Grid_3', '100%', 400) }}

注意:*gap属性曾经有一个grid-前缀,不过后来的标准进行了修改,目的是让他们能够在不同的布局方法中都能起作用。尽管现在这个前缀不会影响语义,但为了代码的健壮性,你可以把两个属性都写上。

.container {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  grid-gap: 20px;
  gap: 20px;
}

重复构建行/列

你可以使用repeat来重复构建具有某些宽度配置的某些列。举个例子,如果要创建多个等宽轨道,可以用下面的方法。

.container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-gap: 20px;
}

和之前一样,你仍然得到了3个1fr的列。第一个传入repeat函数的值(3)表明了后续列宽的配置要重复多少次,而第二个值(1fr)表示需要重复的构建配置,这个配置可以具有多个长度设定。例如repeat(2, 2fr 1fr),如果你仍然不明白,可以实际测试一下效果,这相当于填入了2fr 1fr 2fr 1fr

显式网格与隐式网格

到目前为止,我们定义过了列,但还没有管过行。但在这之前,我们要来理解一下显式网格和隐式网格。显式网格是我们用grid-template-columns 或 grid-template-rows 属性创建的。而隐式网格则是当有内容被放到网格外时才会生成的。显式网格与隐式网格的关系与弹性盒子的main和cross轴的关系有些类似。

隐式网格中生成的行/列大小是参数默认是auto,大小会根据放入的内容自动调整。当然,你也可以使用{{cssxref("grid-auto-rows")}}和{{cssxref("grid-auto-columns")}}属性手动设定隐式网格的大小。下面的例子将grid-auto-rows设为了100px,然后你可以看到那些隐式网格中的行(因为这个例子里没有设定{{cssxref("grid-template-rows")}},因此,所有行都位于隐式网格内)现在都是100像素高了。

译者注:简单来说,隐式网格就是为了放显式网格放不下的元素,浏览器根据已经定义的显式网格自动生成的网格部分。

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 100px;
  grid-gap: 20px;
}

{{ EmbedLiveSample('Grid_4', '100%', 400) }}

方便的minmax() 函数

100像素高的行/列有时可能会不够用,因为时常会有比100像素高的内容加进去。所以,我们希望可以将其设定为至少100像素,而且可以跟随内容来自动拓展尺寸保证能容纳所有内容。显而易见,你很难知道网页上某个元素的尺寸在不同情况下会变成多少,一些额外的内容或者更大的字号就会导致许多能做到像素级精准的设计出现问题。所以,我们有了{{cssxref("minmax")}}函数。

{{cssxref("minmax")}} 函数为一个行/列的尺寸设置了取值范围。比如设定为 minmax(100px, auto),那么尺寸就至少为100像素,并且如果内容尺寸大于100像素则会根据内容自动调整。在这里试一下把 grid-auto-rows 属性设置为minmax函数。

.container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-auto-rows: minmax(100px, auto);
    grid-gap: 20px;
}

如果所有网格内的内容均小于100像素,那么看起来不会有变化,但如果在某一项中放入很长的内容或者图片,你可以看到这个格子所在的哪一行的高度变成能刚好容纳内容的高度了。注意我们修改的是grid-auto-rows ,因此只会作用于隐式网格。当然,这一项属性也可以应用于显示网格,更多内容可以参考{{cssxref("minmax")}}页面。

自动使用多列填充

现在来试试把学到的关于网格的一切,包括repeat与minmax函数,组合起来,来实现一个非常有用的功能。某些情况下,我们需要让网格自动创建很多列来填满整个容器。通过设置grid-template-columns属性,我们可以实现这个效果,不过这一次我们会用到{{cssxref("repeat")}}函数中的一个关键字auto-fill来替代确定的重复次数。而函数的第二个参数,我们使用{{cssxref("minmax")}}函数来设定一个行/列的最小值,以及最大值1fr

在你的文件中试试看,你也许可以用到以下的代码。

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-auto-rows: minmax(100px, auto);
  grid-gap: 20px;
}

{{ EmbedLiveSample('Grid_5', '100%', 400) }}

你应该能看到形成了一个包含了许多至少200像素宽的列的网格,将容器填满。随着容器宽度的改变,网格会自动根据容器宽度进行调整,每一列的宽度总是大于200像素,并且容器总会被列填满。(This works because grid is creating as many 200 pixel columns as will fit into the container, then sharing whatever space is leftover between all of the columns — the maximum is 1fr which, as we already know, distributes space evenly between tracks.)

基于线的元素放置

在定义完了网格之后,我们要把元素放入网格中。我们的网格有许多分隔线,第一条线的起始点与文档书写模式相关。在英文中,第一条列分隔线(即网格边缘线)在网格的最左边而第一条行分隔线在网格的最上面。而对于阿拉伯语,第一条列分隔线在网格的最右边,因为阿拉伯文是从右往左书写的。

我们根据这些分隔线来放置元素,通过以下属性来指定从那条线开始到哪条线结束。

这些属性的值均为分隔线序号,你也可以用以下缩写形式来同时指定开始与结束的线。

注意开始与结束的线的序号要使用/符号分开。

下载这个文件(或者查看在线预览)。文件中已经定义了一个网格以及一篇简单的文章位于网格之外。你可以看到元素已经被自动放置到了我们创建的网格中。

接下来,尝试用定义网格线的方法将所有元素放置到网格中。将以下规则加入到你的css的末尾:

header {
  grid-column: 1 / 3;
  grid-row: 1;
}

article {
  grid-column: 2;
  grid-row: 2;
}

aside {
  grid-column: 1;
  grid-row: 2;
}

footer {
  grid-column: 1 / 3;
  grid-row: 3;
}

{{ EmbedLiveSample('Grid_6', '100%', 400) }}

注意:你也可以用-1来定位到最后一条列分隔线或是行分隔线,并且可以用负数来指定倒数的某一条分隔线。但是这只能用于显式网格,对于隐式网格-1不一定能定位到最后一条分隔线。

使用grid-template-areas属性放置元素

另一种往网格放元素的方式是用{{cssxref("grid-template-areas")}}属性,并且你要命名一些元素并在属性中使用这些名字作为一个区域。

将之前基于线的元素放置代码删除(或者重新下载一份新的文件),然后加入以下CSS规则:

.container {
  display: grid;
  grid-template-areas:
      "header header"
      "sidebar content"
      "footer footer";
  grid-template-columns: 1fr 3fr;
  grid-gap: 20px;
}

header {
  grid-area: header;
}

article {
  grid-area: content;
}

aside {
  grid-area: sidebar;
}

footer {
  grid-area: footer;
}

刷新页面,然后你应该能看到的元素会被放到与之前相同的地方,整个过程不需要我们指定任何分隔线序号。

{{ EmbedLiveSample('Grid_7', '100%', 400) }}

grid-template-areas属性的使用规则如下:

你可以在文件中尽情发挥你的想象来测试各种网格排版,比如把页脚放在内容之下,或者把侧边栏一直延伸到最底。这种直观的元素放置方式很棒,你在CSS中看到的就是实际会出现的排版效果。

一个用CSS网格实现的网格排版框架

网格排版框架一般由12到16列的网格构成,你可以用CSS网格系统直接实现而不需要任何第三方的工具,毕竟这是标准定义好了的。

下载这个初始文件,文件中包含了一个定义了12列网格的容器。文件中的一些内容我们曾在前两个示例中使用过,我们暂时可以先用基于线的元素放置模式来将我们的内容放到这个12列的网格中。

header {
  grid-column: 1 / 13;
  grid-row: 1;
}

article {
  grid-column: 4 / 13;
  grid-row: 2;
}

aside {
  grid-column: 1 / 4;
  grid-row: 2;
}

footer {
  grid-column: 1 / 13;
  grid-row: 3;
}

{{ EmbedLiveSample('Grid_8', '100%', 400) }}

你可以使用Firefox Grid Inspector去查看页面中的网格线,你应该能看到这12列的网格是如何工作的。

A 12 column grid overlaid on our design.

纸上得来终觉浅!

你已经读完了这篇教程,那你记住那些最重要的内容了么? 在继续之前,您可以通过一些其他测试来验证您是否真正学习到了这些知识,参见技能测试:网格.

小结

我们在这篇文章中接触了CSS网格版面的主要特性,你现在应该可以在你自己的设计中使用了。想深入了解这些内容,你可以读一读下面关于网格版面的文章,可以下面的推荐阅读里看到。

推荐阅读

{{PreviousMenuNext("Learn/CSS/CSS_layout/Flexbox", "Learn/CSS/CSS_layout/Floats", "Learn/CSS/CSS_layout")}}


以下是旧版教程

在你的项目中使用“网格系统”

为了确保整个网站或应用程序的一致性体验,从一开始就将其置于网格系统上,这意味着您不需要考虑某个元素相对于其他元素的宽度。您的选择限于“该元素将跨越多少个网格列”。

您的“网格系统”可以简单地是在设计过程中使用常规网格所做的决策。如果你的设计开始于一个图形编辑应用,如Photoshop的话,你可以参考这篇文章中所描述的过程创建一个网格 一个更好响应网页设计的Photoshop网格艾利特杰伊提供。

您的网格系统也可能是一个框架—— 无论是由第三方还是您为您自己的的项目创建的——通过CSS强制实现网格。

创建简单的网格框架

首先,看看我们将如何为你的项目创建一个简单的网格框架。

目前大多数网格类型布局是使用浮动创建的。如果你阅读过我们前面关于浮动的文章,你可以看到我们如何使用这种技术来创建一个多列布局——这是任何使用网格系统的本质方法。

要创建的最简单的网格框架类型是固定宽度的 —— 我们只需要计算出想要设计的总宽度,想要多少列,以及沟槽和列的宽度。如果我们决定在具有列根据浏览器宽度增长和缩小的网格上布置设计,我们需要计算出列和沟槽之间的百分比宽度。

在接下来的部分中,我们会讨论如何创建这两者。我们将创建一个12列网格 —— 一种很常见的选择,因为12可以被6、4、3和2整除,被认为非常适应不同的情况。

一个简单的定宽网格

首先,创建一个使用固定宽度列的网格系统。

制作本地样本simple-grid.html的文件副本,该文件在其body中包含以下标记。

<div class="wrapper">
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
    <div class="col">4</div>
    <div class="col">5</div>
    <div class="col">6</div>
    <div class="col">7</div>
    <div class="col">8</div>
    <div class="col">9</div>
    <div class="col">10</div>
    <div class="col">11</div>
    <div class="col">12</div>
  </div>
  <div class="row">
    <div class="col span1">13</div>
    <div class="col span6">14</div>
    <div class="col span3">15</div>
    <div class="col span2">16</div>
  </div>
</div>

第一行显示单个列的大小,第二行显示网格上一些不同大小的区域——目的是将其转换为12列上的两行演示网格。

为包装容器提供980像素的宽度,其右侧有20px的padding,这使总共列/沟槽宽度960像素——在这里,padding被整个content的宽度减去,因为我们将这里所有元素的{{cssxref("box-sizing")}}属性的值设置为 border-box  (可以看 Changing the box model completely 有更详细的解释)。在<style>元素中,添加以下代码。

* {
  box-sizing: border-box;
}


body {
  width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 20px;
}

在网格的每一行的行容器从另一行中清除一行,在上一个规则下面添加以下规则:

.row {
  clear: both;
}

此清除意味着我们不需要应用构成完整十二列的元素去填充每一行。行将保持分离,并且彼此不干扰。

列之间的沟槽为20像素宽。我们在每列的左侧创建一个20px的外边距(margin)作为沟槽——包括第一列,以平衡容器右侧的填充的20像素。所以,我们共有12个沟槽 — 12×20 = 240。

我们需要从960像素的总宽度中减去它,为列提供720像素。如果我们除以12,每列就应该是60像素宽。

下一步是为.col类创建一个规则集,让它向左浮动,给它一个20像素的{{cssxref("margin-left")}}形成一个沟槽,一个60像素的 {{cssxref("width")}}。将以下规则添加到CSS的底部:

.col {
  float: left;
  margin-left: 20px;
  width: 60px;
  background: rgb(255, 150, 150);
}

现在,最上面一行的每一列将被整齐地排列为网格。

注意:我们还为每个列指定了一个浅红色,以便您可以准确地看到每个列占用多少空间。

那些我们想要跨越多个列的布局容器需要被赋予特殊的类,来将它们的{{cssxref("width")}} 值调整到所需的列数(加上之间的沟槽)。我们需要创建一个额外的类,以允许容器跨越2到12列。每个宽度是将该列数的列宽加上沟槽宽度得到的结果,总是比列数少1。

在CSS的底部添加以下内容:

/* Two column widths (120px) plus one gutter width (20px) */
.col.span2 { width: 140px; }
/* Three column widths (180px) plus two gutter widths (40px) */
.col.span3 { width: 220px; }
/* And so on... */
.col.span4 { width: 300px; }
.col.span5 { width: 380px; }
.col.span6 { width: 460px; }
.col.span7 { width: 540px; }
.col.span8 { width: 620px; }
.col.span9 { width: 700px; }
.col.span10 { width: 780px; }
.col.span11 { width: 860px; }
.col.span12 { width: 940px; }

创建这些类后,我们现在可以在网格上布置不同的宽度列。尝试保存并在浏览器中加载页面以查看效果。

注意:如果您无法使上述示例工作,请尝试将其与我们在GitHub上完成的版本进行比较(也可以看在线运行)。

尝试修改元素上的类,添加和删除一些容器,看看如何改变布局。例如,您可以使第二行如下所示:

<div class="row">
  <div class="col span8">13</div>
  <div class="col span4">14</div>
</div>

现在你有一个网格系统工作!你可以简单地定义行和每一行的列数,然后填充每个容器所需的内容。

创建流体网格

我们的网格工作得很好,但它有一个固定的宽度。我们真的想要一个灵活(流体)网格,它将随着浏览器视口中的可用空间而增长和缩小。为了实现这一点,我们可以使用参考像素宽度并将其转换为百分比。

将固定的宽度变为基于百分比的灵活(flexible)宽度的公式如下。

target / context = result

对于列宽来说,上下文是一个960像素的包装器,目标的宽度是60像素,我们可以使用以下计算百分比。

60 / 960 = 0.0625

然后我们移动小数点2个位置,给出6.25%的百分比。因此,在我们的CSS中,我们可以替换60像素列宽度为6.25%。

我们需要对沟槽宽度做同样的事情:

20 / 960 = 0.02083333333

因此,我们需要更换20像素margin-left在我们的.col规则和20像素padding-right的.wrapper有2.08333333%。

更新网格

要开始使用本部分,请制作您之前的示例页面的新副本,或者将我们的simple-grid-finished.html代码的本地副本用作起点。

更新第二个CSS规则(使用.wrapper选择器)如下:

body {
  width: 90%;
  max-width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 2.08333333%;
}

不仅我们给了它一个百分比width,我们还添加了一个max-width属性,以阻止布局变得太宽。

接下来,更新第四个CSS规则(使用.col选择器)如下:

.col {
  float: left;
  margin-left: 2.08333333%;
  width: 6.25%;
  background: rgb(255, 150, 150);
}

现在来看稍微更费力的部分 —  我们需要更新所有的 .col.span 规则,使用百分比而不是像素宽度。这需要一点时间与计算器; 为了省你一些努力,我们已经为你做了下面。

使用以下内容更新CSS规则的底部块:

/* Two column widths (12.5%) plus one gutter width (2.08333333%) */
.col.span2 { width: 14.58333333%; }
/* Three column widths (18.75%) plus two gutter widths (4.1666666) */
.col.span3 { width: 22.91666666%; }
/* And so on... */
.col.span4 { width: 31.24999999%; }
.col.span5 { width: 39.58333332%; }
.col.span6 { width: 47.91666665%; }
.col.span7 { width: 56.24999998%; }
.col.span8 { width: 64.58333331%; }
.col.span9 { width: 72.91666664%; }
.col.span10 { width: 81.24999997%; }
.col.span11 { width: 89.5833333%; }
.col.span12 { width: 97.91666663%; }

现在保存您的代码,在浏览器中加载它,并尝试更改视口宽度 - 您应该看到列宽调整很好地适合!

注意:如果您无法使上述示例工作,请尝试将其与我们在GitHub上完成的版本进行比较请参见它如何运行的)。

使用calc() 函数更容易的计算

你可以使用calc()函数在你的CSS里面做数学 — 这允许你插入简单的数学方程到你的CSS值,计算一个值应该是什么。当需要执行复杂的数学运算时,它是特别有用的,甚至可以计算使用不同单位的计算,例如“我希望此元素的高度始终为父级高度的100%,减去50px”。从MediaRecorder API教程中查看此示例

无论如何,回到我们的网格!跨越网格的多个列的任何列具有6.25%的总宽度乘以跨越的列数加上2.08333333%乘以槽的数量(其将总是列数减去1)。该calc()函数允许我们在宽度值内部执行此计算,因此对于跨4列的任何项目,我们可以执行此操作,例如:

.col.span4 {
  width: calc((6.25%*4) + (2.08333333%*3));
}

尝试使用以下代码替换您的底部规则,然后在浏览器中重新加载它,看看是否得到相同的结果:

.col.span2 { width: calc((6.25%*2) + 2.08333333%); }
.col.span3 { width: calc((6.25%*3) + (2.08333333%*2)); }
.col.span4 { width: calc((6.25%*4) + (2.08333333%*3)); }
.col.span5 { width: calc((6.25%*5) + (2.08333333%*4)); }
.col.span6 { width: calc((6.25%*6) + (2.08333333%*5)); }
.col.span7 { width: calc((6.25%*7) + (2.08333333%*6)); }
.col.span8 { width: calc((6.25%*8) + (2.08333333%*7)); }
.col.span9 { width: calc((6.25%*9) + (2.08333333%*8)); }
.col.span10 { width: calc((6.25%*10) + (2.08333333%*9)); }
.col.span11 { width: calc((6.25%*11) + (2.08333333%*10)); }
.col.span12 { width: calc((6.25%*12) + (2.08333333%*11)); }

注意:您可以在fluid-grid-calc.html(也可以看到它的live)中看到我们的完成版本。

注意:如果你不能让这个工作,它可能是因为你的浏览器不支持该calc()功能,虽然它是相当支持跨浏览器 - 远在IE9

语义与“非语义”网格系统

向您的标记添加类来定义布局意味着您的内容和标记与其视觉呈现相关联。有时你会听到这种使用描述为“非语义”的CSS类 - 描述内容的外观 - 而不是描述内容的类的语义使用。这正是我们的情况下span2,span3类,等等。

这些不是唯一的办法,你可以改为决定网格。然后将大小信息添加到现有语义类的规则中。例如,如果你有一个<div>类content,你想跨越8列,你可以复制从span8类的宽度,给你一个像这样的规则:

.content {
  width: calc((6.25%*8) + (2.08333333%*7));
}

注意:如果你使用一个预处理器,如Sass,你可以创建一个简单的mixin来插入这个值。

在网格中启用偏移容器

我们创建的网格工作良好,只要我们想要启动所有的容器与网格的左手边齐平。如果我们想在第一个容器之前留下一个空的列空间 - 或者在容器之间 - 我们需要创建一个偏移类来添加一个左边距到我们的网站,以推动它在网格上。更多数学!

让我们试试这个。

从您之前的代码开始,或使用我们的fluid-grid.html文件作为起点。

让我们在CSS中创建一个类,它将容器元素偏移一列宽度。将以下内容添加到CSS的底部:

.offset-by-one {
  margin-left: calc(6.25% + (2.08333333%*2));
}

如果你喜欢自己计算百分比,请使用这一个:

.offset-by-one {
  margin-left: 10.41666666%;
}

现在可以将此类添加到任何容器,列如你要在其左侧留下一列宽的空白。在HTML中添加这个:

<div class="col span6">14</div>

尝试替换它

<div class="col span5 offset-by-one">14</div>

注意:您需要减少跨越的列数,为偏移量腾出空间!

尝试加载和刷新以查看差异,或查看我们的fluid-grid-offset.html示例(见在线运行)。完成的示例应如下所示:

注意:作为一个额外的练习,你能实现一个offset-by-two类吗?

浮动网格限制

当使用浮动网格时,你需要注意:你的总宽度要加起来正确,并且你不能在一行中包含跨(越)度为多列的超过该行所能包含的元素。由于浮动工作方式,如果网格列的数量相对于网格变得太宽,则末端上的元素将下降到下一行,从而打破网格。

还要记住,元素的内容比它们占据的行更宽,它会溢出,会看起来像一团糟。

这个系统的最大限制是它基本上是一维的。我们处理的是列元素只能跨越多个列,而不能跨越行。这些旧的布局方法非常难以控制元素的高度,而没有明确设置高度,这是一个非常不灵活的方法 - 它只有当你能保证你的内容将是一定的高度才有效。

Flexbox 网格?

如果你阅读我们以前关于flexbox的文章,可能会认为flexbox是创建网格系统的理想解决方案。目前有一些基于flexbox的网格系统可用,flexbox可以解决我们在创建上面的网格时已经发现的许多问题。

然而,flexbox从来没有被设计为网格系统,并且在作为一体时提出了一系列新的挑战。作为一个简单的例子,我们可以把我们上面使用同样的例子标记和使用以下CSS样式的wrapper,row和col类:

body {
  width: 90%;
  max-width: 980px;
  margin: 0 auto;
}

.wrapper {
  padding-right: 2.08333333%;
}


.row {
  display: flex;
}

.col {
  margin-left: 2.08333333%;
  margin-bottom: 1em;
  width: 6.25%;
  flex: 1 1 auto;
  background: rgb(255,150,150);
}

你可以在你自己的例子中尝试这些替换,或者看看我们的flexbox-grid.html示例代码(看它如何运行)。

这里我们把每一行变成一个flex容器。使用基于flexbox的网格,我们仍然需要行,以便允许我们添加小于100%的元素。我们设置该容器display: flex。

在.col我们将flex属性的第一个值(flex-grow)设置为1,项目可以增长,第二个值(flex-shrink)为1,所以项目可以收缩,第三个值(flex-basis)auto。由于我们的元素有一个width集合,auto将使用该宽度作为flex-basis值。

在顶端,我们在网格上获得十二个整洁的盒子,并且它们随着我们改变视口宽度而同样地增长和收缩。然而,在下一行,我们只有四个项目,这些也从60px基础增长和收缩。只有四个他们可以增长比上面的行中的项目多,结果是他们都占据第二行相同的宽度。

为了解决这个问题,我们仍然需要包含我们的span类来提供一个宽度来替换flex-basis那个元素所使用的值。

他们也不尊重上面的网格,因为他们不知道如何使用它。

Flexbox一维设计。它处理单个维度,即行或列。不能为列和行创建严格的网格,这意味着如果我们要为网格使用flexbox,我们仍然需要为浮动布局计算百分比。

在您的项目中,您可能仍然选择使用flexbox'grid',因为flexbox提供的额外对齐和空间分布能力超过浮动。但是,您应该知道,您仍在使用工具,而不是它的设计目的。所以你可能会觉得它让你跳过额外的箍,得到你想要的最终结果。

第三方网格系统

现在我们了解了我们的网格计算背后的数学,我们是一个很好的地方看看一些第三方网格系统的共同使用。如果你在网上搜索“CSS Grid框架”,你会发现一个巨大的选项列表可供选择。流行的框架如BootstrapFoundation包括一个网格系统。还有独立的网格系统,使用CSS或使用预处理器开发。

让我们来看看这些独立系统之一,因为它演示了使用网格框架的常见技术。我们将使用的网格是Skeleton的一部分,一个简单的CSS框架。

开始访问Skeleton网站,并选择“下载”以下载ZIP文件,解压缩此文件并将skeleton.css和normalize.css文件复制到一个新目录中。

制作我们的html-skeleton.html文件副本,并将其保存在与骨架相同的目录中,并规范化CSS。

在HTML页面中包含骨架并规范化CSS,方法是在其头部添加以下内容:

<link href="normalize.css" rel="stylesheet">
<link href="skeleton.css" rel="stylesheet">

Skeleton不仅仅包括一个网格系统 - 它还包含用于排版的CSS和其他可以用作起点的页面元素。我们现在将这些默认值,但是 - 这是我们真正感兴趣的网格在这里。

注意:Normalize是由Nicolas Gallagher编写的一个非常有用的小型CSS库,它自动执行一些有用的基本布局修复,并使默认元素样式在不同浏览器之间更一致。

我们将使用类似的HTML到我们前面的例子。在您的HTML内文中添加以下内容:

<div class="container">
  <div class="row">
    <div class="col">1</div>
    <div class="col">2</div>
    <div class="col">3</div>
    <div class="col">4</div>
    <div class="col">5</div>
    <div class="col">6</div>
    <div class="col">7</div>
    <div class="col">8</div>
    <div class="col">9</div>
    <div class="col">10</div>
    <div class="col">11</div>
    <div class="col">12</div>
  </div>
  <div class="row">
    <div class="col">13</div>
    <div class="col">14</div>
    <div class="col">15</div>
    <div class="col">16</div>
  </div>
</div>

要开始使用Skeleton,我们需要给包装器<div>一个类container- 这已经包括在我们的HTML中。这将以960像素的最大宽度为中心。你可以看到盒子现在从不变得宽于960像素。

你可以看一下skeleton.css文件,看看我们应用这个类时使用的CSS。在<div>使用居中auto左,右页边距,以及20像素的填充应用于左侧和右侧。Skeleton也设置box-sizing属性border-box像我们以前做的,所以此元素的填充和边框将包括在总宽度。

.container {
  position: relative;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 0 20px;
  box-sizing: border-box;
}

元素只能是网格的一部分(如果它们在一行内),因此与前面的示例一样,我们需要一个额外的<div>或其他元素,其中一个类row嵌套在content <div>实际的内容容器之间<div>。我们已经做到了这一点。

现在让我们布置集装箱。骨架基于12列网格。顶行框都需要类,one column以使它们跨越一列。

现在添加这些,如下面的代码段所示:

<div class="container">
  <div class="row">
    <div class="col one column">1</div>
    <div class="col one column">2</div>
    <div class="col one column">3</div>
    /* and so on */
  </div>
</div>

接下来,给出第二行类上的容器,解释它们应该跨越的列数,如下:

<div class="row">
  <div class="col one column">13</div>
  <div class="col six columns">14</div>
  <div class="col three columns">15</div>
  <div class="col two columns">16</div>
</div>

尝试保存HTML文件并将其加载到浏览器中以查看效果。

注意:如果您无法使此示例工作,请尝试将其与我们的html-skeleton-finished.html文件进行比较(见在线运行)。

如果你看看skeleton.css文件,你可以看到这是如何工作的。例如,Skeleton对下面定义的样式元素添加了“三列”类。

.three.columns { width: 22%; }

所有的Skeleton(或任何其他网格框架)正在设置预定义的类,您可以通过将它们添加到您的标记使用。这和你自己计算这些百分比的工作完全一样。

正如你所看到的,当使用Skeleton时,我们需要写很少的CSS。它处理所有的浮动我们当我们添加类到我们的标记。正是这种将布局的责任转移到其他使网格系统的框架成为一个引人注目的选择的能力!

骨架是比你可能遇到的一些框架更简单的网格系统。大型框架(如Bootstrap和Foundation)中的网格为各种屏幕宽度提供了更多的功能和额外的断点。但是,它们都以类似的方式工作 - 通过向您的标记添加特定类,您可以使用预定义网格控制元素的布局。

本地CSS网格与网格布局

我们在本文一开始所说的,CSS的之前没有过一个真正的体系,用于创建网格布局。但这已经改变了。大部分常用的浏览器的最新版本已经提供了对新CSS的网格布局模块的支持。

我们看过上面的Skeleton Grid框架 - 像其他第三方网格,甚至是手工构建的网格,它需要你添加<div>s形成行,然后指定这些行中的项目将跨越的列数。

使用CSS网格布局,您可以完全在CSS中指定网格,而不需要将这些帮助类添加到标记。让我们看看我们的简单示例,看看我们将如何使用CSS Grid Layout创建相同的布局。

构建本地网格

首先,通过制作css-grid.html文件的本地副本来开始。它包含以下标记:

<div class="wrapper">
  <div class="col">1</div>
  <div class="col">2</div>
  <div class="col">3</div>
  <div class="col">4</div>
  <div class="col">5</div>
  <div class="col">6</div>
  <div class="col">7</div>
  <div class="col">8</div>
  <div class="col">9</div>
  <div class="col">10</div>
  <div class="col">11</div>
  <div class="col">12</div>
  <div class="col">13</div>
  <div class="col span6">14</div>
  <div class="col span3">15</div>
  <div class="col span2">16</div>
</div>

这次我们有一个父 <div> 的类 wrapper,所有的子元素只是直接出现在包装器内——没有行元素。我们已经将一个类添加到应该跨越多个列的项目。

现在将以下内容添加到<style>元素中:

.wrapper {
  width: 90%;
  max-width: 960px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-gap: 20px;
}

.col {
  background: rgb(255,150,150);
}

这里我们设置.wrapper规则,因此它是90%的身体宽度,居中,并且 max-width 为 960px。

现在为CSS网格属性。我们可以使用{{cssxref("display")}} 属性的 grid 值声明一个网格,使用 {{cssxref("grid-gap")}} 设置网格的间隔,然后使用{{cssxref("grid-template-columns")}} 属性、 repeat() 函数和 fr 单位——这个为网格布局定义的单位——创建一个12列等宽的网格。

该fr单元是一小部分单元-它描述在网格容器的可用空间的一小部分。如果所有列都是1fr,它们将占用相等的空间量。这消除了计算百分比以创建灵活网格的需要。

创建网格后,网格自动布局规则将立即在这个网格上布置我们的框,我们得到一个十二列灵活的网格布局。

要对跨越网格上的多个列轨道的容器进行样式化,我们可以使用该grid-column属性。跨6列例如:

.span6 {
  grid-column: auto / span 6;
}

跨越3:

.span3 {
  grid-column: auto / span 3;
}

正斜杠之前的值是开始列——在这种情况下,我们没有明确设置,允许浏览器放在下一个可用的列。然后我们可以设置它跨越6,3或我们想要的许多列。

在CSS的底部添加以下内容:

.span2 { grid-column: auto / span 2;}
.span3 { grid-column: auto / span 3;}
.span4 { grid-column: auto / span 4;}
.span5 { grid-column: auto / span 5;}
.span6 { grid-column: auto / span 6;}
.span7 { grid-column: auto / span 7;}
.span8 { grid-column: auto / span 8;}
.span9 { grid-column: auto / span 9;}
.span10 { grid-column: auto / span 10;}
.span11 { grid-column: auto / span 11;}
.span12 { grid-column: auto / span 12;}

OK!尝试保存和刷新,你会看到容器适当地跨多个列。

CSS网格是二维的,因此随着布局的增长和缩小,元素保持水平和垂直排列。

您可以通过将以下内容替换最后的4个字符串来进行测试<div>:

<div class="col">13some<br>content</div>
<div class="col span6">14this<br>is<br>more<br>content</div>
<div class="col span3">15this<br>is<br>less</div>
<div class="col span2">16</div>

这里我们有意添加了一些行break(<br>)标签,以强制某些列变得比其他列高。如果你尝试保存和刷新,你会看到列的高度调整为与最高的容器一样高,所以一切都保持整洁。

最终的布局如下:

注意:如果您无法使此示例工作,您可以检查您的代码与我们的完成版本(也可以看在线运行)。

一些不错的CSS网格特性

对于CSS网格,我们不需要通过边距来抵消它们。尝试在您的CSS中进行这些更改:

.content {
  grid-column: 2 / 8;
}
<div class="col span2 content">16</div>

容器16,现在将下一个可用的行上跨越第2列到第8列。

我们可以像跨越列一样轻松地跨越多行:

.content {
  grid-column: 2 / 8;
  grid-row: 3 / 5;
}

现在将容器16,跨越行3至5以及列2至8。

不需要使用边距伪造沟槽或显式计算它们的宽度 — CSS网格具有这种功能内置的grid-gap 属性。

我们只是接触了CSS网格布局所有可能的皮毛,但在本文的行文中要理解的关键是,你不需要用网格创建一个网格系统——它已经是一个了。您可以编写CSS,将项目直接放入预定义的网格上。要了解更多,请看 CSS Grid Layout Module

主动学习:编写自己的简单网格

CSS布局aticle简介中,我们包括一个关于CSS表的部分,其中包括一个简单的形式示例(参见css-tables-example.html实例和源代码)。我们希望您复制此示例,并执行以下操作:

  1. 删除 <div>元素 —  将会对你的内容处理行数和列。就不再需要此作为CSS网格。
  2. 使用CSS网格属性创建一个接近原始的表单布局,你必须设定容器元素的宽度,并思考怎么设置列的的间隙和行差距。

注意:先去做这个,如果你真的卡住了,你可以检查你的代码和我们的css-tables-as-grid.html例子。不要作弊,先试着练习!

概要

阅读这篇文章后,你应该已经了解了网格布局和网格框架如何在CSS中工作。你也已经窥探未来的CSS网格,现在应该明白,我们今天使用的网格框架本质上是一个临时的解决方案,直到我们有一个广泛支持的本地方式在CSS中实现这一点。

{{PreviousMenu("Learn/CSS/CSS_layout/Flexbox", "Learn/CSS/CSS_layout")}}

在本单元中