如果你在面试的时候面试官让你谈谈对盒模型的理解,你是不是不知从何谈起。这种看似简单的题其实是最不好答的。
下面本文章将会从以下几个方面谈谈盒模型。
- 基本概念:标准模型 和IE模型
- CSS如何设置这两种模型
- JS如何设置获取盒模型对应的宽和高
- 实例题(根据盒模型解释边距重叠)
- BFC(边距重叠解决方案)
基本概念
盒模型的组成大家肯定都懂,由里向外content,padding,border,margin.
盒模型是有两种标准的,一个是标准模型,一个是IE模型。
从上面两图不难看出在标准模型中,盒模型的宽高只是内容(content)的宽高,
而在IE模型中盒模型的宽高是内容(content)+填充(padding)+边框(border)的总宽高。
css如何设置两种模型
这里用到了CSS3 的属性 box-sizing
/* 标准模型 */ box-sizing:content-box; /*IE模型*/ box-sizing:border-box;
JS获取宽高
通过JS获取盒模型对应的宽和高,有以下几种方法:
为了方便书写,以下用dom来表示获取的HTML的节点。
1. dom.style.width/height
这种方式只能取到dom元素内联样式所设置的宽高,也就是说如果该节点的样式是在style标签中或外联的CSS文件中设置的话,通过这种方法是获取不到dom的宽高的。
2. dom.currentStyle.width/height
这种方式获取的是在页面渲染完成后的结果,就是说不管是哪种方式设置的样式,都能获取到。
但这种方式只有IE浏览器支持。
3. window.getComputedStyle(dom).width/height
这种方式的原理和2是一样的,这个可以兼容更多的浏览器,通用性好一些。
4. dom.getBoundingClientRect().width/height
这种方式是根据元素在视窗中的绝对位置来获取宽高的
5.dom.offsetWidth/offsetHeight
这个就没什么好说的了,最常用的,也是兼容最好的。
边距重叠
什么是边距重叠
如下图,父元素没有设置margin-top,而子元素设置了margin-top:20px;可以看出,父元素也一起有了边距。
上图的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> *{ margin:0; padding:0; } .demo{ height:100px; background: #eee; } .parent{ height:200px; background: #88f; } .child{ height:100px; margin-top:20px; background: #0ff; width:200px; } </style> </head> <body> <section class="demo"> <h2>此部分是能更容易看出让下面的块的margin-top。</h2> </section> <section class = "parent"> <article class="child"> <h2>子元素</h2> margin-top:20px; </article> <h2>父元素</h2> 没有设置margin-top </section> </body> </html>
边距重叠解决方案(BFC)
首先要明确BFC是什么意思,其全英文拼写为 Block Formatting Context 直译为“块级格式化上下文”
BFC的原理
- 内部的box会在垂直方向,一个接一个的放置
- 每个元素的margin box的左边,与包含块border box的左边相接触(对于从做往右的格式化,否则相反)
- box垂直方向的距离由margin决定,属于同一个bfc的两个相邻box的margin会发生重叠
- bfc的区域不会与浮动区域的box重叠
- bfc是一个页面上的独立的容器,外面的元素不会影响bfc里的元素,反过来,里面的也不会影响外面的
- 计算bfc高度的时候,浮动元素也会参与计算
怎么取创建bfc
- float属性不为none(脱离文档流)
- position为absolute或fixed
- display为inline-block,table-cell,table-caption,flex,inine-flex
- overflow不为visible
- 根元素
应用场景
- 自适应两栏布局
- 清除内部浮动
- 防止垂直margin重叠
看一个垂直margin重叠例子
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> *{ margin:0; padding:0; } .top{ background: #0ff; height:100px; margin-bottom:30px; } .bottom{ height:100px; margin-top:50px; background: #ddd; } </style> </head> <body> <section class="top"> <h1>上</h1> margin-bottom:30px; </section> <section class="bottom"> <h1>下</h1> margin-top:50px; </section> </body> </html>
效果图
用bfc可以解决垂直margin重叠的问题
关键代码
<section class="top"> <h1>上</h1> margin-bottom:30px; </section> <!-- 给下面这个块添加一个父元素,在父元素上创建bfc --> <div style="overflow:hidden"> <section class="bottom"> <h1>下</h1> margin-top:50px; </section> </div>
效果图
关于bfc的应用的案例这里就不在一一举出了,大家去网上找一些其他的文章看一下。
二、盒子塌陷
1,盒子塌陷是什么?
本应该在父盒子内部的元素跑到了外部。
2,为什么会出现盒子塌陷?
当父元素没设置足够大小的时候,而子元素设置了浮动的属性,子元素就会跳出父元素的边界(脱离文档流),尤其是当父元素的高度为auto时,而父元素中又没有其它非浮动的可见元素时,父盒子的高度就会直接塌陷为零, 我们称这是CSS高度塌陷。
下图下方两个子元素的盒子分别设置了左浮动和右浮动,顶端的长条盒子出现了塌陷
ex :
3,关于盒子塌陷的几种解决方法
最简单,直接,粗暴的方法就是盒子大小写死,给每个盒子设定固定的width和height,直到合适为止,这样的好处是简单方便,兼容性好,适合只改动少量内容不涉及盒子排布的版面,缺点是非自适应,浏览器的窗口大小直接影响用户体验。
给外部的父盒子也添加浮动,让其也脱离标准文档流,这种方法方便,但是对页面的布局不是很友好,不易维护。
给父盒子添加overflow属性。
overflow:auto; 有可能出现滚动条,影响美观。
overflow:hidden; 可能会带来内容不可见的问题。父盒子里最下方引入清除浮动块。最简单的有:
<br style="clear:both;"/>
有很多人是这么解决的,但是我们并不推荐,因为其引入了不必要的冗余元素 。after伪类清除浮动。
外部盒子的after伪元素设置clear属性。
#parent:after{
clear: both;
content: "";
width: 0;
height: 0;
display: block;
visibility: hidden;
}
这是一种纯CSS的解决浮动造成盒子塌陷方法,没有引入任何冗余元素,推荐使用此方法来解决CSS盒子塌陷。
备注:第五种方法虽好,但是低版本IE不兼容,具体选择哪种解决方法,可根据实际情况决定。
三、深入理解BFC
BFC 已经是一个耳听熟闻的词语了,网上有许多关于 BFC 的文章,介绍了如何触发 BFC 以及 BFC 的一些用处(如清浮动,防止 margin 重叠等)。虽然我知道如何利用 BFC 解决这些问题,但当别人问我 BFC 是什么,我还是不能很有底气地解释清楚。于是这两天仔细阅读了CSS2.1 spec 和许多文章来全面地理解BFC。
一、BFC是什么?
在解释 BFC 是什么之前,需要先介绍 Box、Formatting Context的概念。
Box: CSS布局的基本单位
Box 是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:
- block-level box:display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context;
- inline-level box:display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;
- run-in box: css3 中才有, 这儿先不讲了。
Formatting context
Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)。
CSS2.1 中只有 BFC
和 IFC
, CSS3 中还增加了 GFC
和 FFC。
BFC 定义
BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。
BFC布局规则:
- 内部的Box会在垂直方向,一个接一个地放置。
- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
- 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
- BFC的区域不会与float box重叠。
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- 计算BFC的高度时,浮动元素也参与计算
二、哪些元素会生成BFC?
- 根元素
- float属性不为none
- position为absolute或fixed
- display为inline-block, table-cell, table-caption, flex, inline-flex
- overflow不为visible
三、BFC的作用及原理
1. 自适应两栏布局
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<style>
body {
width: 300px;
position: relative;
}
.aside {
width: 100px;
height: 150px;
float: left;
background:
#f66;
}
.main {
height: 200px;
background:
#fcc;
}
</style>
<body>
<div
class
=
"aside"
></div>
<div
class
=
"main"
></div>
</body>
|
页面:
根据BFC
布局规则第3条:
每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
因此,虽然存在浮动的元素aslide,但main的左边依然会与包含块的左边相接触。
根据BFC
布局规则第四条:
BFC
的区域不会与float box
重叠。
我们可以通过通过触发main生成BFC
, 来实现自适应两栏布局。
1
2
3
|
.main {
overflow: hidden;
}
|
当触发main生成BFC
后,这个新的BFC
不会与浮动的aside重叠。因此会根据包含块的宽度,和aside的宽度,自动变窄。效果如下:
2. 清除内部浮动
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<style>
.par {
border: 5px solid
#fcc;
width: 300px;
}
.child {
border: 5px solid
#f66;
width:100px;
height: 100px;
float: left;
}
</style>
<body>
<div
class
=
"par"
>
<div
class
=
"child"
></div>
<div
class
=
"child"
></div>
</div>
</body>
|
页面:
根据BFC
布局规则第六条:
计算
BFC
的高度时,浮动元素也参与计算
为达到清除内部浮动,我们可以触发par生成BFC
,那么par在计算高度时,par内部的浮动元素child也会参与计算。
代码:
1
2
3
|
.par {
overflow
:
hidden
;
}
|
效果如下:

3. 防止垂直 margin 重叠
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<style>
p {
color
:
#f55
;
background
:
#fcc
;
width
:
200px
;
line-height
:
100px
;
text-align
:
center
;
margin
:
100px
;
}
</style>
<body>
<p>Haha</p>
<p>Hehe</p>
</body>
|
页面:
两个p之间的距离为100px,发送了margin重叠。
根据BFC布局规则第二条:
Box
垂直方向的距离由margin决定。属于同一个BFC
的两个相邻Box
的margin会发生重叠
我们可以在p外面包裹一层容器,并触发该容器生成一个BFC
。那么两个P便不属于同一个BFC
,就不会发生margin重叠了。
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<style>
.wrap {
overflow: hidden;
}
p {
color:
#f55;
background:
#fcc;
width: 200px;
line-height: 100px;
text-align:center;
margin: 100px;
}
</style>
<body>
<p>Haha</p>
<div
class
=
"wrap"
>
<p>Hehe</p>
</div>
</body>
|
效果如下:
总结
其实以上的几个例子都体现了BFC
布局规则第五条:
BFC
就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
因为BFC
内部的元素和外部的元素绝对不会互相影响,因此, 当BFC
外部存在浮动时,它不应该影响BFC
内部Box的布局,BFC
会通过变窄,而不与浮动有重叠。同样的,当BFC
内部有浮动时,为了不影响外部元素的布局,BFC
计算高度时会包括浮动的高度。避免margin重叠也是这样的一个道理
四、CSS元素居中方法
如果要问如何CSS实现绝对定位元素的居中效果,很多人心里已经有答案了。
兼容性不错的主流用法是:
.element { width: 600px; height: 400px; position: absolute; left: 50%; top: 50%; margin-top: -200px; /* 高度的一半 */ margin-left: -300px; /* 宽度的一半 */ }
但,这种方法有一个很明显的不足,就是需要提前知道元素的尺寸。否则margin
负值的调整无法精确。此时,往往要借助JS获得。
CSS3的兴起,使得有了更好的解决方法,就是使用transform
代替margin
. transform
中translate
偏移的百分比值是相对于自身大小的,于是,我们可以:
.element {
width: 600px; height: 400px;
position: absolute; left: 50%; top: 50%;
transform: translate(-50%, -50%); /* 50%为自身尺寸的一半 */
}
于是乎,无论绝对定位元素的尺寸是多少,其都是水平垂直居中显示的。
然,问题很明显,兼容性不好。IE10+以及其他现代浏览器才支持, IE9(-ms-), IE10+以及其他现代浏览器才支持。中国盛行的IE8浏览器被忽略是有些不适宜的(手机web开发可忽略)。
实际上,绝对定位元素的居中实现还有另外一种方法,可以说是权衡了上面的尺寸自适应以及兼容性的一个方案,其实现的核心是margin:auto
.
三、margin:auto实现绝对定位元素的居中
首先,我们来看下CSS代码:
.element {
width: 600px; height: 400px;
position: absolute; left: 0; top: 0; right: 0; bottom: 0;
margin: auto; /* 有了这个就自动居中了 */
}
代码两个关键点:
- 上下左右均
0
位置定位; margin: auto
于是,就居中了。上面代码的width: 600px
height: 400px
仅是示意,你修改为其他尺寸,或者不设置尺寸(需要是图片这种自身包含尺寸的元素),都是居中显示的。很有意思的~~
五、清除浮动方法
为什么要清除浮动
我们通常使用浮动来实现某些元素的布局,但是往往这些元素浮动会影响其他元素的布局,因此会产生副作用。
1. 块状元素,会钻进浮动元素的下面,被浮动元素所覆盖
2. 行内元素,例如文字, 则会环绕在浮动元素的周围,为浮动元素留出空间
3. 浮动元素的父元素坍缩
额外标签法四大
使用:after 伪元素
给父元素定高
利用overflow:hidden;属性
父元素浮动
父元素处于绝对定位
在开发网页的时候经常需要用到各种浮动,此时便需要及时的清除浮动,否则将会导致布局出现问题
引出问题:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width:300px;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="footer"></div>
</body>
</html>
可以看出本应包住3个 inner DIV的 outer DIV 却没有包住他们,此刻只剩一条由上下边框贴合组成的线,同时 footer DIV元素也跑到了三个浮动元素的底下
解决办法:
1. 使用额外标签法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
.clearfix{
clear: both;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
<div class="clearfix"></div>
</div>
<div class="footer"></div>
</body>
</html>
这是早期普遍使用的方法,但是对于有代码洁癖的人来说,解决的不够完美
2. 使用 :after 为元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
.clearfix:after{ /*最简方式*/
content: '';
display: block;
clear: both;
}
/* 新浪使用方式
.clearfix:after{
content: '';
display: block;
clear: both;
height: 0;
visibility: hidden;
}
*/
.clearfix{ /*兼容 IE*/
zoom: 1;
}
</style>
</head>
<body>
<div class="outer clearfix">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="footer"></div>
</body>
</html>
3. 给父元素定高
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
height: 50px;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="footer"></div>
</body>
</html>
4. 利用 overflow:hidden 属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
overflow: hidden;
zoom: 1;/*兼容 IE*/
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="footer"></div>
</body>
</html>
5. 父元素浮动
当父元素浮动的时候,无需为子元素的浮动清除浮动,布局时经常用到
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
float: left;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="footer"></div>
</body>
</html>
可以看出虽然 outer DIV 消除了塌陷现象,但由于其也发生了浮动故,其后元素若处于正常文档流,会被跌在底下。
如果要解决可以参考前面的办法。
例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
float: left;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
.clearfix:after{
content: "";
display: block;
clear: both;
}
.clearfix{
zoom: 1;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="clearfix"></div>
<div class="footer"></div>
</body>
</html>
6. 父元素处于绝对定位
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.outer{
border: 1px solid black;
width: 300px;
position: absolute;
}
.inner{
width: 50px;
height: 50px;
background-color: #ff4400;
margin-right: 20px;
float: left;
}
.footer{
background-color: #005FC3;
width: 200px;
height: 100px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
<div class="inner"></div>
<div class="inner"></div>
</div>
<div class="clearfix"></div>
<div class="footer"></div>
</body>
</html>