由浮动塌陷引发的块级格式上下文思考
浮动最初的定义:实现文本环绕图片的效果。除此之外,没有其他方法可实现。
什么是浮动?
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)。
注意: 浮动元素只在水平方向浮动,也就是说,只能左右移动而不能上下移动;并且它会尽量向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止,也就是说,它只能居左或居右,而无法居中。
<section class="box_02">
<div class="item_02">
<div class="container_float"><img src="./images/pins_3333913039.jpg"></div>
<div class="bg"></div>
</div>
<div class="item_02">
<img class="img_float" src="./images/pins_3333913039.jpg">
<div class="text">读书,去别人的灵魂里偷窥。旅行,去陌生的环境里去感悟。电影,去荧屏里感受别人的生活历程。冥想,去自己内心的秘境里探寻。<br />读书,去别人的灵魂里偷窥。旅行,去陌生的环境里去感悟。电影,去荧屏里感受别人的生活历程。冥想,去自己内心的秘境里探寻。</div>
</div>
</section>
<style>
.box_02 img {
width: 100%;
}
.box_02 > .item_02 {
padding: 20px;
border: 1px solid #0aa;
}
.box_02 > .item_02 .container_float,
.box_02 > .item_02 .img_float {
float: left;
width: 100px;
margin-left: 20px;
margin-right: 20px;
}
.box_02 > .item_02 .bg {
height: 50px;
margin-top: 5px;
background: #0aa;
}
</style>
浮动会造成哪些影响呢?
浮动元素会对其相邻元素和父元素造成不同影响:
对相邻元素(紧邻浮动元素后面的元素): 如果相邻元素是普通流的块级元素,则不会影响块级元素的布局(如上图的背景色模块——块级元素与浮动元素处于同一行,但其宽度占据了父元素的整个内容区域,浮动元素只是悬浮在块元素上方),但会影响块级元素包含的内联元素的排列(内联元素环绕浮动元素);
如果相邻元素是普通流的内联元素,则会影响内联元素的布局和其包含的内联元素的排列(如上图的文本模块——内联元素的位置是紧跟浮动元素,而不是从父元素的最左侧开始,且文本会尽可能围绕浮动元素)。
注意: 浮动元素之前的元素将不会受到影响。
对父元素: 父元素在获取高度计算时,会忽略浮动元素,即不计算浮动元素高度。这就是浮动塌陷,也称高度塌陷现象。
什么是块级格式化上下文?
块格式化上下文(Block Formatting Context,BFC) 是 Web 页面的可视 CSS 渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。一个元素创建了 BFC 后,它就拥有了一个独立的环境,该环境内的子元素不会影响外面元素的布局,同样,外面的元素也不会影响其子元素的布局。
块级格式化上下文的特性与层叠上下文相似,都是指独立的空间,不过,块级格式化上下文是 x、y 的独立空间,而层叠上下文是 z 轴上的独立空间。
BFC 有哪些特性?
块格式化上下文对浮动定位(float)、清除浮动(clear)、外边距折叠(Margin collapsing)都很重要。浮动定位和清除浮动时只会应用于同一个 BCF 内的元素。外边距折叠也只会发生在属于同一 BFC 的块级元素之间。
BFC 的三个特性:
BFC 会阻止外边距折叠(CSS探索系列之Margin)
当两个相邻的块框在同一个块级格式化上下文中时,它们之间垂直方向的外边距可能会发生叠加。换句话说,如果这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。
BFC 不会重叠浮动元素
BFC 内的浮动不会影响其它 BFC 中元素的布局,而
clear
属性只能清除同一 BFC 中在它前面的元素的浮动。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?
hasLayout
是 IE 浏览器私有的概念,可以简单看作是 IE 5.5/6/7 中的 BFC!
IE 浏览器使用 Layout
概念来控制元素的尺寸和位置。如果一个元素有 Layout
,相当于元素创建了 BFC,其尺寸和位置以自身的内容进行计算(如:可通过width、height 来设置自身的宽高);否则,相当于没有 BFC,它的尺寸和位置由最近的拥有 Layout
的祖先元素控制。
IE 浏览器,可能通过如下方法判断元素是否有 Layout
:
// 返回 true 表示有 Layout,否则就是没有
Element.currentStyle.hasLayout
需要注意的是:
hasLayout
是一个只读属性,所以无法通过 Javascript 进行设置,只能通过一些 CSS 属性间接创建;- 与 BFC 不同的是:有些 CSS 属性是以不可逆方式间接创建
Layout
,并且默认创建 BFC 的只有 html 元素,而默认Layout
的元素就不只有 html 元素了。
哪些元素有 Layout?
<html> <body>
<table> <tr> <th> <td>
<img> <hr>
<input> <button> <select> <textarea> <fieldset> <legend>
<iframe> <embed> <object> <applet> <marquee>
如何为元素创建 Layout?
- 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:
<section class="box_02">
<div class="item_02" style="clear: both;">
<div class="container_float"><img src="./images/pins_3333913039.jpg"></div>
<div class="bg"></div>
</div>
...
</section>
再如:我们将上一个示例的背景色模块,加上样式 display: flow-root
闭合浮动,创健了新 BFC,那么,它就不会影响其他 BFC 的元素了。如下图4:
<section class="box_02">
<div class="item_02" style="display: flow-root;">
<div class="container_float"><img src="./images/pins_3333913039.jpg"></div>
<div class="bg"></div>
</div>
...
</section>
三种清除浮动的方法
方法一(摧荐): 父元素加伪元素,并给伪元素 ::after
加 clear:both
,据说是最高大上的方法。
优点:浏览器支持好,不容易出现怪问题。
缺点: 需要全局定义一个清除浮动的 .clear_float
,且要加在标签上。
<!--定义一个通用清除浮动的样式,在需要的元素中加上-->
<!--clear 属性只有块级元素才有效的,而 ::after 等伪元素默认都是内联元素,所以加 display: block。
<style>
.clear_float {
/*对IE6/7的兼容处理,触发 hasLayout*/
zoom: 1;
}
.clear_float:after,
.clear_float::after {
clear: both;
content: '.';
display: block;
width: 0;
height: 0;
visibility: hidden;
}
</style>
<section class="box_02">
<div class="item_02 clear_float">...</div>
<div class="item_02">...</div>
</section>
方法二:浮动元素后,新增空标签 div
,并设置样式 clear:both
。
优点:通俗易懂,容易掌握。
缺点:将添加很多无意义的空标签。
<section class="box_02">
<div class="item_02">...</div>
<div style="clear: both;"></div>
<div class="item_02">...</div>
</section>
方法三:浮动元素后,新增空标签 br
标签,并设置属性 clear="both"
,与方法二类似:
<section class="box_02">
<div class="item_02">...</div>
<br style="clear: both;" />
<div class="item_02">...</div>
</section>
闭合浮动的方法
为浮动元素的父元素创建 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: none|left|right|both
官方对 clear
属性的解释是:一个元素是否必须移动(清除浮动后)到在它之前的浮动元素下面。我们对元素设置 clear
属性只是避免了浮动元素对该元素的影响,实际上,浮动一直还在,并没有清除。
当应用于非浮动块时,它将非浮动块的边框边界移动到所有相关浮动元素外边界的下方;当应用于浮动元素时,它将元素的外边界移动到所有相关的浮动元素外边框边界的下方。这会影响后面浮动元素的布局,后面的浮动元素的位置无法高于它之前的元素。
BFC 和 IFC 有什么区别?
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
,则这个浮动元素将导致包裹了它的各行框变短。