提出问题
margin溢出
现象
<style>
main {
background-color: #95a5a6;
width: 200px;
height: 200px;
}
div {
background-color: #2ecc71;
width: 100px;
height: 100px;
margin-top: 20px;
}
</style>
<main>
<div></div>
</main>
可以先想象一下上面代码所渲染出的页面是怎样的?
是不是会渲染成这样
那可大错特错了,渲染结果出乎意料
div元素的margin-top
样式并没有在main元素
内部渲染,而是在main外部
渲染了
之所以会出现margin溢出,是因为子元素的外边距在父元素中找不到参照,简单来说,就是子元素不知道外边距要顶着谁,所以就一直往上顶
这是浏览器渲染普通流块元素的BUG之一,在很多教程中,都称此现象为margin塌陷,但是个人感觉称为margin溢出更为合理,只要方便记忆就行
特定
发生的位置
发生在普通流块父子元素之间
注:块元素指的是显示类型是块元素dispaly:bolck;
注:HTML文档流包含普通文档流、浮动文档流、定位文档流
块元素之间
借用上面的代码,修改一下main元素的现实类型display: inline-block;
main {
display: inline-block;
}
再次运行代码,会发现盛世如我所愿呀
因为main元素不是普通流块元素了,就不符合margin溢出的出现条件
元素普通流
修改了main元素的HTML文档流方式position: absolute;
main {
position: absolute;
}
结果不言而喻
传递性
上文说过,margin溢出发生在普通流块父子元素之间,其实也不是完全对,因为margin溢出可以向上传递
<style>
main {
background-color: #95a5a6;
width: 300px;
margin-top: 10px;
}
article {
background-color: #d35400;
width: 200px;
}
div {
width: 100px;
height: 100px;
background-color: #2ecc71;
margin-top: 50px;
}
</style>
<main>
<article>
<div></div>
</article>
</main>
发现main元素和div元素中间隔着article元素,已经是祖孙关系了,还是发生了margin溢出
这是因为div元素的top外边距会一直往上传递,直到顶级元素
发生的方位
边距溢出只会发生在父元素上下位置,至于左右并不会发生边距溢出
<style>
main {
background-color: #95a5a6;
width: 200px;
}
div {
background-color: #2ecc71;
width: 100px;
height: 100px;
margin-top: 20px;
margin-bottom: 20px;
}
</style>
<main>
<div></div>
</main>
外边距竞争
上面只介绍div元素拥有外边距的情况,那如果main元素也设置了外边距,外层边距改显示那个元素喃?还是两个元素边距相加?
<style>
main {
background-color: #95a5a6;
width: 200px;
margin-top: 50px;
}
div {
background-color: #2ecc71;
width: 100px;
height: 100px;
margin-top: 20px;
}
</style>
<main>
<div></div>
</main>
这里把div元素和main的顶部外边距互换一下
main {
margin-top: 20px;
}
div {
margin-top: 50px;
}
是不是就可以得出结论了
当内外元素都设置外边距时,会选出中间最大那个值作为外层元素的外边距
解决方案
设置间隔
让子元素知道外边距在父元素中有参照
<style>
main {
background-color: #95a5a6;
width: 200px;
height: 200px;
/* 方案一 */
border: 5px solid #000;
/* 方案二 */
padding: 5px;
}
div {
background-color: #2ecc71;
width: 100px;
height: 100px;
margin-top: 20px;
}
</style>
<main>
<div></div>
</main>
设置border或者padding都可以
BFC
上面讲特性时,讲过修改元素普通流块元素特性,这属于BFC的一小部分,具体内容下文讲
margin重叠
现象
<style>
main {
background-color: #95a5a6;
width: 200px;
}
div {
width: 100px;
height: 100px;
}
.div1 {
background-color: #2ecc71;
margin-bottom: 20px;
}
.div2 {
background-color: #f1c40f;
margin-top: 20px;
}
</style>
<main>
<div class="div1"></div>
<div class="div2"></div>
</main>
还是先猜猜页面渲染结果吧?
肯定不是这样啦!!!现实往往是残酷的
div1元素和div2公用一个margin区域,这是浏览器渲染普通流块元素的BUG之二
特点
发生的位置
发生在普通流块兄弟元素之间
块元素之间
修改div1元素的显示类型display: inline-block;
.div1 {
display: inline-block;
}
结果就是我们一开始想要的样子
元素普通流
修改了div2元素的HTML文档流方式float: left;
.div2 {
float: left;
}
这里发生了浮动元素的高度塌陷,有兴趣可以参考CSS浮动布局
传递性
首先说结论,margin重叠不具有传递性
在div1元素外层加上section元素
<main>
<section>
<div class="div1"></div>
</section>
<div class="div2"></div>
</main>
可能你会觉得上面margin重叠不具有传递性的结论是错的,但并不是这样的
之所以显示上面的结构,是因为margin溢出的传递性造成的
div1元素margin溢出至section元素*,就相当在section元素上设置了* margin-bottom: 20px;
如果不信,可以通过设置section元素的padding: 1px;
去除margin溢出
section {
padding: 1px;
}
又恢复正常了
发生的方位
和margin重叠一样,边距重叠只会发生在父元素上下位置,至于左右并不会发生边距重叠
很容易理解块元素是独占一行,自上而下排列的
外边距竞争
这里margin重叠也是一样的,都是取最大的值作为最后的值
<style>
main {
background-color: #95a5a6;
width: 200px;
}
div {
width: 100px;
height: 100px;
}
.div1 {
background-color: #2ecc71;
margin-bottom: 60px;
}
.div2 {
background-color: #f1c40f;
margin-top: 20px;
}
</style>
<main>
<div class="div1"></div>
<div class="div2"></div>
</main>
当相邻元素都设置外边距时,会选出中间最大那个值作为相邻元素之间的外边距
解决方案
BFC
具体内容下文讲
父元素高度塌陷
有兴趣可以参考CSS浮动布局
BFC解决该浮动缺陷,BFC具体内容下文讲
浮动元素覆盖后置元素
有兴趣可以参考CSS浮动布局
BFC解决该浮动缺陷,BFC具体内容下文讲
BFC是什么
上面4种都可以通过启动BFC解决,是不是很想知道BFC到底是什么?
举个例子,小明家有一亩良田,平时都是耕种小麦,突然有一天,国家要退耕还林了,这一亩良田就回归国家种树
普通流块元素触发BFC就改变该区域浏览器渲染规则,就好比上面的例子一样,良田变成了树林,区域还是那片区域,只是该区域的浏览器渲染规则改变了
普通流块元素区域的渲染规则和开启BFC之后该区域渲染规则是不一样
这也很好理解,为什么开启BFC会解决上面的提出BUG,是因为上面这些BUG只存在于普通流块元素渲染规则中,开启BFC改变了该区域的渲染规则,自然就没有原来的BUG了
好比你从穷光蛋变成了富二代,你原来那些没钱的焦虑都会消失(扎心了。。。)
触发BFC产生规则
BFC的全称是Block Formatting Context,中文叫块级格式化上下文
很容易理解,触发BFC就可以影响该块区域的规则,也格式化了该块区域
- 开启BFC的区域,是一块独立的区域,隔绝了内部和外部的联系
- 内部元素、父元素、内层元素三方渲染互不素影响
注意: 父元素和内部元素尽量分为两个元素,要不某些触发BFC的方式不起作用
BFC的规则,可以形容为世外桃源,就是说是隔离的一块区域,内外部互不影响
如何触发BFG
其实通过上面的知识,你也能猜到一些触发BFC的方式吧,既然触发BFC是改变元素区域的渲染规则不是普通流块元素规则,那只需要改变该区域的显示类型(display)、**文档流方式(float、position)**等
display
属性值为非block
、none
、inline
(none
属性值是隐藏,设置没有意义)float
属性值非none
(更改文档流为浮动流)position
属性值absolute
、fixed
(更改文档流为定位流,relative
属性值并不行)overflow
属性值非visible
(个人不知道原因,欢迎补充)- 浏览器
html根元素
初始就有BFC - 伸缩盒子项目(flex盒子种的子元素)
- 多列容器(设置
column-count
属性) column-span
属性值为all
的元素(表格第一行横跨的所有列)
display: flow-root
其他方式触发语义化,或多或少都会影响代码运行,但是display: flow-root
并不会影响,所有建议使用display: flow-root
触发BFC,但是IE不兼容
解决问题
margin溢出
<style>
main {
background-color: #95a5a6;
width: 200px;
height: 200px;
}
div {
background-color: #2ecc71;
width: 100px;
height: 100px;
margin-top: 20px;
}
</style>
<main>
<div></div>
</main>
-
让div元素触发BFC,让div元素的外边距不影响外部元素
(不建议,父元素和内部元素尽量分为两个元素,要不某些触发BFC的方式不起作用,再次提醒)
div{
display: inline-block;
}
- 触发让main元素触发BFC,让main内部元素不影响外部元素
main{
display: flow-root;
}
margin重叠
<style>
main {
background-color: #95a5a6;
width: 200px;
}
div {
width: 100px;
height: 100px;
}
.div1 {
background-color: #2ecc71;
margin-bottom: 20px;
}
.div2 {
background-color: #f1c40f;
margin-top: 20px;
}
</style>
<main>
<div class="div1"></div>
<div class="div2"></div>
</main>
-
div元素触发BFC让div元素的外边距不影响外部元素
(不建议,父元素和内部元素尽量分为两个元素,要不某些触发BFC的方式不起作用,再再次提醒)
div { display: inline-block; }
-
为div1元素和div2元素设置父元素,然后让父元素触发BFC
<style> section { display: flow-root; } </style> <main> <section> <div class="div1"></div> </section> <section> <div class="div2"></div> </section> </main>
父元素高度塌陷
<style>
main {
border: 3px solid #34495e;
padding: 3px;
background-color: #ecf0f1;
}
main>div {
width: 50px;
height: 50px;
background-color: #f1c40f;
float: right;
}
</style>
<main>
<div></div>
</main>
-
main元素触发BFC
main{ display: flow-root; }
浮动元素覆盖后置元素
<style>
main {
border: 3px solid #34495e;
background-color: #ecf0f1;
}
div {
height: 80px;
}
.div1 {
background-color: #f39c12;
float: left;
width: 80px;
}
.div2 {
background-color: #d35400;
width: 100px;
}
</style>
<main>
<div class="div1"></div>
<div class="div2"></div>
</main>
-
div2元素触发BFC让div1元素的浮动不影响外部元素(老规矩,不建议)
.div2 { display: flow-root; }
-
为div1元素和div2元素设置父元素,然后让父元素触发BFC
<style> section { display: flow-root; } </style> <main> <section> <div class="div1"></div> </section> <section> <div class="div2"></div> </section> </main>