从CSS视觉格式化模型看盒模型

学习CSS的时候,大部分时间只是背属性,写写Demo,并没有真正系统地研究一些基础的原理,学到的还只是一些零碎的知识点。趁着有时间,通过查看w3c的官方文档,希望能从视觉格式化模型(Visual formatting model)的角度深入了解。本篇整理几个重要的知识点:盒模型,包含块,BFC,IFC。

盒模型(Box model)

margin edge = outer edge

content edge = inner edge

包含块(Containing blocks)

许多盒的定位和尺寸都取决于一个矩形边界,即包含块。通常,一个盒子(box)扮演它的后代元素盒子的包含块。

如何得到确定包含块?如下:

  1. 根元素html,初始包含块,由viewport决定。
  2. positionstaticrelative,最近的祖先块容器盒的content box。
  3. positionabsolute,取决于最近的定位祖元素,如果是内联元素,包含块为包裹第一和最后一个内联盒padding box的边界,若该内联元素分成多行,则包含块为undefined;否则为祖元素padding box。
  4. positionfixed,viewport。
  5. 其他,初始包含块。

块级元素和块盒

block-level element/block box

元素的display决定了盒子的类型,进一步决定其在format model中的行为。

概念

区分:块级盒(Block-level box),块容器盒(Block container box),块盒(Block box)

1
display: block | list-item | table

以上为块级元素,产生块级盒,参与BFC

除了table box和替换元素(replaced element),块级盒同时也是块容器盒。块级盒+块容器盒 = 块盒。

块容器盒可以只包含块级盒,或者创建IFC从而只含内联级盒(inline-level box)。块容器盒除了可以是块级盒,还可以是非替换inline-block/table-cell。

辨析:块级盒是从自身的角度,作为一部分参与BFC,而块容器盒从元素内部的角度,作为容器。

匿名块盒

  • some text周围有匿名块盒。当块容器盒内有块级盒,则强制全变为块级盒。
1
2
3
4
<div>
some text
<p>other text</p>
</div>
  • 当内联盒内含有块级盒,内联盒会在块级盒周围分成两部分,被匿名块盒包裹。

一般内联盒中不会包含块级盒,可以不用考虑

内联元素和内联盒

inline-level element/inline box

概念

区分:内联级盒(inline-level box),内联盒(inline box)

1
display: inline | inline-block | inline-table

以上为内联级元素,生成内联级盒,参与IFC

内联盒:本身是内联级盒,并且内容也参与这个IFC,包括非替换且display: inline元素。

替换内联级元素、inline-block、table-cell不是内联盒,为atomic inline-level box。它们自身被当成内联级盒处理,但内部还是以块盒的形式format。

匿名内联盒

任意直接处于块容器盒中的text都被视为匿名内联元素,继承父级块盒的可继承属性。

1
<p>some <em>emphasized</em> text </p>

在正常流/文档流(Normal flow),块级盒参与block formatting context,内联级盒参与inline formatting context

BFC

创建

  • 根元素
  • 浮动
  • 绝对定位
  • 块级元素overflow不为visiable
  • displayinline-block table-cell table-caption

A block formatting context contains everything inside of the element creating it that is not also inside a descendant element that creates a new block formatting context.

当一个元素创建了一个BFC,它的后代元素如果不在另一个创建新BFC的后代元素内部,则该后代元素被此BFC包含。解释起来比较拗口。。

1
2
3
4
5
6
<div class="bfc1">
<p class="p1"></p>
<div class="bfc2">
<p class="p2"></p>
</div>
</div>
1
2
3
.bfc1, .bfc2 {
position: absolute;
}

p1在bfc1内,而p2已有祖元素bfc2创建了新BFC,则p2不在bfc1中,其实就是子元素会包含在最近的祖元素创建的BFC中。

特性

创建一块独立的渲染区域,规定内部块级盒布局方式

  • BFC中盒子从其包含块顶部开始,依次竖直排布。
  • 竖直间距由margin决定,同一BFC中相邻的块级盒子间的竖直外边距发生合并。
  • 每个盒子margin box左边界与包含块左边缘接触,(即使有浮动元素(floats)存在,此时盒子的行盒由于floats会收缩,除非盒子形成新BFC)。
  • 计算高度时,浮动元素也加入
  • BFC不与浮动元素重叠
  • BFC内部元素不会影响外部

应用

  • 两栏自适应
  • 清除浮动
  • 防止margin合并

IFC

特性

IFC中的内联级盒从包含块顶端开始,依次水平放置。

水平方向的margin/border/padding在放置时会被考虑,宽度由内容决定,而竖直方向的对齐方式由vertical-align决定,margin/border/padding不会影响高度和布局,但会有视觉效果。包含这些内联盒子形成的矩形区域组成行盒(line box)。

行盒的width由包含块及浮动的存在决定,一般行盒左右紧贴包含块两端,当存在浮动会缩小(如左浮动,则行盒左边缘与浮动元素右边紧贴)。

行盒的height由计算”line-height”给出,具体计算方法见下方。

当内联级盒总宽度小于行盒的width,水平的分布由text-align决定;总宽度超出时会在垂直方向形成新的行盒无缝堆叠,发生超出位置的内联盒一般会分割到多个行盒,除非是单个字符、设置work-breakwhite-space等,此时overflow。

当内联盒分割时,分割处不会产生新的margin/padding/border。

“line-height”的计算

  1. 计算行盒中每个内联级盒的高度。对于替换元素,inline-block,inline-table,取margin box高度;对于内联盒(span等)和字符,取line-light

  2. 各个内联级盒有自己的基线,非替换inline元素基线为字体基线,替换元素基线为margin box底端,inline-block的基线是它最后一个在文档流中的行盒的基线,当找不到这个行盒,或者overflow不为visible,此时基线为margin box底端。内联级盒以其参照物的基线为基准,根据vertical-align来排布自己的基线,参照物为内联父元素或者块容器父元素的支柱(strut,假设存在的一个宽度0,font、line-height与容器一致的内联盒,提供基线作为参照)。

  3. 行盒的高度为最高盒的顶端到最低盒的底部(比较时包括支柱)。

i的基线参照span,可以看做参照字符aa基线;span基线参照div的支柱,可以看成参照字符ccc基线。

参考

  1. W3-CSS2.1-8 Box model

  2. w3-CSS2.1-9 Visual formatting model

  3. W3-CSS2.1-10 Visual formatting model details

  4. MDN-Block_formatting_context

推荐文章