display: flow-root —— 你早该用上的 clearfix 替代方案
如果你写过几年 CSS,那你一定写过 clearfix。大概长这样:
.clearfix::after {
content: "";
display: table;
clear: both;
}
它能用,但它是个 hack —— 之所以存在,只是因为 CSS 一直没有一种干净的方式来表达「让这个元素包住它内部的浮动」。从 CSS Display Level 3 开始,有了正式的答案:display: flow-root。
问题所在:块级格式化上下文
块级格式化上下文(BFC,block formatting context)是一块独立的布局区域。内部的浮动子元素会被限制在其中,外边距不会跨越它的边界折叠,外部的浮动也不会侵入。
很多属性会作为副作用创建一个 BFC —— overflow: hidden、float: left、position: absolute、display: table-cell、display: inline-block、display: flex —— 但每一个都带着包袱:
overflow: hidden能建立 BFC,但它同时会裁剪内容并创建滚动容器。float: left会移动元素本身,几乎从来不是你想要的。display: table-cell会让元素收缩包裹,并破坏正常的块级行为。position: absolute会让元素完全脱离文档流。display: flex/grid能建立 BFC,但它们同时改变了子元素的布局方式 —— 如果你只是想要「包住浮动」,这就是一次真正的行为变更。
一直以来都没有办法表达「让这个元素保持普通块级盒,但同时拥有自己的 BFC」。这个缺口正是 flow-root 要填的。
flow-root 做了什么
.container {
display: flow-root;
}
元素仍然是一个块级盒,内容仍按正常流布局,但它始终会建立一个新的块级格式化上下文。不裁剪、不收缩、不改变定位、不改变子元素的布局方式。只是包住而已。
<div class="card">
<img class="thumb" src="cover.jpg" alt="" />
<p>应该排在浮动元素旁边的文字……</p>
</div>
.thumb {
float: left;
margin-right: 1rem;
}
.card {
/* 没有这一行,卡片会塌陷成零高度,
因为浮动图片已经脱离文档流。 */
display: flow-root;
}
名字的由来
理解 flow-root 最好的入口是 display 的双值语法 —— 这是 CSS Display Level 3 引入的。每个元素都有一个外部显示类型(它如何参与父级布局)和一个内部显示类型(它如何布局自己的子元素)。display: flow-root 只是下面这行的简写:
display: block flow-root;
你也可以显式写出完整形式:
block flow-root—— 块级盒 + 自己的 BFC。等同于flow-root。inline flow-root—— 等同于inline-block。flow-root list-item—— 一个建立 BFC 的列表项。block flow—— 等同于block。
这也是为什么 flow-root 给人一种「它本来就该存在」的感觉:它只是缺失的那个内部显示类型,而不是一种新的布局模式。
时间线与浏览器支持
flow-root 最初写在 CSS Display Module Level 3 规范中,该规范已进入 Candidate Recommendation 阶段。浏览器落地情况如下:
| 浏览器 | 首个支持版本 | 发布日期 |
|---|---|---|
| Chrome | 58 | 2017-04-19 |
| Firefox | 53 | 2017-04-19 |
| Opera | 45 | 2017-05-10 |
| Safari | 13 | 2019-09-19 |
| iOS Safari | 13 | 2019-09-19 |
| Edge(Chromium) | 79 | 2020-01-15 |
| Samsung Internet | 7.2 | 2017 |
| Internet Explorer | 从未支持 | — |
这张表里有几点值得注意:
- Chrome 和 Firefox 同一天发布。 2017-04-19,Chrome 58 和 Firefox 53 同时上线了这个特性。两个团队发布日历撞车是巧合,但也说明当时规范已经稳定了。
- Safari 是最长的那块短板。 直到 2019 年 9 月的 Safari 13 才支持,比 Chrome 和 Firefox 晚了两年多。如果你要兼容 Safari 12 及更早版本,
flow-root根本不可用,只能继续用老 clearfix。 - 旧版 Edge 从未支持。 Edge 是在 2020 年 1 月改用 Chromium 内核(Edge 79)之后才支持的。EdgeHTML(版本 12–18)从未支持。
- Internet Explorer 从未支持,而且永远不会。如果 IE 还在你的支持矩阵里,
flow-root直接出局。
display: flow-root 于 2022-07-15 达到 Baseline Widely Available,目前全球支持率超过 96%。对于任何现代浏览器目标,都可以放心使用,不需要降级方案。
实践建议
当你只想要一个 BFC、别的什么都不要时,用 flow-root。 这就是它的全部意义 —— 它是包住浮动、或阻止外边距穿过容器边界折叠的、最不意外的做法。
清理浮动时优先选它,而不是 overflow: hidden。 overflow: hidden 是最常见的替代品,但它会静默裁剪任何溢出的内容 —— 下拉菜单、tooltip、阴影、focus ring。flow-root 没有这个副作用。
只想「包住」时,优先选它而不是 display: flex。 为了包住浮动而把容器变成 flex 容器,会连带改变每一个子元素的布局。这比你想要的变化大得多。
如果你需要的只是防止外边距折叠,有更简单的选择。 父元素上加 padding 或 border 同样能阻止外边距穿过它折叠,而且不会引入格式化上下文。当你明确需要 BFC 时,再选 flow-root。
一个注意点:flow-root 会建立 BFC,而 BFC 会阻止外边距穿过该边界折叠。如果你原本依赖外边距穿过某个元素折叠,加上 flow-root 会改变间距。这通常正是你想要的效果,但值得提前知道,以免看到布局变化时措手不及。
小结
display: flow-root 让你在不引入任何副作用的前提下获得一个块级格式化上下文。它是 clearfix hack、以及 overflow: hidden 在清理浮动场景下的大多数用法的正确现代替代品。它在 2017 年 4 月随 Chrome 和 Firefox 上线,直到 2019 年的 Safari 13 才补齐,自 2022 年 7 月起达到 Baseline Widely Available。除非你还需要支持 IE 或旧版 Edge,否则已经没有任何理由再写 clearfix 了。
If you have written CSS for more than a few years, you have written a clearfix. Something like this:
.clearfix::after {
content: "";
display: table;
clear: both;
}
It works, but it is a hack that exists only because CSS had no clean way to say "make this element contain its floats." Since CSS Display Level 3, there is a proper answer: display: flow-root.
The problem: block formatting contexts
A block formatting context (BFC) is an independent layout region. Floated children are contained inside it, margins do not collapse across its boundary, and floats from outside do not intrude.
Plenty of properties create a BFC as a side effect — overflow: hidden, float: left, position: absolute, display: table-cell, display: inline-block, display: flex — but each one comes with baggage:
overflow: hiddenestablishes a BFC, but it also clips content and creates a scroll container.float: leftmoves the element, which is rarely what you want.display: table-cellmakes the element shrink-to-fit and breaks normal block behavior.position: absolutetakes the element out of flow entirely.display: flex/gridestablish a BFC, but they also change how children are laid out — a real behavior change if all you wanted was containment.
There was no way to say "keep this element a normal block box, but give it its own BFC." That gap is exactly what flow-root fills.
What flow-root does
.container {
display: flow-root;
}
The element stays a block-level box and lays out its contents with normal flow, but it always establishes a new block formatting context. No clipping, no shrinking, no positioning changes, no changes to how children are laid out. Just containment.
<div class="card">
<img class="thumb" src="cover.jpg" alt="" />
<p>Text that should sit beside the float...</p>
</div>
.thumb {
float: left;
margin-right: 1rem;
}
.card {
/* Without this, the card collapses to zero height
because the floated image is out of flow. */
display: flow-root;
}
Where the name comes from
flow-root is best understood through the two-value syntax of display, which CSS Display Level 3 introduced. Every element has an outer display type (how it participates in its parent's layout) and an inner display type (how it lays out its children). display: flow-root is shorthand for:
display: block flow-root;
You can write the long form explicitly:
block flow-root— block-level box with its own BFC. Same asflow-root.inline flow-root— same asinline-block.flow-root list-item— a list item that establishes a BFC.block flow— same asblock.
This is why flow-root feels like it "should" have existed from the start: it is just the missing inner display value, not a new layout mode.
Timeline and browser support
flow-root was specified in CSS Display Module Level 3, which reached Candidate Recommendation, and shipped in browsers like this:
| Browser | First version | Release date |
|---|---|---|
| Chrome | 58 | 2017-04-19 |
| Firefox | 53 | 2017-04-19 |
| Opera | 45 | 2017-05-10 |
| Safari | 13 | 2019-09-19 |
| iOS Safari | 13 | 2019-09-19 |
| Edge (Chromium) | 79 | 2020-01-15 |
| Samsung Internet | 7.2 | 2017 |
| Internet Explorer | never | — |
A few things worth noting from that table:
- Chrome and Firefox shipped it on the same day — 2017-04-19. Chrome 58 and Firefox 53 both landed the feature simultaneously, which is a neat coincidence of release calendars and a sign the spec was settled by then.
- Safari was the long pole. It took until Safari 13 in September 2019, more than two years after Chrome and Firefox. If you were shipping to Safari 12 or earlier,
flow-rootwas simply unavailable and you needed the old clearfix. - Legacy Edge never got it. Edge only gained support with the Chromium rewrite in Edge 79 (January 2020). EdgeHTML (versions 12–18) never supported it.
- Internet Explorer never supported it, and never will. If IE is in your support matrix,
flow-rootis off the table.
display: flow-root reached Baseline Widely Available on 2022-07-15, and global support is now above 96%. For any modern browser target, it is safe to use without a fallback.
Practical guidance
Use flow-root when you want a BFC and nothing else. That is the whole point — it is the least surprising way to contain floats or stop margin collapse through a container boundary.
Reach for it over overflow: hidden for clearing floats. overflow: hidden is the most common substitute, but it silently clips any content that overflows the box — dropdowns, tooltips, shadows, focus rings. flow-root has no such side effect.
Reach for it over display: flex when you only want containment. Making a container a flex container to contain its floats also changes every child's layout. That is a much bigger change than you asked for.
If all you need is margin containment, simpler options exist. A padding or border on the parent also stops margin collapse through it, without introducing a formatting context. Reach for flow-root when you specifically want the BFC.
One caveat: flow-root establishes a BFC, and a BFC prevents margin collapsing through that boundary. If you were relying on margins collapsing through an element, adding flow-root will change the spacing. That is usually the desired behavior, but it is worth knowing when to expect a layout shift.
Summary
display: flow-root gives you a block formatting context with no side effects. It is the correct modern replacement for the clearfix hack and for most uses of overflow: hidden when clearing floats. It shipped in Chrome and Firefox in April 2017, took until Safari 13 in 2019, and has been Baseline Widely Available since July 2022. Unless you are supporting IE or legacy Edge, there is no reason left to write a clearfix.