Skip to content

前现代布局 ​

元信息 ​

  • 目标:沿"表格→浮动→栅格系统"的历史线看清布局方法的演化逻辑——每一种都是对前一种缺陷的回答;理解布局的本义、浮动从文字环绕到多栏再到被弃用的全过程、栅格作为设计理念的延续
  • 关键概念:布局、表格布局、浮动、高度塌陷、清除浮动、栅格、container/column/gutter、960.css
  • 关联阶段:cp2
  • 常见误区:以为"禁止表格布局"连 display:table 也一并禁用(被废弃的是拿 table 语义标记做布局,表格布局特性本身已回归 CSS);以为浮动天生是布局工具(它最初为文字环绕而生,布局是对它的挪用);把栅格布局等同于 CSS Grid(栅格是设计理念,早期基于 float 实现,如今由 flex/grid 承载)
  • 在线示例:布局演化八版——同一页面骨架八代重写(表格→display:table→浮动→960 栅格→flex→grid),第 2–7 版 HTML 一字未动只换样式,相邻版本可左右对照差异

前置 ​

布局的本义 ​

布局(layout)指的是网页元素之间的相对关系——谁在左谁在右、谁宽谁窄、上下如何排布。Web 的古早时代 CSS 尚未成熟,开发者只能借用 HTML 内置的盒子类型凑出多行多列的视觉效果。这段"古代史"值得学,不是因为要再用,而是因为后续每一种现代方案,都是对这段历史中某个缺陷的回答。

表格时代:语义标记被借去做布局 ​

最现成的多列结构是表格,于是它被借去干布局的活:

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

问题出在"表格"一词的两层含义被混用了:

  • 作为语义:它表示一组关系型数据(这是表格的本职,见第 2 周)
  • 作为视觉布局:它表示多行多列的排列样式

拿语义标记做视觉呈现,复杂布局就得表格套表格,HTML 里堆满语义无关的标记,爬虫和读屏苦不堪言。因此现代规范禁止用 table 标记做布局。

有意思的是后半段故事:CSS 成熟后,表格布局的特性(等高列、垂直居中)被收回样式层——display: table 与 display: table-cell 让普通 div 也能享用,且不污染语义:

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

案例对照:表格布局版 → display:table 版——同一副骨架走过这两步:表格版是真实 <table> 标记,display:table 版换成 div 三层,语义还给 HTML、等高列留给 CSS。

浮动时代:把块元素"漂"成多栏 ​

浮动的最初用途是文字环绕图片——图片浮到一侧,文字绕着它流:

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

关键观察:浮动元素脱离了正常的文档流位置,但文字知道绕开它——这与绝对定位的"完全压在上面"不同(见定位)。

古人发现了另一个用途:块元素浮动后不再独占一行,会横向排排站——多栏布局有了着落。让各栏全部 float: left,宽度用百分比,就是当年最主流的布局法。

高度塌陷与清除浮动 ​

全部子元素漂走后,父元素的高度失去了依据(塌陷为 0),后续元素于是"撞上"还在漂的子元素,布局乱套。这就是高度塌陷,浮动布局的头号事故。

解法叫清除浮动,机制要从 clear 的规范定义讲起:挂了 clear 的盒子,上边缘必须低于它之前的浮动盒。于是在浮动元素之后放一个挂 clear 的盒子,它会被压到浮动之下;父元素算高度时把它包进去,高度自然回到了浮动的下沿——不是"把浮动重新计入",是这个盒子的位置把父高度撑到了那里。早期做法是加一个空标记 <div style="clear:both"></div>,能用但污染结构;现代做法就是 clearfix——用内容生成在样式层造出那个"清除点"(见伪类、伪元素与内容生成),HTML 零改动:

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

动手:把 .main::after 那行注释掉再看,父元素边框立刻塌陷——这一眼胜过千言万语。

另一条同效路:给父元素设 overflow: hidden——它让父元素建立了 BFC(块级格式化上下文,见定型),独立排版环境必须包住自己的浮动。

案例对照:浮动多栏版——整副骨架用 float 百分比栏实现,样式表最后一块就是这里的 clearfix;高度塌陷的事故现场与修复,定位九版有专版逐步复现。

浮动布局至此凑合能用,但代价清单很长:要记得清除浮动、要防百分比加边框超宽、改一栏全栏牵动。它撑起了 Web 的少年时代,直到 flex/grid 出台才功成身退(做布局请直接用现代方案;float 只干它的老本行——文字环绕,或干脆不用)。

栅格系统:浮动时代的工程化 ​

浮动布局还有一个深层缺陷:实现细节太多,不利于共享。有没有办法让开发者专注布局的设计而非实现,像写 HTML 那样声明式地表达布局?

栅格(grid)思想给出了答案。设计行业早就有坐标纸:纸上打满栅格,设计师无需尺子就能对齐边界、控制比例。

栅格布局-0

把它搬进 Web:预先约定若干等宽的列和列间距,布局变成"占几列"的声明。960.css 是这个思路的代表作(在线演示)——引入它的样式包,元素挂上约定好的类名,立刻获得 12 列体系里的对应宽度:

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

引入样式包,元素挂上约定好的类名,立刻获得 12 列体系里的对应宽度。它的概念词汇沿用至今:

  • container:包裹整个栅格系统的容器
  • column:一列(12 列体系下每列 60px)
  • gutter:列与列之间的空隙(20px)

算式:960px = 10 + 60×12 + 20×11 + 10(两侧补白各 10px)。实现原理朴素得惊人——把每种"占 N 列"的宽度预先算好写死成类,底层正是 float:

css
.container_12 .grid_1 { width: 60px; }
.container_12 .grid_2 { width: 140px; }
/* …… */
.grid_1, .grid_2, /* …… */ .grid_12 {
  float: left;
  margin-left: 10px;   /* gutter */
  margin-right: 10px;
}

案例对照:栅格系统 960 版——container_12 加 grid_8/grid_4 挂类名即得两栏,在线改类名可试 12 列的其它组合。

固定 960px、不支持移动端、不支持响应式——960.css 的缺陷清单,恰好就是下一代的需求清单。它的继任者 bootstrap 栅格保留了类名约定(.row、.col-md-6),底层换成 flex,并带上断点支持(见媒体查询)。

演化逻辑小结 ​

方案回答了什么留下什么缺陷
表格布局多行多列从哪来语义污染,难以维护
浮动布局布局与语义分离高度塌陷、细节繁琐、难共享
栅格系统声明式表达布局底层仍是 float,固定宽度不响应
flex / grid(下两篇)一维轴线 / 二维网格的原生能力——

这张表的每一行,都是布局演化八版里一个可运行的版本——顺链走一遍,或逐代对照差异面板。

栅格布局是一种设计理念而非具体技术:早期基于 float 实现,如今由 flex/grid 承载,设计工具里的网格参考线也是它。

延伸 ​