这几个概念等价:block-and-inline layout
normal flow
flow layout
。
flow layout
就是所谓的流体布局
,它分为垂直和水平两个方向的布局上下文,垂直方向上的布局上下文称为:block formatting context
,简称BFC
;水平方向上的布局上下文称为inline formatting context
,简称IFC
。boxes
在flow layout
里面,要么处于BFC
当中,要么处于IFC
当中,不可能同时位于两个上下文。具体来说:block-level
的box
位于BFC
,inline-level
的box
位于IFC
。
block formatting context
网页默认就有一个BFC
,一开始的内容都是布局在这个BFC
里面的。 有很多方法可以让一个box
新建BFC
,比如绝对或固定定位
floats
display: inline-block | table-cell | table-caption
非visible值的overflow
。
在BFC
里面,只有block-level
的box
,不会出现inline box
,如果同时有block box
和inline box
,则会在inline box
外部包裹一层anonymous block-level box
。这些box
从它的containing block
顶部边缘开始,从上到下依次排列布局。两个相邻的box
之间的间隙是由它们的外边距决定的,外边距满足条件时会触发外边距合并。
如果一个block-level
的box
未创建一个新的BFC
,则它的children
就会跟自己一起,布局在相同的BFC
里面;如果它创建了BFC
,则它的children
会部署在自己新建的BFC
里面,与自己所在的BFC
是分离的。 只有位于相同BFC
当中的box
,才会发生特定的flow layout
行为,否则不会。比如外边距合并这种行为就只能发生在相同的BFC
当中。
看下面这个例子:1
2
3
4
5
6
7
8
9
10<body>
<div class="box1" style="margin-top: 30px">
<div class="ovh">
<div class="child1" style="margin-top: 10px">123</div>
<div class="child2">abc</div>
</div>
456
</div>
<div class="box2" >
</body>
上面的例子,没有创建任何新的BFC
,所以所有的box
全部在相同的BFC
中布局,具体如下:
div.box1
与div.box2
这两个box,按照先后顺序,从body
的顶部边缘开始依次布局下来456
这个文本外面,会生成一个anonymous block box
div.ovh
和456 anonymous block box
这两个box,按照先后顺序,从div.box1
的顶部边缘依次布局下来div.child1
与div.child2
这两个box,按照先后顺序,从div.ovh
的顶部边缘开始依次布局下来div.child1
与div.box1
的top margin
会发生合并
如果代码改动一下:1
2
3
4
5
6
7
8
9
10<body>
<div class="box1" style="margin-top: 30px">
<div class="ovh" style="overflow:hidden">
<div class="child1" style="margin-top: 10px">123</div>
<div class="child2">abc</div>
</div>
456
</div>
<div class="box2" >
</body>
- 因为
overflow:hidden
的作用,div.ovh
现在会新建BFC
; div.child1
与div.child2
这两个box,按照先后顺序,从div.ovh
的顶部边缘开始依次布局下来;但此时它俩所属的布局上下文是div.ovh
新建的那个,而不是div.ovh
自身所处的那个;body
div.box1
div.ovh
div.box2
456 anonymouse box
仍然处于相同的BFC
当中- 因为
div.ovh
所新建的BFC
的作用,div.child1
和div.box1
的top margin
不会再发生合并
在BFC
中还有一个特性,就是box
的左边的边缘,会与containing block
的左边边缘对齐。这样的话,加上box
的width
初始值始终是auto
,就会让box
形成一个水平方向上充满containing block
的布局效果,这就是box
所谓的流体特性。 注意width: auto
与width: 100%
不是一样的,这就是为啥有的时候子元素设置width: 100%
,反而不能自动充满父元素,可能会超出的原因,比如这个代码:1
2
3<div style="width: 300px; padding: 0 30px;">
<div style="width: 100%"></div>
</div>
父级div最终的宽度会变为360,而不是300,因为子div的宽度设置为100%,所以它的宽度也是300,一下子就把父div撑开了,width: 100%
导致子div失去了自动在水平方向充满父div内容区的流体效果。
BFC
有两个布局要点:
box
的顶部边缘对齐containing block
的顶部。box
的左边对齐containing block
的左边。
float box
inline block box
和absolutely positioned box
这类BFC
的box
会导致它失去在水平方向上的流体布局特性,宽度不再自动充满containing block
的宽度,而是收缩到自己的内容实际填充宽度,这就是所谓的包裹性。
inline formatting context
当一个block-level
的box
里面只有inline box
的时候,它会创建一个inline formatting context
来布局这些inline
内容。inline box
一定是在一个IFC
中布局的,它的children
也会跟它一起布局在相同的IFC
当中。
在IFC
当中,inline box
也是从containing block
的顶部开始布局的。水平方向上的margin
border
padding
都会起作用。这些inline box
在垂直方向上有多种对齐方式,可能是按照box
的顶部边缘对齐、可能是底部边缘对齐、也可能是按照文本基线对齐。在IFC
当中,每一行都是一个不可见矩形区域,来包裹这些inline box
,这个矩形区域称为line box
。
line box
默认的宽度是跟containing block
的宽度一样的,但是当line box
遇到float
元素后,line box
会自动收缩自己的宽度,以便line box
不跟float
发生重叠,形成了line box
环绕float box
的效果。line box
的高度根据line-height
属性的计算规则有关,需要学习其它规范内容。
line box
的高度始终大于它里面任意一个inline box
的高度,甚至会出现line box
的高度比它里面最高的box
的高度还要高。当某个box
的高度低于line box
的高度时,这个box
在这一行内垂直方向上的对齐方式是由vertical-align
属性决定的。当inline boxs
用一个line box
排不下的时候,它们会被分割到垂直堆叠的多个line box
中来布局,因为line box
的宽度最大就是containing block
的宽度。一个inline formatting context
实际上由垂直堆叠的多个line box
布局出来的,这些堆叠的line box
不会发生重叠,同时它们之间在默认情况下不会出现间隙。
同一个IFC
当中的line box
,高度不一定相同,比如某个line box
内有张图片,另一个没有。默认情况下line box
的左边与containing block
的左边对齐,line box
的右边与containing block
的右边对齐;但是当float
出现后,部分line box
的宽度会变窄,以便不与float box
发生重叠。line box
与float box
的这种环绕行为,以及清除浮动
的行为,都只会在同一个BFC
当中发生。 float
会让box
脱离BFC
或IFC
的这种文档流布局,但是不会脱离BFC
,所以float
与相应的line box
还是处于同一个BFC
,它们才能发生一些特定行为。 假如不想让某个box
里面的line box
与这个box
所在的BFC
中的float box
发生特定行为,只需要把这个box
变为BFC
即可。
当一个line box
内所有的inline box
的总宽度小于line box
的宽度时,这些inline box
在水平方向上的对齐方式,就由text-align
属性来决定。如果某个inline box
在一个line box
内排不下,它就会被分割为多个box
,然后布局到下一个line box
当中。但是当一个inline box
不允许被分割时,比如设置了white-space: nowrap | pre
,那这个inline box
就会溢出当前它所在的line box
。
当一个inline box
被分割时,margin
padding
border
都不会有分割的视觉效果,就是margin padding border
看起来仍然会是连续的。如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style type="text/css">
.container {
width: 150px;
margin: 0 auto;
background: #ccc;
}
.target {
border: 10px solid red;
margin: 0 20px;
padding: 0 20px;
background: blue;
color: #fff;
border-top: 0;
border-bottom: 0;
}
</style>
</head>
<body>
<div class="container">
Several <em class="target">emphasized words</em> appear
<strong>in this</strong> sentence, dear.</div>
</body>
</html>
其实就是说虽然inline box
被分割为多个box
了,但是它们从整体上是满足一个box
的box model
的,所以把它当成一个box
看待即可。
line box
是IFC
为了包含inline-level
的内容创建的。
Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them
不含文本,没有preserved white space
,没有带marign
、或带border
或带padding
的内联元素,没有其它未脱离文档流的布局内容(比如图片、内联block,内联table),并且没有换行符的此类line box
,必须被当做是zero-height line box
(0高的line box)来处理,这是为了这些line box
中包含的元素定位的目的。比如某个div
内包含一个inline-block
的div
,此时这个子div所属于的line box
肯定不是zero-height line box
,但是如果把inline-block
的div
,设置了绝对定位之后呢?它就会脱离它原来所在的line box
,原来的line box
就变为zero-height line box
了,这个line box
虽然高度为0,但是是对于子div
的定位是有作用的。但是另一方面,zero-height line box
在其它的场合中,必须被当成不存在的line box
处理。