CSS 浮动布局底层原理:文档流、BFC 与清除浮动的秘密

0 阅读4分钟

一、文档流

浏览器在渲染页面时,遵照从上往下、从左往右的顺序依次排列元素,这种页面排版方式就是文档流

当元素设置 float 后,它会脱离文档流,就像"浮"在了页面上方。

二、浮动布局

2.1 文字环绕效果

浮动元素会脱离文档流,但一定不会覆盖文字。这是浮动最重要的特性 —— 文字会自动环绕在浮动元素周围。

<style>
        img{
            float: left;<!-- 浮在左边 -->
        }

    </style>
</head>
<body>
    <div class="page"> <!-- img默认是行内元素,但可以改变大小 -->
        <img width="200" src="https://img95.699pic.com/photo/50070/5999.jpg_wh860.jpg" alt="示例图片">
        <p>文本</p>
    </div>
</body>

效果示意:

image.png

  • 如图所示,文字环绕在图片周围,并且没有覆盖掉图片,即使图片已经脱离文档流

2.2 父容器高度塌陷

浮动元素脱离文档流后,父容器无法感知其高度,导致高度塌陷

<style>  
        .item {
            width: 200px;
            height: 100px;
            float: left;
        }
        /* 选中class为item的元素中的第一个子元素 */
        .item:nth-child(1){
            background-color: #db5a5a;
        }
        /* 选中class为item的元素中的二个子元素 */
        .item:nth-child(2){
            background-color: #d1c93e;
        }
        .item:nth-child(3){
            background-color: #159a5a;
        }
    </style>

<ul>          <!-- 高度为 0 ,子元素都浮动了 -->
  <li class="item">1</li>
  <li class="item">2</li>
  <li class="item">3</li>
</ul>
<h2>hello world</h2>  <!-- 这个元素会跑到浮动元素右边 -->

效果示意:

image.png

  • 检查发现,ul标签高度为0,因子元素全部脱离文档流,相当于没有子元素能够“撑开”ul的高度了
  • 此时,p标签也默认占据一整行,ul标签又没有高度,故p标签会在页面顶部
  • 由于浮动元素不会覆盖文字 ,故文字hello word会出现在右边

补充:行内块级元素间隙问题

使用 display: inline-block 时,HTML 代码中的换行会被渲染为空白间隙

  <style>
.item {
            width: 200px;
            height: 100px;
            display: inline-block;
        }
        .item:nth-child(1){
            background-color: #db5a5a;
        }
        .item:nth-child(2){
            background-color: #d1c93e;
        }
        .item:nth-child(3){
            background-color: #159a5a;
        }
    </style>

<ul>
  <li class="item">1</li>
  <li class="item">2</li>
  <li class="item">3</li>
  <!-- 换行 → 间隙 -->
</ul>

效果示意: image.png

解决方案:父元素设置 font-size: 0

ul {
  font-size: 0;   /* 消除空白间隙 */
}

.item {
  display: inline-block;
  font-size: 16px;  /* 子元素重新设置字体大小 */
}
  • font-size可以改变标签内文字/字符的大小,值设为0会令文字/字符消失,即换行符消失了
  • 由于ul内的文字全部消失了,子容器li中的文字也会消失,此时我们需要font-size: 16px;给子元素重新设置字体大小

三、清除浮动布局负面影响的五种方法

方法一:给父容器设置固定高度(不推荐)

ul {
  height: 100px;  /* 写死高度,不够灵活 */
}

方法二:末尾添加空元素 + clear(不推荐)

通过clear:left;清除左浮动, clear:right;清除右浮动, clear:both;左右浮动全部清除。

<style>
  .clear {
    clear: both;  /* 清除左右浮动的影响 */
  }
</style>

<ul>
  <li class="item">1</li>
  <li class="item">2</li>
  <li class="item">3</li>
  <div class="clear"></div>  <!-- 额外的空div -->
</ul>

缺点:添加了无意义的空元素,污染 HTML 结构。

方法三:设置 after 伪元素清除(推荐)

将伪元素的content属性设为空,这样就不会出现字符影响页面。伪元素默认是行内元素,不能设置宽高,需要先display: block;转换为块级元素

ul::after {           /* 在ul中设置一个after伪元素 */
  content: '';        /* 必须有 content 属性 */
  clear: both;
  display: block;
}

方法四:被影响元素设置 clear(不推荐)

h2 {
  clear: both;  /* 被浮动影响的元素自己清除 */
}

缺点:治标不治本,只能解决单个元素的问题,倘若后续需要在h2前添加元素,仍然会出现问题

方法五:将父容器设置为 BFC 容器(推荐)

通过一些语句,例如overflow: auto;可以将ul设置为BFC容器

ul {
  overflow: auto;  /* 触发 BFC */
}

四、BFC 详解

4.1 什么是 BFC?

BFC (Block Formatting Context)块级格式化上下文,是一个独立的渲染区域,它有一套特殊的渲染规则,使得容器内部的布局不会影响外部元素。

4.2 BFC 渲染规则

  1. BFC容器内部的子元素也是从上往下,从左往右排列
  2. BFC容器是一个独立的拥有特殊渲染规则的容器,它内部的子元素不会影响外部
  3. BFC容器在计算高度的时候会将浮动的子元素的高度也计算在内

4.3 如何创建 BFC 容器?

方式CSS 属性
overflowhidden / auto / scroll / overlay
positionabsolute / fixed
floatleft / right
displayflex / grid / inline-xxxx

4.4 BFC 解决父子 margin 重叠

HTML 中父容器的 margin-top 和子元素的 margin-top重叠(取较大值),这是一个经典 bug:

 <style>
        .parent{
            height: 400px;
            background-color: #4fcf32;
            margin-top: 100px;
        }

        .child{
            height: 200px;
            background-color: #d221ab;
            margin-top: 50px;
        }
    </style>
    
    
    <div class="parent">
        <div class="child"></div>
    </div>

效果示意: image.png

子容器(紫色容器)的margin-top应该是距离父容器(绿色容器)顶部50px,我们发现他与父容器的margin-top: 100px;重叠了

解决方案:将父容器变为 BFC

.parent{
            height: 400px;
            background-color: #4fcf32;
            margin-top: 100px;
            overflow: hidden; /* 触发 BFC,阻止 margin 穿透 */
        }

效果示意:

image.png 子容器成功距离父容器顶部50px