旧版布局方法
网格系统是 CSS 布局中非常常见的功能。在 CSS 网格布局出现之前,它们通常使用浮动或其他布局功能来实现。您将布局想象成一组固定数量的列(例如 4、6 或 12),然后将您的内容列放入这些假想的列中。在本文中,我们将探讨这些旧方法是如何工作的,以便您在处理旧项目时了解它们是如何使用的。
CSS 网格布局之前的布局和网格系统
对于有设计背景的人来说,CSS 直到最近才内置网格系统,这可能令人惊讶。相反,我们似乎一直在使用各种次优方法来创建类似网格的设计。我们现在将这些方法称为“传统”方法。
对于新项目,在大多数情况下,CSS 网格布局将与一种或多种其他现代布局方法结合使用,作为任何布局的基础。但是,您会不时遇到使用这些传统方法的“网格系统”。了解它们的工作原理以及它们与 CSS 网格布局的不同之处是值得的。
本课程将解释基于浮动和 Flexbox 的网格系统和网格框架是如何工作的。学习了网格布局后,您可能会惊讶于这一切看起来多么复杂!如果您需要为不支持新方法的浏览器创建备用代码,以及允许您处理使用这些类型系统的现有项目,这些知识将对您有所帮助。
在探索这些系统时,值得记住的是,它们都没有以 CSS 网格布局创建网格的方式实际创建网格。它们通过给项目一个大小并将它们推来推去以使其对齐,从而看起来像一个网格。
两列布局
让我们从最简单的例子开始——两列布局。您可以按照以下步骤操作:在您的计算机上创建一个新的 index.html 文件,用一个 简单的 HTML 模板 填充它,然后将以下代码插入到适当的位置。在本节的底部,您可以看到最终代码的实时示例。
首先,我们需要一些内容放入我们的列中。将主体中当前的所有内容替换为以下内容
<h1>2 column layout example</h1>
<div>
<h2>First column</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
aliquam dolor, eu lacinia lorem placerat vulputate. Duis felis orci,
pulvinar id metus ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at
ultricies tellus laoreet sit amet. Sed auctor cursus massa at porta. Integer
ligula ipsum, tristique sit amet orci vel, viverra egestas ligula. Curabitur
vehicula tellus neque, ac ornare ex malesuada et. In vitae convallis lacus.
Aliquam erat volutpat. Suspendisse ac imperdiet turpis. Aenean finibus
sollicitudin eros pharetra congue. Duis ornare egestas augue ut luctus.
Proin blandit quam nec lacus varius commodo et a urna. Ut id ornare felis,
eget fermentum sapien.
</p>
</div>
<div>
<h2>Second column</h2>
<p>
Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
est. Nam id risus quis ante semper consectetur eget aliquam lorem. Vivamus
tristique elit dolor, sed pretium metus suscipit vel. Mauris ultricies
lectus sed lobortis finibus. Vivamus eu urna eget velit cursus viverra quis
vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum sociis natoque
penatibus et magnis dis parturient montes, nascetur ridiculus mus.
</p>
</div>
每个列都需要一个外部元素来包含其内容,并让我们一次性操作所有内容。在此示例中,我们选择了 <div>,但您可以选择更具语义的元素,例如 <article>、<section> 和 <aside> 等。
现在是 CSS。首先,将以下内容应用于您的 HTML 以提供一些基本设置
body {
width: 90%;
max-width: 900px;
margin: 0 auto;
}
主体将占据视口宽度的 90%,直到它达到 900 像素宽,此时它将保持这个宽度并居中在视口中。默认情况下,它的子元素(h1 和两个 <div>)将占据主体宽度的 100%。如果我们要让这两个 <div> 彼此浮动,我们需要将它们的宽度设置为其父元素总宽度的 100% 或更小,以便它们可以彼此并排排列。将以下内容添加到您的 CSS 底部
div:nth-of-type(1) {
width: 48%;
}
div:nth-of-type(2) {
width: 48%;
}
在这里,我们都将它们设置为其父级宽度的 48%——总计 96%,剩下 4% 的空间作为两列之间的间距,让内容有一些呼吸空间。现在我们只需浮动这些列,如下所示
div:nth-of-type(1) {
width: 48%;
float: left;
}
div:nth-of-type(2) {
width: 48%;
float: right;
}
将所有这些放在一起应该会得到如下结果
您会注意到这里我们所有的宽度都使用了百分比——这是一个相当不错的策略,因为它创建了一个**流体布局**,一个可以适应不同屏幕尺寸并在较小屏幕尺寸下保持相同列宽比例的布局。尝试调整浏览器窗口的宽度,亲自看看。这是响应式网页设计的一个有价值的工具。
注意:您可以在 0_two-column-layout.html 查看此示例(另请参阅源代码)。
创建简单的传统网格框架
大多数传统框架都利用 float 属性的行为,将一列浮动到另一列旁边,以创建类似网格的东西。通过创建浮动网格的过程,您可以了解其工作原理,并引入一些更高级的概念,以在您在关于 浮动和清除 的课程中学到的知识上进行构建。
最容易创建的网格框架是固定宽度的——我们只需要计算出我们设计的总宽度、我们想要的列数以及间距和列的宽度。如果我们决定将设计布局在一个网格上,其列根据浏览器宽度而增长和收缩,那么我们需要计算列的百分比宽度以及它们之间的间距。
在接下来的部分中,我们将探讨如何创建这两种网格。我们将创建一个 12 列网格——这是一个非常常见的选择,鉴于 12 可以被 6、4、3 和 2 整除,因此被认为非常适合不同的情况。
一个简单的固定宽度网格
我们先创建一个使用固定宽度列的网格系统。
首先,复制我们的示例 simple-grid.html 文件,其中包含以下标记。
<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>
目标是将其转换为一个十二列网格上的两行演示网格——顶行演示单个列的大小,第二行演示网格上不同大小的区域。

在 <style> 元素中,添加以下代码,它为包装容器提供了 980 像素的宽度,右侧填充 20 像素。这使我们总共有 960 像素用于我们的总列/间距宽度——在这种情况下,填充是从总内容宽度中减去的,因为我们将 box-sizing 设置为站点上所有元素的 border-box(有关更多解释,请参阅 替代 CSS 盒模型)。
* {
box-sizing: border-box;
}
body {
width: 980px;
margin: 0 auto;
}
.wrapper {
padding-right: 20px;
}
现在使用包裹每行网格的行容器来清除一行和另一行。在您之前的规则下面添加以下规则
.row {
clear: both;
}
应用此清除意味着我们不需要用完整的十二列元素完全填充每一行。行将保持分离,并且不会相互干扰。
列之间的间距为 20 像素宽。我们通过在每个列的左侧创建边距来创建这些间距——包括第一列,以平衡容器右侧 20 像素的填充。所以我们总共有 12 个间距——12 x 20 = 240。
我们需要从总宽度 960 像素中减去这个值,这样我们的列就剩下 720 像素。如果我们现在将这个值除以 12,我们知道每个列的宽度应该是 60 像素。
我们的下一步是为类 .col 创建一个规则,将其左浮动,为其提供 20 像素的 margin-left 以形成间距,以及 60 像素的 width。将以下规则添加到您的 CSS 底部
.col {
float: left;
margin-left: 20px;
width: 60px;
background: rgb(255 150 150);
}
顶部的单列现在将整齐地排列成一个网格。
注意:我们还为每列指定了浅红色,这样您就可以确切地看到每列占用了多少空间。
我们希望跨越多列的布局容器需要赋予特殊的类,以将其 width 值调整为所需的列数(加上之间的间距)。我们需要创建一个额外的类,以允许容器跨越 2 到 12 列。每个宽度都是该列数的列宽加上间距宽度的结果,间距宽度总是比列数少一个。
将以下内容添加到您的 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;
}
创建这些类后,我们现在可以在网格上布局不同宽度的列。尝试保存并在浏览器中加载页面以查看效果。
尝试修改元素上的类,甚至添加和删除一些容器,以查看如何改变布局。例如,您可以使第二行看起来像这样
<div class="row">
<div class="col span8">13</div>
<div class="col span4">14</div>
</div>
现在您已经拥有一个工作的网格系统,您可以定义行和每行中的列数,然后用所需内容填充每个容器。太棒了!
创建流体网格
我们的网格运行良好,但它是固定宽度的。我们真正想要的是一个灵活(流体)的网格,它会随着浏览器 视口 中的可用空间而增长和收缩。为了实现这一点,我们可以将参考像素宽度转换为百分比。
将固定宽度转换为基于百分比的灵活宽度的公式如下。
target / context = result
对于我们的列宽,我们的**目标宽度**是 60 像素,我们的**上下文**是 960 像素的包装器。我们可以使用以下公式计算百分比。
60 / 960 = 0.0625
然后我们将小数点向右移动 2 位,得到 6.25% 的百分比。因此,在我们的 CSS 中,我们可以用 6.25% 替换 60 像素的列宽。
我们还需要对我们的间距宽度做同样的事情
20 / 960 = 0.02083333333
所以我们需要将 .col 规则上的 20 像素 margin-left 和 .wrapper 上的 20 像素 padding-right 替换为 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%;
}
现在保存你的代码,在浏览器中加载它,然后尝试改变视口宽度——你应该会看到列宽很好地调整以适应。
使用 calc() 函数进行更简单的计算
您可以使用 calc() 函数在 CSS 中直接进行计算——这允许您在 CSS 值中插入简单的数学方程式,以计算一个值应该是什么。当需要进行复杂的数学运算时,它特别有用,您甚至可以计算使用不同单位的计算,例如“我希望这个元素的高度始终是其父元素高度的 100%,减去 50 像素”。请参阅 此 MediaStream Recording 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 中查看我们完成的版本(另请参阅 实时运行)。
语义与“非语义”网格系统
向标记添加类来定义布局意味着您的内容和标记与您的视觉呈现绑定在一起。您有时会听到这种 CSS 类用法被描述为“非语义”——描述内容的外观——而不是描述内容的语义类用法。我们的 span2、span3 等类就是这种情况。
这些并非唯一的方法。您可以改为确定您的网格,然后将尺寸信息添加到现有语义类的规则中。例如,如果您有一个带有 content 类的 <div>,您希望它跨越 8 列,您可以从 span8 类复制宽度,从而得到如下规则
.content {
width: calc((6.25% * 8) + (2.08333333% * 7));
}
注意:如果您使用 Sass 等预处理器,您可以创建一个简单的混入来为您插入该值。
在我们的网格中启用偏移容器
我们创建的网格在所有容器都与网格左侧齐平的情况下运行良好。如果我们在第一个容器之前或容器之间留出一个空的列空间,我们就需要创建一个偏移类,为我们的站点添加左外边距,以在视觉上将其推过网格。更多数学!
我们来试试看。
从您之前的代码开始,或者使用我们的 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 示例代码(另请参阅 实时运行)。
在这里,我们将每一行都变成了一个弹性容器。对于基于弹性盒的网格,我们仍然需要行,以便允许我们拥有总和小于 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“网格”,因为 flexbox 比浮动提供了额外的对齐和空间分布功能。但是,您应该意识到,您仍然在使用一个并非为此目的设计的工具。因此,您可能会觉得它让您需要进行额外的操作才能达到您想要的结果。
第三方网格系统
既然我们已经了解了网格计算背后的数学原理,那么我们就可以很好地研究一些常用的第三方网格系统了。如果您在网上搜索“CSS 网格框架”,您会发现大量的选择。像 Bootstrap 和 Foundation 这样的流行框架都包含网格系统。还有独立的网格系统,它们使用 CSS 或预处理器开发。
让我们来看看其中一个独立的系统,因为它展示了使用网格框架的常用技术。我们将使用的网格是 Skeleton(一个简单的 CSS 框架)的一部分。
首先访问 Skeleton 网站,然后选择“下载”以下载 ZIP 文件。解压此文件,并将 skeleton.css 和 normalize.css 文件复制到一个新目录中。
复制我们的 html-skeleton.html 文件,并将其保存在与 skeleton 和 normalize CSS 相同的目录中。
通过在 HTML 页面的 head 中添加以下内容,将 skeleton 和 normalize CSS 包含到 HTML 页面中
<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,嵌套在内容 <div> 元素和容器 <div> 之间。我们也已经这样做了。
现在我们来布置容器盒。Skeleton 基于 12 列网格。顶部行中的所有盒都需要 one column 类,使它们跨越一列。
现在添加这些,如下面的代码片段所示
<div class="container">
<div class="row">
<div class="one column">1</div>
<div class="one column">2</div>
<div class="one column">3</div>
/* and so on */
</div>
</div>
接下来,为第二行中的容器添加类,说明它们应该跨越的列数,如下所示
<div class="row">
<div class="one column">13</div>
<div class="six columns">14</div>
<div class="three columns">15</div>
<div class="two columns">16</div>
</div>
尝试保存您的 HTML 文件并在浏览器中加载它以查看效果。
注意:如果您在使此示例正常工作时遇到问题,请尝试加宽用于查看它的窗口(如果窗口太窄,网格将不会像此处描述的那样显示)。如果这不起作用,请尝试将其与我们的 html-skeleton-finished.html 文件进行比较(也请参阅 实时运行)。
如果你查看 skeleton.css 文件,你就能看到它是如何工作的。例如,Skeleton 定义了以下内容来样式化添加了“三列”类的元素。
.three.columns {
width: 22%;
}
Skeleton(或任何其他网格框架)所做的只是设置预定义的类,您可以通过将它们添加到您的标记来使用它们。这与您自己计算这些百分比的工作完全相同。
如您所见,在使用 Skeleton 时,我们几乎不需要编写任何 CSS。当我们向标记添加类时,它会为我们处理所有的浮动。正是这种将布局责任交给其他东西的能力使得使用框架作为网格系统成为一个引人注目的选择!然而,如今,随着 CSS 网格布局的出现,许多开发人员正在放弃这些框架,转而使用 CSS 提供的内置原生网格。
总结
您现在已经了解了各种网格系统是如何创建的,这对于处理旧站点以及理解 CSS 网格布局的本机网格与这些旧系统之间的差异将非常有用。