深入解读BFC,防止高度坍塌4种方案,CSS面试高频问题

1,331 阅读7分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

问题重现

  • 父元素的高度,都是由内部浮动子元素的高度撑起的。
  • 如果子元素浮动起来,就不占用普通文档流的位置。父元素高度就会失去支撑,也称为高度坍塌。
  • 即使有部分元素留在普通文档瀑布流布局中支撑着父元素,如果浮动起来的元素高度高于留下的元素。那么浮动元素的高度会超出父元素边框,用户体验同样不好!

不好的解决

  • 给父元素设置固定的高度
  • 缺点:多数情况下,父元素高度由内容撑起,很难提前固定父元素的高度。

解决:防止高度坍塌的4种方案

  • 方案一:为父元素设置overflow:hidden属性。

    • 原理:CSS中的overflow:hidden属性会强制要求父元素必须包裹住所有内部浮动的元素,以及所有元素的margin范围。
    • 缺点:如果刚好父元素有些超范围的子元素内容需要显示(比如,个别position定位的子菜单项),不想隐藏,就会发生冲突。

  • 方案二:在父元素内的结尾追加一个空子元素(块级元素),并设置空子元素清除浮动影响(clear:both)。

    • 原理:利用clear:both属性和父元素必须包裹含非浮动的元素两个原理。
    • 缺点:无端多出一个无意义的看不见的空元素,影响选择器和查找元素。

  • 方案三:设置父元素也浮动。

    • 原理:浮动属性也会强制父元素扩大到包含所有浮动的内部元素。
    • 缺点:会产生新的浮动影响。比如,父元素浮动,导致父元素之后平级的页脚div上移,被父元素挡住。
    • 解决:设置父元素之后的平级元素清除浮动(clear:both)。

  • 完美解决:为父元素末尾伪元素设置clear:both)。

    • 优点:既不会影响显示隐藏,又不会影响查找元素,又不会产生新的浮动问题。

总结: 防止高度坍塌,4种方式

为什么overflow和float会强制父元素包裹浮动的子元素?

什么是BFC

  • BFC(Block formatting context)
  • 直译为”块级格式化上下文“
  • 他是网页中一个独立的渲染区域(也称为formatting context)
  • 这个渲染区域只有块级(Block)元素才能参与
  • 它规定了内部的块级元素如何布局。
  • BFC渲染区域内部如何布局,与区域外部毫不相干。
  • 外部元素也不会影响BFC渲染区域内的元素。

简单总结

  • BFC就是页面上的一个隔离的独立渲染区域
  • 区域里面的子元素不会影响到外面的元素
  • 外面的元素也不会影响到区域里面的子元素

2种渲染区域(Formatting Context)

BFC的布局规则

  • 默认,内部的块级元素会在垂直方向,一个接一个地放置。每个块元素独占一行。

  • 块元素垂直方向的总距离由边框内大小+margin共同决定。

  • 属于同一个BFC的两个相邻块元素在垂直方向上的margin会发生重叠/合并。但水平方向的margin不会。

  • 左侧BFC渲染区域的margin,必须与右侧BFC渲染区域的margin相衔接,不能出现重叠。

  • 计算父元素BFC渲染区域的高度时,内部浮动元素的高度,都必须算在内。

4种情况会形成BFC渲染区域

  • float的值不是none
  • position的值不是static或者relative
  • display的值不是inline-bloce、table-cell、flex、table-caption或者inline-flex
  • overflow的值不是visible

所以,形成BFC区域可以解决高度坍塌!

  • 方案一:为父元素设置overflow:hidden属性。

    • 原理:因为形成BFC区域,所以必须父元素必须包含内部float浮动元素
    • 其实这里改成display:table,也可以。因为display:table也可以形成BFC区域。只不过,需要预防其他可能造成的新问题。

  • 方案三:设置父元素也浮动。

    • 原理:因为父元素float,也形成了BFC区域,必须包含内部float浮动的子元素。

BFC还可以解决更多问题...

1.避免垂直方向margin合并

问题重现

  • 垂直方向上,两个元素上下margin相遇时,两元素之间的总监局并不等于两个margin的和,而是等于最大的margin
  • 小的margin会被大的margin吞并

解决

  • 第一步: 用一个外围块元素包裹下方元素

  • 第二步:设置新外层元素overflow:hidden

原理

  • 新外层元素,变成一个BFC方式的渲染区域,就必须包裹内部子元素及子元素的margin。
  • 而且,内部元素不能超出范围影响外部,外部元素也不能进入BFC范围内,影响内部。

总结

  • 缺点: 为如果父元素中部分自由定位的子元素,希望即使超出父元素范围,也能显示时,就冲突了。
  • 优化: 第二步: 父元素::before{ content:””; display:table}
  • 原理: display:table,在子元素之前形成平级的bfc渲染区域。不允许子元素的margin进入::before范围内。
  • 优点: 既不隐藏内容,又不添加新元素,又不影响高度。

2.避免垂直方向margin溢出

问题重现:

  • 问题:子元素设置margin-top,会超出父元素上边的范围,变成父元素的margin-top。
  • 而实际上,子元素与父元素之间,依然是没有margin-top的。
  • 效果不是想要的。

5种解决方案

  1. 设置父元素overflow:hidden
    • 原理: 父元素变成BFC渲染区域,就必须包裹内层子元素的margin。
    • 缺点: 万一有的子元素,即使溢出父元素,也希望显示呢?就会发生冲突。
  2. 为父元素添加上边框,颜色设置为透明(transparent)
    • 原理: 这里不是bfc。而是因为边框本身可以阻隔margin溢出。
    • 缺点: 边框会增大父元素的实际大小,导致布局错乱。
  3. 用父元素的padding-top代替第一个子元素的margin-top
    • 原理: 这里也不是bfc。而是因为padding本身可以阻隔margin溢出。、
    • 缺点: 对父元素高度有影响。
    • 解决: 可以设置父元素box-sizing:border-box。
  4. 在父元素内第一个子元素之前添加一个空的
    • 原理: table的display属性默认相当于table,所以形成小的bfc渲染区域。其他元素的margin不能进入table范围内。就阻隔了margin向上溢出。
    • 优点: 空table元素没有大小,不占用父元素控件。
    • 缺点: 增加一个看不见的空元素,干扰查找元素。
  5. 最好的解决: 父元素::before{ content:""; display:table; }
    • 优点:既不隐藏内容,又不添加新元素,又不影响高度。

左定宽,右自适应布局

希望效果

错误解决

  • 第一步: 左边定宽元素左浮动: .left{ float:left; width:固定宽 }
  • 第二步: 右边元素右浮动:.right{ float:right; ...}
  • 问题: 右边元素虽然在右边了,但是宽度无法自适应。

正确解决

  • 第一步: 左边定宽元素左浮动: .left{ float:left; width:固定宽 }
  • 第二步: 右边元素不用右浮动,而是.right{overflow:hidden; ... }
  • 原理: 右边元素overflow:hidden后,形成BFC渲染区域。左边的float元素就不能进入右边范围了。

总结:

解决垂直方向margin合并

  • Step1: 添加父元素包裹下方元素
  • Step2:
    • 父元素overflow:hidden
    • 父元素下第一个子元素前添加空
    • 父元素padding代替子元素margin
    • 父元素+透明上边框
    • 父元素::before{ content:””; display:table }
    • 解决垂直方向margin溢出

      • 父元素overflow:hidden
      • 父元素下第一个子元素前添加空
    • 父元素padding代替子元素margin
    • 父元素+透明上边框
    • 父元素::before{ content:””; display:table }
    • 左定宽,右自适应布局

      • Step1: 左: float:left
      • Step2: 右: 右overflow:hidden