包含块
什么是包含块
如果有两个 div,其中一个是父元素,另外一个是子元素,则父元素会决定子元素的大小和定位。包含块是什么呢?简单来说,就是可以决定一个元素的大小和定位的元素。
包含块是视觉格式化模型的一个重要概念,它跟 CSS 盒子模型类似。你也可以将包含块理解为一个矩形盒子,这个矩形的作用是为这个矩形内部的后代元素(子元素、孙元素等)提供一个参考。一个元素的大小和定位往往是由该元素所在的包含块决定的。
通常情况下,一个元素的包含块是由离它最近的“块级祖先元素”的“内容边界”决定的。但当元素被设置为绝对定位时,该元素的包含块是由离它最近的 position:relative 或 position:absolute 的祖先元素决定的。一个元素生成的盒子会扮演该元素的内部元素包含块的角色。也就是说,一个元素的 CSS 盒子为它的内部元素创建了包含块。
包含块判定以及包含块范围
一个元素会为它的内部元素创建包含块,内部元素的大小以及定位都跟它的包含块有关。但是并不能说一个元素的包含块就是它的父元素。
根元素
根元素(html 元素),是一个页面中最顶端的元素,它没有父元素。根元素存在的包含块,被称为初始包含块(initial containing block)。
固定定位元素
如果元素的 position 属性为 fixed 那么它的包含块就是当前的可视窗口,也就是当前浏览器的窗口。该元素不会随着浏览器拖动而改变位置。
静态定位元素和相对定位元素
如果元素的 position 属性为 static 或 relative,那么它的包含块是由离它最近的块级祖先元素创建的。祖先元素必须是 block、inline-block 或者 table-cell 类型。
绝对定位元素
如果元素的 position 属性为 absolute,那么它的包含块是最近的 position 属性不为 static 的祖先元素。这里的祖先元素可以是块元素,也可以是行内元素。
如果找不到 position 属性不为 static 的祖先元素,则它的包含块是 body 元素,这也是为什么默认情况下,绝对元素是相对浏览器窗口来定位的。
对于绝对定位元素包含块的范围,我们也分以下两种情况考虑。如果祖先元素是块元素,则包含块的范围为祖先元素的 padding edge。如果祖先元素是行内元素,则包含块取决于祖先元素的 direction 属性。
层叠上下文
什么是层叠上下文
层叠上下文,即 stacking context,是 HTML 中的一个三维的概念。从“z-index 属性”这 一节我们知道,虽然一个网页是平面的,但实际上网页是三维结构,除了 x 轴、y 轴,它还有 z 轴。 其中,z 轴用来设定层的先后顺序。
层叠上下文(stacking context)跟块级格式上下文(BFC)相似,是可以创建出来的。也就是说,跟创建 BFC 一样,你可以在 CSS 中添加一定的属性用于为某个元素创建一个层叠上下文。 如果一个元素具备以下任何一个条件(不考虑 CSS3),则该元素会创建一个新的层叠上下文:根元素。z-index 不为 auto 的定位元素。
什么是层叠级别
层叠级别,即 stacking level。从上面我们知道,可以使用 z-index 属性为一个元素创建一个新的层叠上下文。但一个元素往往会有背景色、浮动子元素、定位子元素等,那么这些东西又是遵 循着怎样的顺序来堆叠的呢?
同一个层叠上下文的背景色以及内部元素,谁在上谁在下,这些都是由层叠级别(stacking level)来决定的。也就是说,层叠级别是针对同一个层叠上下文而言的。层叠级别与层叠上下文是两个不同的概念。
在同一个层叠上下文中,层叠级别从低到高排列如下:
① 边框和背景:也就是当前层叠上下文的边框和背景。
② 负 z-index :z-index 为负值的“内部元素”。
③ 块盒子:普通文档流下的块盒子(block-level box)。
④ 浮动盒子:非定位的浮动元素(也就是排除了 position:relative 的浮动盒子)。
⑤ 行内盒子:普通文档流下的行内盒子(inline-level box)。
⑥ z-index:0:z-index 为 0 的“内部元素”。
⑦ 正 z-index :z-index 为正值的“内部元素”
层叠上下文的特点
一个元素在 z 轴方向上的堆叠顺序,是由“层叠上下文”和“层叠级别”这两个因素决定的。
▶ 在同一个层叠上下文中,我们比较的是“内部元素层叠级别”。层叠级别大的元素显示在上,层叠级别小的元素显示在下。
▶ 在同一个层叠上下文中,如果两个元素的层叠级别相同(即 z-index 值相同),则后面的元素堆叠在前面元素的上面,遵循“后来者居上”原则。
▶ 在不同的层叠上下文中,我们比较的是“父级元素层叠级别”。元素显示顺序以“父级层叠上下文”的层叠级别来决定显示的先后顺序,与自身的层叠级别无关。
同一层叠上下文中,元素设置为浮动之后,它的层叠级别比正常文档流下的块级盒子的层叠级别要高,此时浮动元素会浮到上面去,脱离文档流。
BFC 和 IFC
基本概念
在 CSS 中,页面的任何一个元素都可以看成是一个盒子。在普通文档流(normal flow)中, 盒子会参与一种格式上下文(formatting context)。这个盒子可能是块盒子(block-level box), 也可能是行内盒子(inline-level box)。一个盒子只能是块盒子或者是行内盒子,不能是块盒子的同时又是行内盒子。其中块盒子参与 BFC(块级格式上下文),行内盒子参与 IFC(行级格式 上下文)。
格式上下文
格式上下文是 W3C CSS2.1 规范中的一个重要概念,它指的是页面中的一块渲染区域。除此之外,这个格式上下文有一套自己的渲染规则。格式上下文决定了其内部元素将如何定位,以及和其他元素之间的关系。
格式上下文有以下两种类型。 块级格式上下文,即 Block Formatting Context,简称 BFC。行级格式上下文,即 Inline Formatting Context,简称 IFC。
盒子
盒子,又称 CSS 盒子,是 CSS 布局的基本单位。简单来说,一个页面就是由很多个盒子组成的。元素的类型和 display 属性决定了盒子的类型。不同类型的盒子,会参与不同的格式上下文。
BFC
创建 BFC
浮动元素,绝对定位元素(position 为 absolute 或 fixed),元素类型(即 display 属性)为 inline-block、table-caption、table-cell,以及 overflow 属性不为 visible 的元素将会创建一个新的块级格式上下文(BFC)。
如果一个元素具备以下任何一个条件,则该元素都会创建一个新的 BFC:
▶ 根元素
▶ float 属性除了 none 以外的值,也就是 float:left 和 float:right。
▶ position 属性除了 static 和 relative 以外的值,也就是 position:absolute 和 position:fixed。
▶ overflow 属性除了 visible 以外的值,也就是 overflow:auto、overflow:hidden 和 overflow:scroll。
▶ 元素类型(即 display 属性)为 inline-block、table-caption、table-cell。
也就是说,如果我们要创建一个新的 BFC,只需要在 CSS 中添加上面任意一个属性就可以了。 创建新的 BFC 可以帮助我们解决很多问题。这里要注意一下,根元素也会 创建 BFC。也就是说,默认情况下一个页面中所有的元素都处于同一个 BFC 中,不需要我们去设置。虽然这些属性都可以创建 BFC,但是也会 产生如下的一些影响:
▶ float:left 和 float:right 会将元素移到左边或右边,并被其他元素环绕。
▶ overflow:hidden 会将超出元素的内容隐藏。
▶ overflow:scroll 会产生多余的滚动条。
▶ display:table 可能引发响应性问题。
注意:根据定义,类型为 block、table 的元素不会创建 BFC。元素类型(即 display 属 性)为 block、table、list-item 的元素,会生成块盒子(block-level box),然后块盒子会参与 BFC,即 block、table、list-item 等类型的元素是参与 BFC,而不是创建 BFC。
BFC 特点
W3C 标准描述 BFC 的特点共有两条:在一个 BFC 中,盒子从顶端开始一个接着一个地垂直排列,两个相邻盒子之间的垂直间距由 margin 属性决定。在同一个 BFC 中,两个相邻块盒子之间垂直方向上的外边距会叠加。在一个 BFC 中,每一个盒子的左外边界(margin-left)会紧贴着包含盒子的容器的左边(border-left)(对于从右到左的格式化,则相反),即使存在浮动元素也是如此。
这里进一步分析得到以下几点:
▶ 在一个 BFC 内部,盒子会在垂直方向上一个接着一个排列。
▶ 在一个 BFC 内部,相邻的 margin-top 和 margin-bottom 会叠加。
▶ 在一个 BFC 内部,每一个元素的左外边界会紧贴着包含盒子的容器的左边,即使存在浮 动也是如此。
▶ 在一个 BFC 内部,如果存在内部元素是一个新的 BFC,并且存在内部元素是浮动元素, 则该 BFC 的区域不会与 float 元素的区域重叠。
▶ BFC 就是页面上的一个隔离的盒子,该盒子内部的子元素不会影响外部的元素。
▶ 计算一个 BFC 的高度时,其内部浮动元素的高度也会参与计算。
为什么在一个 BFC 中,盒子会在垂直方向上一个接着一个排列呢?如 果在一个 BFC 中有一个盒子是 span 这种行内元素,岂不是不符合了?再次提醒一下,能够参与 BFC 中的盒子是块盒子(block-level box)。就算在这个 BFC 中存在一个行内元素,这个行内元素参与的是 IFC,而不是 BFC,别混淆了。
BFC 用途
创建 BFC 避免垂直外边距叠加
外边距叠加,准确地说是指在同 一个 BFC 中,两个相邻的 margin-top 和 margin-bottom 相遇时,这两个外边距将会合并为一个外边距,即“二变一”。其中,叠加之后的外边距高度等于发生叠加之前的两个外边距中的最大值。之所以会发生垂直外边距叠加,是因为这是 BFC 的特点。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
#wrapper
{
width:200px;
border:1px solid gray;
overflow:hidden; /*创建一个新的BFC*/
}
#a,#b
{
height:60px;
line-height:60px;
text-align:center;
font-size:30px;
color:White;
background-color:Purple;
}
#a{margin-bottom:20px;}
#b{margin-top:30px;}
</style>
</head>
<body>
<div id="wrapper">
<div id="a">A</div>
<div id="b">B</div>
</div>
</body>
</html>
使用 overflow:hidden 为父元素创建一个 BFC,也就是说父元素是一个 BFC 了。因此 A 和 B 位于同一个 BFC 中,这里就发生了垂直外边界叠加,A B 之间的垂直距离为 30px,如果我们没有为父元 素创建 BFC,则默认情况下 A 和 B 就是处于根元素的 BFC 中,此时依然会发生叠加。
解决方式:可以将两个盒子放到不同的 BFC 中:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
#wrapper
{
width:200px;
border:1px solid gray;
overflow:hidden; /*创建一个BFC*/
}
#bfc-box
{
overflow:hidden; /*创建一个BFC,避免外边距叠加*/
}
#a,#b
{
height:60px;
line-height:60px;
text-align:center;
font-size:30px;
color:White;
background-color:Purple;
}
#a{margin-bottom:20px;}
#b{margin-top:30px;}
</style>
</head>
<body>
<div id="wrapper">
<div id="a">A</div>
<div id="bfc-box">
<div id="b">B</div>
</div>
</div>
</body>
</html>
A 和 B 处于不同的 BFC 中,其中 A 处于 #wrapper 元素的 BFC 中,B 处于 #bfc-box 元素的 BFC 中,所以不会发生垂直外边距叠加。这里要注意:假如我们不给 #bfcbox 元素添加 overflow:hidden,A 和 B 也会发生垂直外边距叠加。但是不对啊,此时 A 和 B 都不属于相邻的元素,为什么它们还会发生外边距叠加呢?
回看结论:同一个 BFC 内部,相邻的 margin-top 和 margin-bottom 会叠加。这里的“相邻”不是指“相邻的兄弟元素”,而是指相邻的 margin-top 和 margin-bottom。
创建 BFC 来清除浮动
BFC 包含浮动
已知“可以使用 overflow:hidden 来清除浮动”,以及“计算一个 BFC 的高度时,其内部浮动元素的高度也会参与计算”两点,可以得出:如果一个元素是一 个 BFC,则计算该元素高度的时候,内部浮动子元素的高度也得算进去。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
#wrapper
{
width:200px;
border: 1px solid black;
overflow:hidden;
}
#first,#second
{
width:80px;
height:40px;
border:1px solid red;
}
#first{float:left;}
#second{float:right;}
</style>
</head>
<body>
<div id="wrapper">
<div id="first"></div>
<div id="second"></div>
</div>
</body>
</html>
把浮动子元素的高度算进去,因此最终父元素的高度等于浮动子元素的高度。此时就相当于清除了浮动。当然我们也可以通过给父元素添加 display:inline-block、float:left 等来创建新的 BFC,以此实现浮动的清除。
BFC 避免文字环绕
举例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
#wrapper
{
width:400px;
height:200px;
border:1px solid gray;
padding:10px;
}
img{float:left;}
#content{background-color:#FFACAC;overflow:hidden;}
</style>
</head>
<body>
<div id="wrapper">
<img src="images/ailianshuo.png" alt=""/>
<div id="content">水陆草木之花,可爱者甚蕃。晋陶渊明独爱菊。自李唐来,世人甚爱牡丹。予独爱
莲之出淤泥而不染,濯清涟而不妖,中通外直,不蔓不枝,香远益清,亭亭净植,可远观而不可亵玩焉。予谓菊,花之隐逸者也;
牡丹,花之富贵者也;莲,花之君子者也。噫!菊之爱,陶后鲜有闻;莲之爱,同予者何人? 牡丹之爱,宜乎众矣。</div>
</div>
</body>
</html>
这里为 #content 元素添加 overflow:hidden,此时 #content 元素变成了一个新的 BFC,根据结论:“在一个 BFC 内部,如果存在内部元素是一个新的 BFC,并且存在内部元素是浮动元素”,则该 BFC 的区域不会与 float 元素的区域重叠。
使用 BFC 创建自适应两列布局
自适应两列布局,指的是在左右两列中,有一列的宽度为自适应,另外一列的宽度是固定的。与上面的避免文字环绕方式相似。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
#sidebar
{
float:left;
width:100px;
height:150px;
background:#FF6666;
}
#content
{
overflow:hidden;
height:200px;
background-color:#FFCCCC;
}
</style>
</head>
<body>
<div id="sidebar"></div>
<div id="content"></div>
</body>
</html>