前置知识: CSS

传统布局技术

10 min中级

浮动、定位、BFC 与经典布局方案。

前置知识

建议先阅读以下内容再进入本文:

前置依赖:先读 003 盒模型与 015 浮动。圣杯/双飞翼布局为进阶内容,0 基础可先跳过。

0. 直觉:没有 Flexbox/Grid 的年代怎么排版

在 Flexbox 与 Grid 出现之前,网页排版靠两件“老工具”:浮动(float)和定位(position)。浮动让元素“靠边站”并让文字环绕,定位让元素“钉”在页面的某个位置。

现代项目已经很少用浮动做整体布局,但很多老代码、打印样式和特殊排版仍在用。理解它们,才能读懂历史代码,也更懂 Flexbox/Grid 解决了什么问题。

1. 浮动布局 (Float)

1.1 浮动基础

浮动最初是为图文环绕设计的,后来被广泛用于布局。元素设置 float 后会脱离文档流,向指定方向靠拢,直到碰到容器边缘或另一个浮动元素。

.float-left {
  float: left;
}
.float-right {
  float: right;
}
<div class="container">
  <div class="float-left" style="width:100px;height:100px;background:#e74c3c;">A</div>
  <div class="float-left" style="width:100px;height:100px;background:#3498db;">B</div>
  <p>这段文字会环绕在浮动元素旁边……</p>
</div>

效果图描述:两个色块 A(红)、B(蓝)并排靠左,文字在右侧环绕排列。

1.2 浮动的副作用——高度塌陷

当子元素全部浮动时,父容器无法感知子元素高度,导致”高度塌陷”:

.parent {
  border: 2px solid #333;
  /* 没有设置 height,子元素浮动后父元素高度为 0 */
}
.child {
  float: left;
  width: 100px;
  height: 100px;
}
<div class="parent">
  <div class="child" style="background:#e74c3c;">A</div>
  <div class="child" style="background:#3498db;">B</div>
</div>
<p>这段文字会紧贴在父容器下方,而非在子元素下方</p>

效果图描述:父容器边框塌缩为一条线(高度为0),两个色块溢出,后续文字紧贴边框线。

讲解: 浮动子元素不参与父元素的高度计算,父容器高度塌成 0,后续内容上移。解决办法见 1.3 的四种 clearfix 方案。

1.3 清除浮动(Clearfix)方案汇总

方案一:额外标签法(不推荐)

在浮动元素后添加一个空标签并设置 clear: both:

<div class="parent">
  <div class="child">A</div>
  <div class="child">B</div>
  <div style="clear:both;"></div>
</div>

缺点:增加无语义标签,违反结构与表现分离原则。

方案二:父元素设置 overflow: hidden(BFC 法)

.parent {
  overflow: hidden;
}

原理:触发 BFC,BFC 会包含浮动元素。但 overflow: hidden 会裁剪溢出内容,不适合需要溢出显示的场景。

方案三:伪元素清除法(推荐 [完成])

.clearfix::after {
  content: '';
  display: block;
  clear: both;
}
.clearfix {
  *zoom: 1;
}

*zoom: 1 是 IE6/7 的 hack,触发 hasLayout 以兼容老浏览器。

讲解: 伪元素法在容器末尾插入一个 clear: both 的隐藏块,是传统方案中的推荐写法;*zoom 仅为旧 IE 兼容,现代代码可删除。

方案四:现代 BFC 方案

.parent {
  display: flow-root;
}

display: flow-root 专门为创建 BFC 设计,无副作用。浏览器支持:Chrome 58+、Firefox 53+、Safari 13+。

讲解: display: flow-root 专门用于创建 BFC,无副作用、语义清晰,是清除浮动的现代标准写法。

1.4 clear 属性详解

.clear-left {
  clear: left;
}
.clear-right {
  clear: right;
}
.clear-both {
  clear: both;
}
  • clear: left:元素顶部不允许有左浮动元素
  • clear: right:元素顶部不允许有右浮动元素
  • clear: both:两侧都不允许

2. 定位系统 (Positioning)

CSS position 属性控制元素的定位方式,配合 top/right/bottom/left/z-index 使用。

2.1 static——默认定位

所有元素默认 position: static,遵循正常文档流。top/left 等偏移属性无效。

.box-static {
  position: static;
  top: 50px;
  left: 100px;
}

效果图描述:元素位置无任何变化,top/left 不生效。

2.2 relative——相对定位

相对于自身原位置偏移,不脱离文档流,原位置仍保留空间。

.box-relative {
  position: relative;
  top: 20px;
  left: 30px;
}
<div style="background:#ecf0f1;padding:20px;">
  <span>前</span>
  <span class="box-relative" style="background:#e74c3c;color:#fff;padding:5px;">相对定位</span>
  <span>后</span>
</div>

效果图描述:“相对定位”文字向右下偏移 30px/20px,但原位置仍留有空间,“前""后”文字位置不变。 常见用途:

  • 微调元素位置
  • 作为 absolute 子元素的定位参考

2.3 absolute——绝对定位

相对于最近的非 static 祖先偏移,脱离文档流,原位置不保留空间。

.parent-abs {
  position: relative;
  width: 300px;
  height: 200px;
  background: #ecf0f1;
}
.child-abs {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 80px;
  height: 80px;
  background: #e74c3c;
}
<div class="parent-abs">
  <div class="child-abs"></div>
</div>

效果图描述:灰色容器内,红色方块紧贴右上角(距顶20px、距右20px)。 关键要点:

  • 若无已定位祖先,则相对于初始包含块(通常是 <html>)
  • 绝对定位元素的 width: auto 会收缩到内容宽度(类似浮动)
  • 常用于弹窗、下拉菜单、角标等

2.4 fixed——固定定位

相对于浏览器视口定位,脱离文档流,滚动页面时位置不变。

.nav-fixed {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 50px;
  background: #2c3e50;
  color: #fff;
  z-index: 1000;
}
.back-to-top {
  position: fixed;
  bottom: 30px;
  right: 30px;
  width: 50px;
  height: 50px;
  background: #3498db;
  border-radius: 50%;
  cursor: pointer;
}

效果图描述:深色导航栏始终固定在页面顶部;蓝色圆形”回到顶部”按钮固定在右下角。 注意:fixed 元素的包含块是视口,但如果祖先设置了 transform/perspective/filter,包含块会变为该祖先(这是一个常见”坑”)。

2.5 sticky——粘性定位

在特定滚动阈值内表现为 relative,超出阈值后表现为 fixed。

.section-title {
  position: sticky;
  top: 0;
  background: #fff;
  padding: 10px;
  border-bottom: 2px solid #3498db;
  z-index: 10;
}
<div style="height:2000px;">
  <h2 class="section-title">第一章</h2>
  <p>内容区域……(很长)</p>
  <h2 class="section-title">第二章</h2>
  <p>内容区域……(很长)</p>
</div>

效果图描述:滚动时,章节标题到达页面顶部后”粘住”,直到被下一个标题推出。 关键要点:

  • sticky 元素不会脱离文档流
  • 必须指定 top/bottom/left/right 中的至少一个
  • 父容器的高度是 sticky 的”活动范围”,超出父容器后 sticky 失效
  • 父容器不能设置 overflow: hidden/auto/scroll,否则 sticky 失效

讲解: sticky 在滚动范围内保持原位,到达阈值(top: 0)后“粘住”不动,常用于吸顶导航与章节标题;它需要父容器有足够滚动空间才能生效。

2.6 z-index 与层叠上下文

z-index 仅对 position 非 static 的元素有效(sticky 除外,它天然创建层叠上下文)。

.layer-1 {
  position: absolute;
  z-index: 1;
  background: rgba(231, 76, 60, 0.7);
}
.layer-2 {
  position: absolute;
  z-index: 10;
  background: rgba(52, 152, 219, 0.7);
}
.layer-3 {
  position: absolute;
  z-index: 5;
  background: rgba(46, 204, 113, 0.7);
}

层叠上下文的创建条件(部分):

  • position 非 static + z-index 非 auto
  • opacity < 1
  • transform 非 none
  • filter 非 none
  • will-change: transform
  • display: flex/grid 子元素的 z-index 非 auto

    [警告] 常见坑:子元素的 z-index 再高,也无法超越父级层叠上下文的限制。


3. BFC(块格式化上下文)

3.1 什么是 BFC

BFC(Block Formatting Context)是 CSS 中一个独立的渲染区域,内部元素的布局不会影响外部元素。可以把它想象成一个隔离的布局容器。

3.2 BFC 的布局规则

  1. 内部块级盒子垂直方向一个接一个排列
  2. 同一个 BFC 中,相邻块级盒子的垂直外边距会发生折叠(margin collapse)
  3. BFC 区域不会与浮动元素重叠
  4. BFC 可以包含浮动元素(解决高度塌陷)
  5. 计算 BFC 高度时,浮动元素也参与计算

3.3 触发 BFC 的条件

属性值
floatleft / right(非 none)
positionabsolute / fixed
displayinline-block / table-cell / table-caption / flex / inline-flex / grid / inline-grid / flow-root
overflowhidden / auto / scroll(非 visible)
containlayout / content / paint

3.4 BFC 的典型应用

应用一:清除浮动(解决高度塌陷)

.container {
  display: flow-root;
}

应用二:防止 margin 折叠

<div style="margin-bottom:20px;background:#e74c3c;">A</div>
<div style="margin-top:30px;background:#3498db;">B</div>

效果图描述:A 和 B 之间间距为 30px(取较大值),而非 50px。 解决方案:将其中一个元素包裹在 BFC 容器中:

<div style="margin-bottom:20px;background:#e74c3c;">A</div>
<div style="overflow:hidden;">
  <div style="margin-top:30px;background:#3498db;">B</div>
</div>

效果图描述:A 和 B 之间间距变为 50px(20+30),margin 不再折叠。

应用三:实现自适应两栏布局

浮动元素不会与 BFC 区域重叠,利用这一点实现右侧自适应:

.left {
  float: left;
  width: 200px;
  height: 300px;
  background: #e74c3c;
}
.right {
  overflow: hidden;
  height: 300px;
  background: #3498db;
}
<div class="left">固定宽度侧栏</div>
<div class="right">自适应内容区</div>

效果图描述:左侧红色固定 200px,右侧蓝色自动填满剩余宽度,不会跑到红色下方。


4. 传统经典布局

4.1 圣杯布局(Holy Grail Layout)

三栏布局:中间自适应,两侧固定宽度。DOM 顺序中间栏优先渲染。

.holy-grail {
  padding: 0 200px 0 150px;
}
.holy-grail .center {
  float: left;
  width: 100%;
  background: #ecf0f1;
  min-height: 300px;
}
.holy-grail .left {
  float: left;
  width: 150px;
  margin-left: -100%;
  position: relative;
  left: -150px;
  background: #e74c3c;
  min-height: 300px;
}
.holy-grail .right {
  float: left;
  width: 200px;
  margin-left: -200px;
  position: relative;
  right: -200px;
  background: #3498db;
  min-height: 300px;
}
<div class="holy-grail clearfix">
  <div class="center">Center(主内容区,优先渲染)</div>
  <div class="left">Left(150px)</div>
  <div class="right">Right(200px)</div>
</div>

效果图描述:三栏并排——左侧红色 150px,中间灰色自适应,右侧蓝色 200px。中间栏在 DOM 中排在最前。 核心原理:

  1. 三栏均左浮动,中间栏 width: 100% 占满
  2. 左栏 margin-left: -100% 移到中间栏左侧
  3. 右栏 margin-left: -200px 移到中间栏右侧
  4. 父容器 padding 留出两侧空间,左右栏 position: relative 偏移到位

4.2 双飞翼布局

与圣杯布局目标相同,但实现方式不同:中间栏内部再包一层,用 margin 留空间而非父容器 padding。

.double-wing .center-wrap {
  float: left;
  width: 100%;
}
.double-wing .center {
  margin: 0 200px 0 150px;
  background: #ecf0f1;
  min-height: 300px;
}
.double-wing .left {
  float: left;
  width: 150px;
  margin-left: -100%;
  background: #e74c3c;
  min-height: 300px;
}
.double-wing .right {
  float: left;
  width: 200px;
  margin-left: -200px;
  background: #3498db;
  min-height: 300px;
}
<div class="double-wing clearfix">
  <div class="center-wrap">
    <div class="center">Center(主内容区)</div>
  </div>
  <div class="left">Left(150px)</div>
  <div class="right">Right(200px)</div>
</div>

效果图描述:视觉效果与圣杯布局一致,但中间栏通过内部 margin 而非父容器 padding 留空间。 圣杯 vs 双飞翼对比:

对比项圣杯布局双飞翼布局
留空间方式父容器 padding + 子元素 relative中间栏内部 margin
DOM 层级三栏同级中间栏多包一层
position左右栏需要 relative不需要 relative
中间栏最小宽度受 padding 约束受 margin 约束

4.3 等高布局

传统方式实现多列等高:

.equal-height {
  overflow: hidden;
}
.equal-height .col {
  float: left;
  width: 33.33%;
  padding-bottom: 9999px;
  margin-bottom: -9999px;
  background: #ecf0f1;
}
.equal-height .col:nth-child(2) {
  background: #bdc3c7;
}
.equal-height .col:nth-child(3) {
  background: #95a5a6;
}

效果图描述:三列高度一致,以内容最多的列为准。利用超大 padding-bottom + 负 margin-bottom 实现。


5. 居中方案汇总

5.1 水平居中

行内元素 / 行内块元素

.parent {
  text-align: center;
}
<div class="parent">
  <span>行内元素居中</span>
</div>

定宽块级元素

.child {
  width: 200px;
  margin: 0 auto;
}

不定宽块级元素

.parent {
  text-align: center;
}
.child {
  display: inline-block;
}

5.2 垂直居中

单行文本

.single-line {
  height: 50px;
  line-height: 50px;
}

多行文本(table-cell)

.parent {
  display: table-cell;
  vertical-align: middle;
  height: 200px;
}

绝对定位 + transform

.parent {
  position: relative;
  height: 200px;
}
.child {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

5.3 水平垂直居中

方案一:绝对定位 + transform(最经典)

.parent {
  position: relative;
  width: 400px;
  height: 300px;
  background: #ecf0f1;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: #e74c3c;
  padding: 20px;
  color: #fff;
}

效果图描述:红色方块精确居中在灰色容器正中央。 优点:无需知道子元素尺寸。缺点:transform 可能影响子元素的 fixed 定位。

方案二:绝对定位 + 负 margin(需已知尺寸)

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  height: 100px;
  margin-top: -50px;
  margin-left: -100px;
}

优点:兼容性极好。缺点:需要知道子元素宽高。

方案三:绝对定位 + margin: auto(需已知尺寸)

.child {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  margin: auto;
  width: 200px;
  height: 100px;
}

原理:绝对定位元素四边为 0 时,浏览器自动计算 margin 使其居中。

方案四:table-cell

.parent {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
  width: 400px;
  height: 300px;
}
.child {
  display: inline-block;
}

优点:兼容 IE8+。缺点:display: table-cell 对布局有限制。

方案五:Flexbox(现代推荐 [完成])

.parent {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 300px;
}

优点:最简洁,无需知道子元素尺寸。缺点:IE9 及以下不支持。

方案六:Grid(最现代)

.parent {
  display: grid;
  place-items: center;
  height: 300px;
}

优点:代码最少。缺点:IE 不支持。

5.4 居中方案对比总结

方案需知尺寸兼容性代码量适用场景
absolute + transform否IE10+少通用
absolute + 负 margin是IE6+中已知尺寸
absolute + margin:auto是IE8+少已知尺寸
table-cell否IE8+多兼容旧浏览器
Flexbox否IE10+少现代项目首选
Grid否现代浏览器最少最新项目

6. 总结

虽然现代开发推荐使用 Flex/Grid,但理解 Float 和 Position 对维护旧项目和处理特定定位需求(如固定导航栏、弹窗)依然至关重要。 关键要点回顾:

  • 浮动:图文环绕 → 布局 → 高度塌陷 → clearfix / flow-root
  • 定位:static(默认)→ relative(微调/参照)→ absolute(脱离流/弹窗)→ fixed(视口固定)→ sticky(滚动粘性)
  • BFC:隔离布局容器,解决塌陷/margin 折叠/自适应两栏
  • 经典布局:圣杯/双飞翼是浮动布局的巅峰应用
  • 居中:从传统 hack 到 Flex/Grid,方案越来越简洁

7. 动手试试

入门版(必做)

  1. 写两个浮动色块和一个段落,观察文字环绕;再给父容器加 display: flow-root,观察高度塌陷被修复;
  2. 用 position: absolute 做一个“右上角关闭按钮”,父元素记得加 position: relative;
  3. 用 position: sticky 做一个吸顶的章节标题,滚动页面观察效果。

进阶版(选做)

  1. 复刻一个简化版圣杯布局(左、中、右三栏),再用 Flexbox 重写对比;
  2. 用 absolute + transform 实现水平垂直居中;
  3. 用 BFC 实现“左边固定、右边自适应”的两栏布局。

8. 核心知识点

一句话记住传统布局:float 让元素靠边站(记得清浮动),position 决定定位参照系;relative 是基准,absolute 脱离流,sticky 会吸顶。

  • float:文字环绕、横向排列,副作用是高度塌陷;
  • 清浮动四法:额外标签、overflow: hidden、伪元素 clearfix、display: flow-root;
  • static/relative/absolute/fixed/sticky 五种定位,参照系各不相同;
  • absolute 相对最近的非 static 祖先,fixed 相对视口,sticky 相对滚动容器;
  • BFC 可清除浮动、防止外边距合并、实现自适应两栏;
  • 居中优先 Flexbox/Grid,旧项目才用 absolute/table-cell。

9. 注意事项与改进建议

问题点说明改进方案
浮动后忘记清父容器高度塌陷用 display: flow-root 或 clearfix
absolute 找不到基准相对整个页面跳动给父元素加 position: relative
父容器 overflow: hiddensticky 失效用 overflow: clip 或调整结构
用 float 做整体布局维护困难新项目用 Flexbox/Grid
负 margin 居中尺寸变化就失效用 transform 或 Flexbox
忽略 z-index 上下文层级不符合预期理解层叠上下文(见 css/230-StackingContext)

10. 扩展学习

  • 定位详解:css/220-PositionDetailed、css/230-StackingContext;
  • 浮动专题:css/200-FloatClear;
  • 现代布局:css/240-CSS3FlexboxFlexLayout、css/250-CSS3GridGridLayout;
  • 边距折叠:css/060-MarginCollapse;
  • 响应式:css/370-ResponsiveDesign。