简单说一下 [清除 & 闭合] 浮动

4,149 阅读7分钟

本文的起因是团队现在处于缺人的阶段,最近开始帮忙进行电话面试的操作。面了不少了,有不少收获,其中最大的还是对自己的警惕作用。虽然是自己在面试别人,但也发现,很多在交流过程中涉及到的内容,自己虽然都知道或者有了解,但是要系统或者清晰的描述出来其实是不容易的,所以开始整理一些看似都懂的一些“乱七八糟”的知识。

【本文主要是根据参考资料整理以及一些自己的理解,参考资料已在文末列出。】

浮动是一个CSS布局中的经典属性,用过浮动的小伙伴肯定会遇到需要清除浮动或者说闭合浮动的情况。我对清除还是闭合的理解是,看你的实现方法来说。

简单来说,使用clear属性来实现可以认为是清除浮动(clearing float),使元素生成BFC或者hasLayout属性为true的方法可以称之为闭合浮动(enclosing float)。

CSS中的定位机制可以分为:

  • 普通流(俗称文档流,但是标准里面为normal flow,所以称之为普通流比较合理)。
  • 浮动:引用一丝姐姐的文章中的话如下,需要注意的点是浮动框会一直浮动到包含框或者另一个浮动框为止,并且浮动的元素是脱离普通流的,脱离了之后只会影响普通流中的内联元素的布局,对于块级元素没有影响,就像这个浮动元素不存在一样。同时,有可能会出现“高度塌陷”的情况(这也是清除&闭合浮动需求的主要场景)。

浮动的框可以左右移动,直至它的外边缘遇到包含框或者另一个浮动框的边缘。浮动框不属于文档中的普通流,当一个元素浮动之后,不会影响到块级框的布局而只会影响内联框(通常是文本)的排列,文档中的普通流就会表现得和浮动框不存在一样,当浮动框高度超出包含框的时候,也就会出现包含框不会自动伸高来闭合浮动元素(“高度塌陷”现象)。

  • 绝对定位:不是本文的重点,就不多说了。简单来说,绝对定位的元素是脱离普通流的,相对于最近的display属性为非static的父级元素定位。

闭合浮动

闭合浮动的本质就是让元素产生BFC或者hasLayout属性为true。

BFC的创建方法(参考自Block formatting context MDN),加粗的为常见或者需要注意的方法:

  • the root element or something that contains it
  • floats (elements where float is not none)
  • absolutely positioned elements (elements where position is absolute or fixed)
  • inline-blocks (elements with display: inline-block)
  • table cells (elements with display: table-cell, which is the default for HTML table cells)
  • table captions (elements with display: table-caption, which is the default for HTML table captions)
  • anonymous table cells implicitly created by the elements with display: table, table-row, table-row-group, table-header-group, table-footer-group (which is the default for HTML tables, table rows, table bodies, table headers and table footers, respectively), or inline-table
  • block elements where overflow has a value other than visible
  • display: flow-root
  • elements with contain: layout, content, or strict
  • flex items (direct children of the element with display: flex or inline-flex)
  • grid items (direct children of the element with display: grid or inline-grid)
  • multicol containers (elements where column-count or column-width is not auto, including elements with column-count: 1)
  • column-span: all should always create a new formatting context, even when the column-span: all element isn't contained by a multicol container (Spec change, Chrome bug).

对于hasLayout属性,一丝的文章中:

IE6-7的显示引擎使用的是一个称为布局(layout)的内部概念,由于这个显示引擎自身存在很多的缺陷,直接导致了IE6-7的很多显示bug。当我们说一个元素“得到 layout”,或者说一个元素“拥有 layout” 的时候,我们的意思是指它的微软专有属性 hasLayout http://msdn.microsoft.com/worksh ... rties/haslayout.asp 为此被设为了 true 。IE6-7使用布局的概念来控制元素的尺寸和定位,那些拥有布局(have layout)的元素负责本身及其子元素的尺寸设置和定位。如果一个元素的 hasLayout 为false,那么它的尺寸和位置由最近拥有布局的祖先元素控制。

简单来说,就是旧版本IE特有的一个控制布局的概念,对于我们来说要考虑的是在处理IE的兼容性的时候需要留意这个属性,从表现上来说,可以将hasLayout等同于BFC来考虑。

触发hasLayout的条件:

  • position: absolute
  • float: left|right
  • display: inline-block
  • width: 除 “auto” 外的任意值
  • height: 除 “auto” 外的任意值 (例如很多人闭合浮动会用到 height: 1% )
  • zoom: 除 “normal” 外的任意值 (MSDN) http://msdn.microsoft.com/worksh ... properties/zoom.asp
  • writing-mode: tb-rl (MSDN) http://msdn.microsoft.com/worksh ... ies/writingmode.asp

在 IE7 中,overflow 也变成了一个 layout 触发器:

  • overflow: hidden|scroll|auto ( 这个属性在IE之前版本中没有触发 layout 的功能。 )
  • overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的属性,尚未得到浏览器的广泛支持。他们在之前IE版本中同样没有触发 layout 的功能)

这么多的触发条件,其实需要记住的就是:最无害和被大家用来处理旧版本IE闭合浮动兼容性的是zoom

BFC

对于为什么触发了BFC可以实现闭合浮动的效果,从BFC的定义来看:

A block formatting context contains everything inside of the element creating it that is not also inside a descendant element that creates a new block formatting context.

简单来说,BFC的一个最重要的效果是,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响,这是利用BFC清除浮动所利用的特性。

此外BFC的特性主要的可以记住以下几点:

  • 内部的盒会在垂直方向一个接一个排列,也就是BFC内部是一个普通流;
  • 处于同一个BFC中的元素相互影响,可能会发生margin collapse;【防止上下margin collapse问题
  • 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此; BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然;
  • 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算;【解决“高度塌陷问题”
  • 浮动盒区域不叠加到BFC上;【文本清除浮动,防止文字环绕效果

清除浮动

清除浮动用的就是clear属性配合both属性值了。可以用额外的元素放到包含块的尾部,或者使用伪元素设置clear both来实现。个人推荐比较好的方法是使用伪类,然后配合IE的hasLayout兼容的方法,在一丝姐姐的文章中的那个最佳解:

.clearfix:after {
    content:".";
    display:block;
    height:0;
    visibility:hidden;
    clear:both;
}
.clearfix {
    *zoom:1;
}

另外在一丝的文章的最后提到了Nicolas Gallagher的文章中的方法:

.clearfix:before,.clearfix:after {
    content:"";
    display:table;
}
.clearfix:after {
    clear:both;
}/* For IE 6/7 (trigger hasLayout) */
.clearfix {
    zoom:1;
}

这里的重点是这个before的伪元素的作用,效果是处理内部元素的margin和上面的外部的元素的margin发生叠加的。我的理解是清除浮动的作用只是在元素的最后通过clear: both的方法实现了清除浮动从而消除了高度塌陷这些问题。但是本质上并没有形成一个BFC,也就是不会有BFC的其他的一些作用,比如防止上下margin collapse的问题。这里通过设置一个before的的display的table的元素(形成BFC)来解决顶部margin collapse的问题。

参考资料: