在CSS奇技淫巧之负边距的应用里提到了某些场景下前一个块的margin-bottom和文档流之后的下一个块的margin-top会发生重叠现象,而一个节点的margin-top和其子节点的margin-top也会互相影响。其实在CSS中,还有不少这样的"灵异"现象:
- 边距折叠
这就是前文所述,上一个盒子的底边距和下一个盒子的顶边距相互重叠,举例如下:
HTML
<div class="top">I am top!</div>
<div class="bottom">I am bottom!</div>
CSS
.top {
height: 100px;
margin-bottom: 20px;
background: red;
}
.bottom {
height: 100px;
margin-top: 10px;
background: green;
}
在codepen中或者本地HTML测试,上下间的间距并不是预期的20+10=30像素,而是max(20,10)=20像素。
- 浮动布局
通过设置浮动属性,很容易实现文字环绕效果:
HTML
<div class="left">I am left!</div>
<div class="right">I am right!</div>
CSS
.left {
height: 50px;
background: red;
float: left;
}
.right {
height: 100px;
background: green;
}
left被right覆盖,而right对left实现了环绕效果!效果是很好的,然而其原理呢? - 自适应三栏布局
比2更神奇的是,基于float的自适应三栏布局:
HTML
<div class="left">I am left!</div>
<div class="right">I am right!</div>
<div class="center">I am center!</div>
CSS
.left {
background: red;
float: left;
height: 100px;
}
.right {
height: 100px;
background: green;
float: left;
}
.center {
background: blue;
height: 100px;
}
对于center节点,这里并没有手动设置其宽度,却很容易的实现了自适应宽度的三列布局!
当然,还有更多的类似的例子,比如父子节点的margin-top的重叠,而通过overflow-hidden能清除子节点浮动效果,避免子元素的浮动脱离文档流而造成的高度坍塌,就不一一枚举了。我们在此探讨一下其背后的原理——BFC(Blocking Formatting Context)。
盒模型
在探究BFC之前,我们有必要回顾一下CSS经典的盒模型:
我们知道,html的布局是由一个一个的box从上至下,从左至右来进行布局的,box作为布局的基本单位,由内到外依次包括content, padding, border, margin组成,对于块级元素来说,margin-top和margin-bottom分别表示其和文档流内前一个box和后一个box的间距,而对于行内元素来说,垂直方向的height, padding-top, padding-bottom, margin-top, margin-bottom不生效,其高度完全由line-height和font-size决定。
除了上述基于文档流的布局,还有脱离文档流的float布局,其布局首先按照普通流的位置出现,然后根据浮动的方向尽可能的向左边或右边偏移,最终效果与印刷排版中的文本环绕相似,而另外一种绝对定位的布局则完全基于设置了定位的祖先节点来布局,不会影响文档中其它节点。
BFC
盒模型是布局的基本单位,而BFC则是渲染的基本单位,它定义了一个基本环境,环境内节点的布局不影响环境外的节点。那么在什么情况下浏览器会定义一个BFC呢?
* 根元素
* float属性不为none
* position为absolute或fixed
* display为inline-block, table-cell, table-caption, flex, inline-flex
* overflow不为visible
而一旦形成BFC,将具有以下性质:
- 内部的Box会在垂直方向,一个接一个地放置。
- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的 margin会发生重叠
- 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
- BFC的区域不会与float box重叠。
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- 计算BFC的高度时,浮动元素也参与计算
以上的定义和描述引用自相关文档,这里我们梳理和理解一下:
- position, display, float, overflow这些属性可能导致节点成为一个BFC
- BFC容器内的两个子节点的上下间距会重叠,左右方向上节点会覆盖前面的浮动节点,但内容不覆盖,形成环绕效果,且浮动元素可影响整个BFC的高度。
由此,对于1,我们可以简单的设置top或者bottom的overflow属性为hidden,使其成为独立的BFC,就可以避免发生上下间距重叠的现象;对于2的文字环绕效果,正是因为处于同一个BFC中,right结构与BFC的左边相接触,因此覆盖了左侧浮动元素,而BFC不允许内容覆盖,因此形成了环绕效果,一旦将right设置了float或者overflow等形成BFC的条件,这种环绕效果便消失了。三栏布局也是基于此原理,而overflow设置为hidden来清除浮动来防止节点高度坍塌也是依靠形成了BFC.
至此,我们了解了这些边距的神奇现象并不是浏览器的bug所致,而是CSS标准规定的实现。关于BFC的其它介绍,请见参考文献。
参考文献
对于BFC(block format context)理解
块级格式化上下文(Block Formatting Context) - 寒琛
关于Block Formatting Context--BFC和IE的hasLayout