本节摘要:在 Flexbox 和 Grid 出现之前的十几年里,
float是 CSS 布局的主力军——它本为图文环绕而生,却被硬拽去搭建整个页面的框架,代价是无数"高度塌陷"的深夜调试。今天它已退居二线,但图文环绕仍是它的独门绝技,清除浮动与 BFC 的概念也依然是读懂存量代码的钥匙。
完成本节后,你将能够:解释 float 的工作原理以及它为什么导致"父元素高度塌陷";动手复现并修复塌陷;使用三种以上方法清除浮动副作用并对比优劣;判断什么场景今天仍然应该用 float。
float 属性最初是为了实现"图文环绕"效果而设计的——让图片浮起来,文字自动围绕着排列。
.article-image { float: left; width: 200px; margin-right: 16px; margin-bottom: 8px; }
<article> <img class="article-image" src="photo.jpg" alt="配图"> <p>这是文章的第一段,文字会围绕着浮动的图片排列。当一行写不下时,文字会自动换到下一行继续围绕。</p> <p>第二段也是同样的效果。</p> </article>
当一个元素被设为 float 后,会发生三件事:它脱离正常的块级排版(但仍在文本流的计算范围内);它向左或向右移动,直到碰到父元素的边界或另一个浮动元素;其他块级盒子会无视它的存在照常排布,但其中的行内内容(文字)会绕开它排列。
这个"块级无视、文字绕行"的分裂行为是理解一切浮动现象的钥匙。块级盒子当它不存在,所以父元素算高度时也不把它算进去——塌陷由此而来;文字却认识它,所以能围绕它排版——环绕效果由此而来。
浮动元素贴左,文字先绕行、后归位
浮动元素不参与父元素的高度计算。如果父容器里只有浮动子元素,父元素的高度会塌缩为 0。
/* 问题代码 */ .nav { /* 没有设置高度,期望由子元素撑开 */ } .nav-item { float: left; width: 120px; height: 40px; }
<nav class="nav"> <div class="nav-item">首页</div> <div class="nav-item">产品</div> <div class="nav-item">关于</div> </nav> <p>这段文字会顶上来和导航栏叠在一起,因为 nav 的高度是 0。</p>
动手跑一遍这段代码:三个导航项明明看得见,nav 却像不存在一样,后面的段落直接叠了上来。给 nav 临时加个 background: #E3F2FD 更直观——背景完全消失,因为高度为零的盒子画不出背景。这就是经典的"高度塌陷"。
历史上的怪诞之处在于:整个 2000 年代,全世界的网站都在用这种方式做多栏布局,也就都在用各种偏方对付塌陷。你看懂了塌陷,就看懂了那个时代一半的 CSS 面试题。
在浮动元素后面添加一个空元素,设置 clear: both,强制它出现在所有浮动元素的下方。
.clearfix { clear: both; }
<div class="nav"> <div class="nav-item">首页</div> <div class="nav-item">产品</div> <div class="clearfix"></div> </div>
原理:clear: both 要求这个元素的顶部必须低于左右两侧的所有浮动元素——它被"压"到浮动项下面,父容器要包住它,高度自然就回来了。方法简单直觉,但需要往 HTML 里塞一个没有任何语义的空标签,结构和样式又搅和在了一起。
给父元素设置 overflow: auto 或 overflow: hidden,会触发块格式化上下文(BFC),使父元素能够包含浮动子元素。
.nav { overflow: auto; /* 触发 BFC,自动包含浮动 */ }
BFC(Block Formatting Context,块格式化上下文)可以理解为一块"独立核算"的渲染区域:外面的浮动进不来影响它的内部排版,里面浮动的孩子也必须被算进它的高度。第 3 章防外边距穿透用的也是它——一个 BFC,治两种疑难。
触发 BFC 的方式有好几种:overflow 不为 visible、display: flow-root(最语义化的写法)、float 不为 none、position 为 absolute 或 fixed 等。
/* 最语义化的 BFC 触发方式 */ .nav { display: flow-root; /* 专门为"包含浮动"设计的取值,无副作用 */ }
overflow 方法的副作用是内容真溢出时会被裁剪或出滚动条;flow-root 没有这些副作用,是今天写新代码的首选——可惜老浏览器不认它,这也是它没能统一天下的原因。
实际项目中最常用的方法,不需要添加额外的 HTML 元素——用第 2 章学的伪元素,把那个"压底的元素"从 HTML 挪进 CSS:
.clearfix::after { content: ""; display: table; clear: both; }
<nav class="nav clearfix"> <div class="nav-item">首页</div> <div class="nav-item">产品</div> <!-- 不需要额外的清除元素 --> </nav>
它在父容器的内容末尾插入一个不可见的伪元素并清除两侧浮动——方法一的"空标签"由 CSS 代办了。这段五行咒语曾出现在无数项目的公共样式里,今天你能在几乎所有存量代码库中找到它的身影,认得出它就是一种基本功。
| 清除方法 | 优点 | 缺点 |
|---|---|---|
| 空元素 + clear | 简单直觉 | 多余的 HTML 标签 |
| overflow: auto | 不需要额外标签 | 内容溢出时会出现滚动条 |
| display: flow-root | 语义最清晰、无副作用 | 极老的浏览器不支持 |
| 伪元素 ::after | 兼容性好,无多余标签 | 需要记住固定写法 |
放下历史包袱,做 float 今天真正的本职工作——给个人主页的一篇文章做杂志式图文环绕:
<article class="post"> <img class="post-thumb" src="note.jpg" alt="学习笔记配图"> <p>浮动的设计初衷就是图文环绕。图片浮到左侧后,这些文字会自动围绕它排列:每行的可用宽度被图片占去一部分,浏览器逐行重新计算换行位置。</p> <p>当文字超过图片底部,后续行恢复全宽。想强制某段文字从图片下方开始,就给那一段清除浮动。</p> <p class="post-end">这一段设置了 clear: both,永远从图片下方开始。</p> </article>
.post { max-width: 560px; } .post-thumb { float: left; width: 160px; margin: 0 16px 8px 0; border-radius: 6px; } .post-end { clear: both; padding-top: 8px; border-top: 1px dashed #ccc; } /* 父容器用 flow-root 包住浮动,防止塌陷波及页脚 */ .post { display: flow-root; }
刷新后逐项验证:前两段文字绕图排列,每行右边短一截,超过图片高度后恢复全宽;第三段因为 clear: both 从图片下方起步并带着虚线分隔;.post 自身因为 flow-root 高度完整,页脚没有被卷进来。然后做两个变体实验:把 float: left 改成 float: right,图片换边、文字从左侧环绕;删掉 display: flow-root 再看页脚是否被图片"穿透"——塌陷与修复,一个来回全体验了。
再补一个有趣的历史复刻:两栏布局的"祖传做法"。把两个 div 都设 float: left 加各自 width: 50%(记得父级清除浮动),就能得到两栏。看懂它,下次接手老项目时那些成对的 float: left 就不再是天书;写新代码时,请直接用后面两节的 Flex 或 Grid。
现代项目中,float 已经不再是布局的首选。页面框架用 Grid,组件内部排列用 Flexbox。但 float 仍然在两个场景中有用武之地:图文环绕效果(杂志风格的文章排版,本节的实验)和小范围的左右对齐。判断标准很简单:你要的是"内容围绕它流动"还是"各归其位的框架"——前者是浮动的独门绝技,后者交给现代布局。
💡 动手建议:写一个包含图片和多段文字的页面,给图片设置
float: left,观察文字如何围绕图片排列。然后尝试改变图片的宽度,看环绕效果的变化;再把图片换成float: right,体会两种环绕方向在阅读节奏上的差别。
⚠️ 不要用
float来做多栏布局。如果你看到某个项目还在用float: left加百分比宽度做栅格系统,那它大概率是一个 2015 年以前的老项目。现代布局请用 Flexbox 或 Grid——它们更简洁、更可预测、更易于维护,而且 Flex/Grid 容器里连外边距合并都不存在,行为比浮动时代"讲理"得多。
问:浮动元素之间会重叠吗? 不会。同向浮动依次排列,前一个的右边界就是后一个的左边界;空间不够时后到的自动换到下一"行"继续贴边。这也是老式栅格系统的原理——一排固定宽度的盒子向左浮动,装不下就换行,行为可预测但要小心"高度不齐导致卡位"的错位问题,那是浮动布局时代的另一桩公案。
问:clear 能只清除一边吗? 可以,clear: left 只避让左浮动、clear: right 只避让右浮动、both 两侧都避让。绝大多数场景用 both 最省心,单边清除偶尔在混排(左图右图交错)的文章版式里用得上。
问:文本绕行时贴得太紧怎么办? 给浮动元素加外边距——环绕文字与图片的距离就是由浮动元素的外边距提供的,margin: 0 16px 16px 0 这种"右侧加下侧"的组合是左浮图片的标准配置。反过来,若发现文字莫名离图片有一段距离,也先查浮动元素自带的默认外边距。
问:老项目里一堆 float 布局,值得重构成 Grid 吗? 分情况。纯粹"能跑就行"的展示页,不动它;需要频繁改版、加响应式的页面,重构的收益才大于风险——而且重构时 Grid 的区域命名几乎可以逐栏对照旧的浮动结构来"翻译",工作量可控。工程决策的准绳永远是改动频率与维护成本,而不是技术新旧。
浮动的兴衰是理解 CSS 布局史的最佳切片。它生来只为一件事:让文字围绕图片,杂志排版的网页化。但上世纪末的开发者很快发现"浮动加宽度"能拼出多栏布局——于是件工具被长期用在它的设计意图之外,塌陷、清除、双倍边距等一堆补丁文化随之滋长, clearfix 咒语、overflow 偏方、各类栅格框架都是那个时代的产物。Flex 与 Grid 到来后,布局回归了"专门工具做专门事",浮动退回图文环绕的本职——工具没有变坏,只是任务终于各归各位。
这段历史对学习者的启示是:看懂一段旧代码时,先辨认它属于哪个时代的"主流做法"。满屏 float 的栅格、包裹一层的 clearfix、给行内块消灭间隙的字号归零——这些都是特定年代的集体 workaround,不是某个作者的怪癖。带着时代视角读旧代码,你不会嘲笑前人,只会更清楚今天的便利从何而来,以及今天的主流做法里哪些也可能成为明天的"历史地层"。
另一个值得留下的沉淀是 BFC 这个概念。它因浮动清理而进入大众视野,但真正的价值是"独立渲染王国"这个模型——包浮动、止合并、隔重叠,一处概念三处应用。概念性的知识就是这样穿越技术更迭的:浮动的写法会过时,BFC 的直觉永远有用。
收束到可操作的判断流程:接到一个"要环绕"的需求,用 float 加配套的父容器 flow-root;接到"要分栏"的需求,直接写 Flex 或 Grid,哪怕旧教程教的是浮动——旧教程成文于旧时代,工具箱已经换代。而当你翻阅存量代码遇到浮动布局时,三步读懂它:先找浮动的容器与方向,再找它的清除方案(空标签、overflow 还是伪元素咒语),最后把结构在心里翻译成对应的 Grid 轨道——这套"翻译能力"才是本节留给你的长期资产。
古典时代落幕,接下来是现代布局的双子星。下一节先见 Flexbox——一行代码让导航栏、卡片行、居中盒这些老大难变成声明式的小事。