由浮动塌陷引发的块级格式上下文思考
最后更新于
最后更新于
浮动最初的定义:实现文本环绕图片的效果。除此之外,没有其他方法可实现。
CSS 中的浮动是用 float
属性来定义:float: left | right | none | inline-start | inline-end
float
属性值不为 none
,会对 display
属性的值产生影响:
block、inline-block、inline、table-*
-> block
inline-table、table
-> table
flex、inline-flex
-> 值不变,但是 float
对这样的元素不起作用
其他值不变
当一个元素浮动之后,它会被移出正常的文档流,然后向左或者向右平移,一直平移直到碰到了所处的容器的边框,或者碰到另外一个浮动的元素。浮动元素不属于文档中的普通流,属于浮动布局。顾名思义,浮动就是漂浮于普通流之上,像浮云一样,但是只能左右浮动。
CSS 有三种基本的定位机制:普通流、浮动(float: left|right)和绝对定位(position: absoulte)。
注意: 浮动元素只在水平方向浮动,也就是说,只能左右移动而不能上下移动;并且它会尽量向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止,也就是说,它只能居左或居右,而无法居中。
浮动会造成哪些影响呢?
浮动元素会对其相邻元素和父元素造成不同影响:
对相邻元素(紧邻浮动元素后面的元素): 如果相邻元素是普通流的块级元素,则不会影响块级元素的布局(如上图的背景色模块——块级元素与浮动元素处于同一行,但其宽度占据了父元素的整个内容区域,浮动元素只是悬浮在块元素上方),但会影响块级元素包含的内联元素的排列(内联元素环绕浮动元素);
如果相邻元素是普通流的内联元素,则会影响内联元素的布局和其包含的内联元素的排列(如上图的文本模块——内联元素的位置是紧跟浮动元素,而不是从父元素的最左侧开始,且文本会尽可能围绕浮动元素)。
注意: 浮动元素之前的元素将不会受到影响。
对父元素: 父元素在获取高度计算时,会忽略浮动元素,即不计算浮动元素高度。这就是浮动塌陷,也称高度塌陷现象。
块格式化上下文(Block Formatting Context,BFC) 是 Web 页面的可视 CSS 渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。一个元素创建了 BFC 后,它就拥有了一个独立的环境,该环境内的子元素不会影响外面元素的布局,同样,外面的元素也不会影响其子元素的布局。
块级格式化上下文的特性与层叠上下文相似,都是指独立的空间,不过,块级格式化上下文是 x、y 的独立空间,而层叠上下文是 z 轴上的独立空间。
块格式化上下文对浮动定位(float)、清除浮动(clear)、外边距折叠(Margin collapsing)都很重要。浮动定位和清除浮动时只会应用于同一个 BCF 内的元素。外边距折叠也只会发生在属于同一 BFC 的块级元素之间。
BFC 的三个特性:
BFC 会阻止外边距折叠(CSS探索系列之Margin)
当两个相邻的块框在同一个块级格式化上下文中时,它们之间垂直方向的外边距可能会发生叠加。换句话说,如果这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。
BFC 不会重叠浮动元素
BFC 内的浮动不会影响其它 BFC 中元素的布局,而 clear
属性只能清除同一 BFC 中在它前面的元素的浮动。
BFC 通常可以包含浮动,即计算 BFC 的高度时,浮动元素也参与计算
独立的块级上下文可以包裹浮动流,全部浮动子元素也不会引起容器高度塌陷,也就是说父元素会把浮动元素的高度也计算在内,所以不用清除浮动来撑起高度。同时 BFC 仍然属于文档中的普通流。
也就是说,如果父元素创建了 BFC,就可以闭合浮动,避免浮动塌陷现象
一般来说,是通过 display
属性来创建 BFC ,具体如下:
根元素
浮动元素: float
属性值不为 none
;
绝对定位元素: position
属性值不为 static|relative
;
行内块元素: display
属性值为 inline-block
;
元素的 overflow
属性值不为 visible
;
表格单元格:display: table-cell
;
表格标题:display: table-caption
匿名表格单元格元素:display: table | inline-table | table-row | table-row-group | table-header-group | table-footer-group
元素的 display
属性值为 flow-root
。这是一个新的属性值,可以创建无副作用的 BFC,但 Safari 不支持。
弹性元素:display: flex | inline-flex
元素的直接子元素
网格元素:display: grid | inline-grid
元素的直接子元素
contain: layout | content | paint
多列容器:column-count
或 column-width
不为 auto
、column-count: 1
column-span: all
。即使该元素没有包裹在一个多列容器中(标准变更,Chrome bug)。
注意:
display: table
本身并不会创建 BFC,但是它会产生匿名盒子,而匿名盒子中的display: table-cell
可以创建新的BFC,换句话说,触发块级格式化上下文的是匿名框,而不是display: table
。所以通过display: table
和display: table-cell
创建的 BFC 效果是不一样的。
hasLayout
是 IE 浏览器私有的概念,可以简单看作是 IE 5.5/6/7 中的 BFC!
IE 浏览器使用 Layout
概念来控制元素的尺寸和位置。如果一个元素有 Layout
,相当于元素创建了 BFC,其尺寸和位置以自身的内容进行计算(如:可通过width、height 来设置自身的宽高);否则,相当于没有 BFC,它的尺寸和位置由最近的拥有 Layout
的祖先元素控制。
IE 浏览器,可能通过如下方法判断元素是否有 Layout
:
需要注意的是:
hasLayout
是一个只读属性,所以无法通过 Javascript 进行设置,只能通过一些 CSS 属性间接创建;
与 BFC 不同的是:有些 CSS 属性是以不可逆方式间接创建 Layout
,并且默认创建 BFC 的只有 html 元素,而默认 Layout
的元素就不只有 html 元素了。
display: inline-block;
width、height 属性值为除 auto
外的任意值;
float: left|right;
position: absolute;
writing-mode: tb-rl;
zoom 属性值为除 normal
外的任意值;
IE7 还有一些额外的属性:min-height|min-width
属性值为任意值;max-height|max-width
属性值为除 none
外的任意值;overflow|overflow-x|overflow-y
属性值除 visible
外的任意值(仅用于块级元素)、position: fixed
。
需要注意:
创建 BFC 和 创建 Layout
的方式不完全重叠,因此 Layout
所引发的问题,很大程度可以理解为在不应该的或没有预料到的地方创建了 BFC 导致的。
IE6 以前的版本(也包括 IE6 及以后所有版本的混杂模式,其实这种混杂模式在渲染方面就相当于 IE 5.5), 通过设置任何元素的width
、height
属性值(非 auto
)都可以创建 Layout
; 但在 IE6 和 IE7 的标准模式中的行内元素上却不行,只能设置 display: inline-block
才可以。
display: inline-block|min-width: 0|min-height: 0
将不可逆地创建 Layout
,而在其他属性创建的 Layout
可通过上面创建方法,逆向关闭。如:position: static
,zoom: normal
、max-height|max-width
属性值为 none
(IE 7)等。
清除浮动,也可以称为闭合浮动。两者都是为了消除浮动元素产生的影响,即浮动塌陷。
它们的区别在于:
清除浮动: 对应 CSS 中的 clear:left|right|both|none
,只能清除同一 BFC 中,在它前面的元素的浮动。
闭合浮动:更确切的含义是使浮动元素闭合,即创建 BFC,从而消除浮动塌陷。
确切的说,我们想要达到的效果是闭合浮动,而不是单纯的清除浮动。单纯的清除浮动,并不能解决容器高度塌陷的问题。
比如:我们将上一个示例的背景色模块,加上样式 clear: both
清除浮动,该模块的浮动还是会影响其后面的兄弟元素。如下图3:
再如:我们将上一个示例的背景色模块,加上样式 display: flow-root
闭合浮动,创健了新 BFC,那么,它就不会影响其他 BFC 的元素了。如下图4:
方法一(摧荐): 父元素加伪元素,并给伪元素 ::after
加 clear:both
,据说是最高大上的方法。
**优点:**浏览器支持好,不容易出现怪问题。
缺点: 需要全局定义一个清除浮动的 .clear_float
,且要加在标签上。
**方法二:**浮动元素后,新增空标签 div
,并设置样式 clear:both
。
**优点:**通俗易懂,容易掌握。
**缺点:**将添加很多无意义的空标签。
**方法三:**浮动元素后,新增空标签 br
标签,并设置属性 clear="both"
,与方法二类似:
为浮动元素的父元素创建 BFC(为兼容 ie6,还需触发 hasLayout
,如加: zoom: 1
):
display: inline-block(推荐);
overflow: auto|hidden|visible
:据说 auto
对 SEO 比较友好, hidden
对 SEO 不是太友好。缺点是内部元素宽高超过父元素时,会出现滚动条或者隐藏溢出部分;
float: left|right
: 缺点是使得与父元素相邻的元素的布局会受到影响;
display: table
: 缺点是盒模型属性已经改变,由此造成的一系列问题,得不偿失;
display: flow-root
:缺点是 Safari
不支持。
使用 clear
属性清除浮动,其语法如下:
官方对 clear
属性的解释是:一个元素是否必须移动(清除浮动后)到在它之前的浮动元素下面。我们对元素设置 clear
属性只是避免了浮动元素对该元素的影响,实际上,浮动一直还在,并没有清除。
当应用于非浮动块时,它将非浮动块的边框边界移动到所有相关浮动元素外边界的下方;当应用于浮动元素时,它将元素的外边界移动到所有相关的浮动元素外边框边界的下方。这会影响后面浮动元素的布局,后面的浮动元素的位置无法高于它之前的元素。
Boxes in the normal flow belong to a formatting context, which may be block or inline, but not both simultaneously. Block-level boxes participate in a block formatting context. Inline-level boxes participate in an inline formatting context.
在普通流中的盒子会参与一种格式上下文,这个盒子可能是块盒也可能是行内盒,但不可能同时是块盒又是行内盒。块级盒参与块级格式上下文(BFC),行内级盒参与行级格式上下文(IFC)。
BFC 的特性:
在 BFC 中,盒子都是从它的包含块的顶部一个一个的垂直放置的。两个相邻盒子的垂直间距决定于 margin
属性。在同一 BFC 中,两个相邻块级盒子之间垂直方向上的外边距可能会塌陷的。
在 BFC 中,每个盒子的左外边界挨着包含块的左边界(与文档书写模式有关)。即使是存在浮动元素也是如此(即使一个盒子的行盒是因为浮动而收缩了的)除非这个盒子创建了新的 BFC(在某些情况下这个盒子自身会因为浮动而变窄)。
IFC 的特性:
在 IFC中,盒子水平放置,一个接着一个,从包含块的顶部开始。水平方向的 margins|borders|padding
在这些盒子中被平分。这些盒子也许通过不同的方式进行对齐:底部、顶部、基线对齐。矩形区域包含着来自一行的盒子叫做行盒子。
行盒子的宽度由浮动情况和它的包含块决定;行内盒子的高度由 line-height
的计算结果决定。
当一行不够放置的时候会自动切换到下一行,IFC 的高度,由里面最高盒子的高度决定。
在行内方向上,各行盒通常具有相同的尺寸,即在水平书写模式下,它们有同样的宽度;在垂直书写模式下,它们有同样的高度。但是,如果同一个块格式化上下文中存在一个 float
,则这个浮动元素将导致包裹了它的各行框变短。