理解CSS2.1中的盒子模型

经典知识,学习细节。

box dimensions

csselements生成boxbox是由content区域以及padding border margin区域组成的,其中padding border margin不一定每个box都有。它们的包含关系如图所示:

每个box最里面的矩形区域是contentcontent外面包裹的第一层环形矩形区域是paddingpadding外面包裹的第一层环形矩形区域是borderborder外面包裹的第一层环形矩形区域是margin

其它概念的含义如下:

  • content edge 指的是content的矩形内容区域的四条边
  • padding edge 指的是padding区域外侧的四条边,当padding:0的时候,padding edgecontent edge重合
  • border edge 指的是border区域外侧的四条边,当border:none的时候,border edgepadding edge重合
  • margin edge 指的是margin区域外侧的四条边,当margin:0的时候,margin edgeborder edge重合
  • content box 就是content
  • padding box 指的是padding edge包裹的矩形区域,等于content + padding
  • border box 指的是border edge包裹的矩形区域,等于content + padding + border
  • margin box 指的是margin edge包裹的矩形区域,等于content + padding + border + margin

content padding border margin分上下左右四个部分,比如LM RM 分别指的是left margin rigth margin
content edge, padding edge, border edge, margin edg 当然也分4条边。

boxbackground是在content border padding这三个区域生效的,margin区域的background始终是透明的。

margin properties

margin有关的properties一共有5个,用来定义margin的大小。分别是:

  • margin-top 定义left margin的大小
  • margin-right 定义right margin的大小
  • margin-bottom 定义bottom margin的大小
  • margin-left 定义left margin的大小
  • margin 前面四个的简写,可按照top right bottom left的顺序,在一个属性中一次性定义1-4个margin property

<margin-width>

以上5个属性,全部都依赖<margin-width>这个value type,它的定义如下:

1
<margin-width> = <length> | <percentage> | auto

什么是value type以及<length><percentage>这两个value type在哪定义的,都可前往css-values这个文档学习。

简单来说,<length>一般就是px em rem这些值,属于绝对值,写多少就是多;<percentage>是指百分比或浮点数作为值,属于相对值,它要依赖于box所在的containing blockwidth来计算最终的margin值。下面的值,都是合法的<margin-width>:

1
2
3
margin: 20px;
margin: 20%;
margin: auto;

需要着重说明的是,上下左右四个margin都可以设置<percentage>的值,然后不管是谁,这个百分比值,都是相对于当前boxcontaining blockwidth来计算的。比如:

1
2
3
<div style="width: 400px; height: 300px">
<div style="margin: 20%"></div>
</div>

上面这个例子中,最终内层div上下左右的margin值就是: 20% * 400px = 80px,不能想当然的认为左右margin是根据containing block width计算,而上下margin是根据containing block height计算。
另外,百分比单位是相对containing blockwidth计算的,如果containing block的宽度是由当前box撑起来的话,那么当前box的百分比值计算出来就是undefined。上面的例子之所以百分比单位有效,是因为第一层div的width是明确指定的,不是靠第二层div的box填充起来的。这个要点可以帮助你分析某些时候为什么百分比单位会无效。

auto需要前往其它内容学习,才能知道它的计算方式。另外margin值还能设置负的值,也就是负的<length>是允许的。这两个要点需要学习其它内容才能明确。

margin-top margin-bottom这两个property的定义

定义: <margin-width> | inherit
初始值: 0
应用于: 除了table display types以外的所有元素,table-caption, table, inline-table这3个table display type的元素可以使用margin
是否可被继承: no
百分比计算: 相对containing blockwidth来计算

这两个属性定义垂直方向的margin,它们对display: inlnenon replaced elements无效。

margin-left margin-rigth这两个property的定义

定义: <margin-width> | inherit
初始值: 0
应用于: 除了table display types以外的所有元素,但是table-caption, table, inline-table这3个table display type的元素可以使用margin
是否可被继承: no
百分比计算: 相对containing blockwidth来计算

这两个属性定义水平方向的margin。

margin的定义

定义: <margin-width>{1,4} | inherit
初始值: 参考各个单独的属性定义
应用于: 除了table display types以外的所有元素,但是table-caption, table, inline-table这3个table display type的元素可以使用margin
是否可被继承: no
百分比计算: 参考各个单独的属性定义

这个属性可以一次性定义4个方向的margin,遵循上右下左的顺序。<margin-width>{1,4}代表<margin-width>可以重复1到4次。

Collapsing margins 外边距合并

首先只有垂直外边距才会合并,水平外边距始终不会发生合并,所以谈论外边距合并,说的仅仅是垂直外边距。在css里面,相邻的垂直外边距会发生合并,从两个外边距合并成一个,合并完那个外边距又称为collapsed margin

有两个场景即使满足外边距合并的条件也一定不会合并:

  • root elmentmargin不会合并,root elementhtml这个元素
  • 如果一个有clearancebox上下外边距相邻,那这个box会与它后面相邻的其它boxmargin发生合并,但是它们合并的最终collapsed margin,不会再与parent boxmargin-bottom发生合并。

第二种情况举例如下:

1
2
3
4
5
6
7
<div style="border: 1px solid #ccc;width: 500px; margin: 0 auto;">
<div class="container" style="margin: 10px 0;background-color: rebeccapurple;">
<div style="float:left;width: 50px; height: 50px; background-color: #ccc;"></div>
<div class="clearance" style="margin: 30px 0;clear: both;height:0;"></div>
<div class="sibling" style="margin: 30px auto;height:0;"></div>
</div>
</div>

效果如下:

在这个例子中,div.containerparent box,而div.clearance是一个有设置清除浮动的box(清除浮动会导致该box的margin top上方增加clearance),且上下边距相邻,它会自身进行margin合并,然后再与div.siblingbox发生合并,但是它们俩合并完之后的margin并没有跟div.containermargin-bottom: 10px发生合并,不然从上面的效果图中看到的边框距离颜色内容区域的距离,就不会是那么点距离。

外边距合并的条件为:

  • 要被合并的外边距对应的box,不能脱离普通文档流,且它们的box必须位于同一个block formatting context当中
  • 不能有line box(行框)、paddingborder以及clearance在两个外边距之间,否则它们不会合并

有以下几种外边距合并的形式:

  • parent boxtop margin与它第一个child boxtop margin发生合并
  • boxbottom margin会和它相邻的下一个元素的boxtop margin发生合并
  • parent boxbottom margin与它最后一个chilld boxbottom margin发生合并
  • 同一个boxtop marginbottom marginmin-height为0、height为0或者是auto、并且没有content的时候,也会发生合并;说白了同一个box发生合并,则它的border box高度必须为0,不然top marginbottom margin碰不到

外边距是两两合并,然后得到一个新的外边距,新的外边距如果与其它box的外边距继续满足合并条件则还会发生合并;通过上面的合并形式,可以看到,外边距可以发生在父子元素之间、相邻元素之间、元素自身,所有在满足条件的情况下,一次完整的外边距合并,可能包含多个box,而且会跨越box tree多层结构。

另外一种合并形式总结:

  • Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
  • Margins of elements that establish new block formatting contexts (such as floats and elements with ‘overflow’ other than ‘visible’) do not collapse with their in-flow children.
  • Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
  • Margins of inline-block boxes do not collapse (not even with their in-flow children).
    上面这4条英文规范的说的是同一个情况,就是外边距只能在同一个BFC中发生,当两个外边距相邻,但是属于两个不同的BFC,则不会发生合并
  • The bottom margin of an in-flow block-level element always collapses with the top margin of its next in-flow block-level sibling, unless that sibling has clearance.
    文档流中block-level元素的bottom margin始终会跟文档流中下一个block-level元素的top margin发生合并,除非下一个元素通过清除浮动产生了clearance
  • The top margin of an in-flow block element collapses with its first in-flow block-level child’s top margin if the element has no top border, no top padding, and the child has no clearance.
    文档流中的block-level元素的top margin会跟它第一个在文档流中的block-level子元素的top margin发生合并,前提是这个父元素没有设置top border、没有设置top padding、且子元素没有通过清除浮动产生clearance
  • The bottom margin of an in-flow block box with a ‘height’ of ‘auto’ and a ‘min-height’ of zero collapses with its last in-flow block-level child’s bottom margin if the box has no bottom padding and no bottom border and the child’s bottom margin does not collapse with a top margin that has clearance.
    文档流中block-level元素的bottom margin,会跟它最后一个在文档流中的block-level子元素的bottom margin发生合并,前提是这个父元素自己没有设置bottom padding、没有设置bottom border、并且这个子元素的bottom margin没有跟它之前的有clearancetop margin合并过。 同时父元素如果设置非0的heightmin-height,也可能会导致前面的情况无法合并,因为heightmin-height可能会导致最后一个在文档流中的block-level子元素的bottom margin接触不到parent boxbottom margin
  • A box’s own margins collapse if the ‘min-height’ property is zero, and it has neither top or bottom borders nor top or bottom padding, and it has a ‘height’ of either 0 or ‘auto’, and it does not contain a line box, and all of its in-flow children’s margins (if any) collapse.
    一个元素的上下边距发生合并,前提是:它的min-height是0、且它没有设置上下的border or padding、且它的height0 or auto、且它里面没有line box、如果有childrenchildren也必须全都自身发生了合并。

要点

前面介绍的外边距合并相当复杂,总结几个要点如下:

  1. root elemenntmargin不会合并
  2. 外边距合并只发生在同一个BFC当中
  3. 两个要合并的外边距之间如果有line boxpaddingborderclearance,则不会合并
  4. 外边距有4种合并情况:第一,parenttop margin与第一个childtop margin合并;第二,boxbottom margin与相邻的下一个boxtop margin合并;第三,parentbottom margin与最后一个childbottom margin合并;第四,同一个box的上下外边距发生合并。

外边距合并的计算逻辑:

如果要合并的外边距都是正的,就取它们的最大值。如果里面有负的外边距,就先取所有正的外边距的最大值,然后减去所有负的外边距的绝对值的最大值。如果都是负的,就是0减去所有外边距的绝对值的最大值。

padding properties

padding有关的properties一共有5个,用来定义padding的大小。分别是:

  • padding-top 定义left padding的大小
  • padding-right 定义right padding的大小
  • padding-bottom 定义bottom padding的大小
  • padding-left 定义left padding的大小
  • padding 前面四个的简写,可按照top right bottom left的顺序,在一个属性中一次性定义1-4个padding property

<padding-width>

以上5个属性,全部都依赖<padding-width>这个value type,它的定义如下:

1
<padding-width> = <length> | <percentage> | auto

下面的值,都是合法的<padding-width>:

1
2
3
padding: 20px;
padding: 20%;
padding: auto;

margin一样,上下左右四个padding都可以设置<percentage>的值,然后不管是谁,这个百分比值,都是相对于当前boxcontaining blockwidth来计算的。 containing block有一个专门的定义,在后面讲position的博客中会专门介绍。

padding不能设置负的值。

padding-top pading-bottom padding-left padding-right的定义

定义: <padding-width> | inherit
初始值: 0
应用于: 除了displaytable-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column的所有元素
是否可被继承: no
百分比计算: 相对containing blockwidth来计算

padding的定义

定义: <padding-width>{1,4} | inherit
初始值: 参考各个单独的属性定义
应用于: 除了displaytable-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column的所有元素
是否可被继承: no
百分比计算: 参考各个单独的属性定义

这个属性可以一次性定义4个方向的padding,遵循上右下左的顺序。<padding-width>{1,4}代表<padding-width>可以重复1到4次。

border properties

这个就比较简单了, 参考各种教程学习都行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
border
border-top
border-left
border-right
border-bottom

border-color
border-top-color
border-right-color
border-bottom-color
border-left-color

border-width
border-left-width
border-right-width
border-top-width
border-bottom-width

border-style
border-top-style
border-bottom-style
border-left-style
border-right-style

一共有以上这么多个border属性。