搞懂 BFC:一篇文章彻底拿下两列与三列自适应布局

0 阅读5分钟

前言

在前端开发中,我们经常会遇到这样的场景:左侧固定宽度做导航,右侧自适应填满剩余空间做内容区;或者左右两侧固定,中间自适应的经典三栏布局。

很多人会直接使用 Flexbox 或 Grid 来解决,这当然没问题。但如果面试官追问:“如果不使用 Flex 和 Grid,仅用传统 CSS,你如何实现?背后的原理是什么?

这时候,就需要请出 CSS 布局中的“定海神针”—— BFC(Block Formatting Context,块级格式化上下文)  了。

一、 到底什么是 BFC?

image.png 官方定义很绕,用人话来说:BFC 就像是一个封闭的、独立的“结界”。
在这个结界里面的元素怎么折腾(比如浮动、塌陷),都不会影响到外面的元素;同理,外面的元素也进不来干扰里面。

它有三个维度的核心规则:怎么创建它、它内部怎么排版、它跟外部怎么相处。

二、 怎么召唤出 BFC?(创建规则)

记住一个前提:普通的块级元素(比如最常见的 div)默认不是 BFC,必须通过特定条件触发。

以下是触发 BFC 的常见方式:

  1. 根元素<html> 标签本身就是最大的 BFC。
  2. 浮动元素:给元素加上 float: left / right(只要不是 none)。
  3. 绝对/固定定位position: absolute / fixed
  4. 行内块元素display: inline-block
  5. 表格相关display: table-cell 等(现在极少用来做布局)。
  6. 🔥 溢出非可见(最常用)overflow: hidden / auto / scroll。(注意:默认的 overflow: visible 不会触发)。
  7. 现代布局display: flex / grid / inline-flex / inline-grid(这会使其成为 Flex/Grid 容器,内部自然形成独立的 BFC 机制)。

三、 BFC 内部布局规则

当一个元素变成了 BFC,它内部就会遵守以下规矩:

  • 垂直排列:BFC 内的块级盒子只能从上到下垂直排布,绝对不可能横向并排。

  • Margin 折叠现象:在同一个 BFC 中,相邻的两个块级元素的垂直外边距会发生“折叠”。比如上面的 margin-bottom: 10px,下面的 margin-top: 20px,两者之间的真实距离是取最大值 20px,而不是相加的 30px

    • 破解之法:把这两个元素放在两个不同的 BFC 结界里,就不会折叠了。
  • 触边对齐(⚠️易错点) :BFC 内部的每个盒子的左外边缘,都会死死贴住这个 BFC 容器的左边缘。

    • 注意:这里说的是“盒子的边缘”贴住容器,但盒子里的“文字内容”是活体,看到浮动元素会主动避让,形成“文字环绕浮动元素”的效果。这也是为什么普通 div 会被浮动覆盖,而文字不会。

四、 BFC 的“对外防御机制”(外部交互规则)

这是 BFC 最核心的价值所在,它拥有两大“超能力”:

  1. 包含内部浮动(解决高度塌陷) :如果 BFC 里面有子元素浮动了,按理说父元素会失去高度(高度塌陷)。但只要父元素触发了 BFC,它就会强行计算并把所有浮动子元素包裹在内
  2. 排除外部浮动(防重叠) :如果一个 BFC 旁边有一个浮动的元素,这个 BFC 绝对不会去跟那个浮动元素重叠,它会自动收缩自己的宽度,给浮动元素让出位置。

正是这第二个超能力,造就了经典的浮动布局!

五、 实战演练:用 BFC 搞定两列与三列布局

1. 经典两列式布局(左固定,右自适应)

需求:左侧导航栏宽 200px,右侧内容区撑满剩余宽度。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BFC实现两列布局</title>
</head>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    .left {
        width: 200px;
        height: 500px;
        float: left; /* 1. 左侧脱标浮动 */
        background-color: #ccc;
    }
    .right {
        height: 500px;
        overflow: hidden; /* 2. 右侧触发 BFC */
        background-color: #333;
        color: #fff;
        /* 加点内边距让文字不贴边,更好看 */
        padding: 20px; 
        /* 因为加了padding,需要用box-sizing保证不撑大盒子 */
        box-sizing: border-box; 
    }
</style>
<body>
    <!-- 注意:HTML的class属性里不需要加 . 号 -->
    <div class="left">左侧固定区域 (200px)</div>
    
    <!-- 这里使用上面定义好的 .right 类名 -->
    <div class="right">右侧自适应区域 (无论浏览器多宽,我都会填满剩下的空间,且不会覆盖左侧)</div>
</body>
</html>

image.png

原理解析:
左侧 float 起来了,右侧本是一个普通的块级 div,按理说它会无视浮动,占满整行跑到左侧下面去。但我们给右侧加了 overflow: hidden 触发了 BFC。根据 BFC  【排除外部浮动】  的规则,右侧这个“结界”拒绝与左侧的浮动区域重叠,于是它老老实实退缩到了浮动元素的右边,剩余多少宽度它就占多少,完美实现自适应!

2. 经典三列式布局(左右固定,中间自适应)

需求:左侧 200px,右侧 200px,中间自适应。

HTML 结构:

代码生成完成

HTML代码

CSS 代码:

.left {
  width: 200px; height: 500px;
  float: left;
  background: #ccc;
}
.right {
  width: 200px; height: 500px;
  float: right;
  background: #aaa;
}
.center {
  height: 500px;
  overflow: hidden; /* 触发 BFC */
  background: #333;
  color: #fff;
}

image.png 原理解析:
左右两侧分别向左向右浮动,脱离了文档流。中间的 div 加上 overflow: hidden 触发 BFC 后,它既要躲避左边的浮动,又要躲避右边的浮动。两头都被挤压,最终只能乖乖夹在中间,剩下的空间全归它所有。

总结

在 Flexbox 和 Grid 横行的今天,我们写业务可能很少再手写 float + BFC 布局了。但是,理解 BFC 绝不是为了写古董代码

当你在页面中遇到莫名的“高度塌陷”、遇到“Margin 合并不生效”、遇到“浮动元素覆盖了后续内容”时,如果你脑子里能瞬间闪过 BFC 的那几条规则,你能在一分钟内定位并解决问题。

BFC 不是过时的技术,而是透视 CSS 底层渲染逻辑的一把钥匙。  掌握了它,你的 CSS 功力才算真正跨入了进阶的大门。