10 Visual formatting model details

10.1 containing block 的定义

在一些场景下,一个元素的盒子的尺寸和位置是基于该元素的 containing block(包含块, 后文中简称 CB) 来计算的,元素的 containing block 是一个矩形区域,定义如下:

  1. 根元素的 containing block 叫做 initial containing block (初始化包含块, 后文中简称 ICB) 。对于 continuous media (连续媒体,我们最常见的电脑中的浏览器就是其中的一种),IBC 的尺寸和 viewport 一致,而且被固定在画布的起点位置;对于 paged media IBC 就是页面区域。 IBC 的 direction 的属性值与根元素一致。

  2. 除根元素外的其他元素,如果元素的 positionrelative, static, 那么,元素的 CB 是由该元素的最近 block container 类型的或者是新建了 formating context(格式化上下文)的父元素的 content box 组成。

  3. 如果元素的 positionfixed, 那么该元素的 CB 在 continuous media 中是由 viewport 生成,而在 paged media 中由页面区域生成。

  4. 如果元素的 positionabsolute, 那么该元素的 CB 由最近的有定位(position: relative|absolute|fixed)的祖先元素生成。并且遵守如下规则:

    1. 如果祖先元素是内联元素(display: inline|inline-block), 那么 CB 是该祖先元素生成的第一个和最后一个内联盒子的 padding box 的边界区域。 在 CSS 2.2 规范中,如果该祖先元素被分成了多行,那么 CB 就是未定义的。

    2. 其他情况, CB 由祖先元素的 padding box 组成。

      如果找不到有定位的祖先元素, CB 就是 ICB.

Note: 本文不对 paged media 的情况做更多解释,因为我也搞不太懂,先放弃,之后再研究。

如下一个没有定位的文档,他的 CB 如下表:

    <!DOCTYPE html pubic "-//W3C//DTD HTML 4.01//EN">
    <html>
        <head>
            <title>Illustration of containing blocks</title>
        </head>
        <body id="body">
            <div id="div1">
                <p id="p1">This is text in the first paragraph...</p>
                <p id="p2">This is text
                    <em id="em1"> in the <strong id="strong1">second</strong> paragraph.</em>
                </p>
            </div>
        </body>
    </html>
For box generated by CB is established by
html initial CB
body html
div1 body
p1 div1
p2 div1
em1 p2
strong1 p2

如果这时候给 div1 一个 position:absolute, 那么 div1 的 CB 就不是 body 了,变成了 ICB, 以为 div1 的父元素中没有一个有定位的。

再接下来,我们设置如下样式:

    #div1{
        position: absolute;
        top: 50%;
        left: 50%;
    }

    #em1{
        position: absolute;
        top: 50%;
        left: 50%;
    }

这时候 CB 对应的关系如下表:

For box generated by CB is established by
html initial CB
body html
div1 initial CB
p1 div1
p2 div1
em1 div1
strong1 em1

10.2 Content Width: the width property (Content box 的宽)

width

    width: length | percentage | auto | inherit

默认初始值: auto

对所有除 non-replaced 行内元素、table row 、row group 元素都生效

不可继承

百分值是基于元素的包含块来计算的

Computed Value 为用户指定的 percentage | auto | 绝对值

width 默认情况下指定了 content box 的宽度。

width 属性对 non-replaced 的内联元素不生效, non-replaced 的行内元素的 width 是渲染后元素内容的 width。 Recall that inline boxes flow into line box. The width of line boxes is given by the containing block, but may be shorted by the presence of float.

没翻译的部分没太理解

为 width 指定负值是非法的。

10.3 Calculating widths and margins

元素的 width, margin-left, margin-right, left, right 真正用于布局渲染的值是依赖于该元素生成的盒子类型的。原则上,Used value 和 Computed value 是相等的,width 为 auto 的将会有一个适当的直来替换,width 为百分比的,那么 width 值将会基于 CB 的 width 来计算。但是以下情况需要另当别论:

  1. inline, non-replace 元素; 比如:span em i 等行内元素

  2. inline, replaced 元素;比如:img

  3. 处于正常文档流中的 non-replaced 块级元素;比如:div

  4. 处于正常文档流中的 replaced 块级元素;比如:img{display: block;}

  5. floating, non-replaced 元素;p{float: left;}

  6. floating, replaced 元素;img{float: left;}

  7. 绝对定位的,non-replaced 元素;

  8. 绝对定位的,replaced 元素;

  9. 处于正常文档流中的 inline-block non-replaced 元素;

  10. 处于正常文档流中的 inline-block replaced 元素;比如:textarea input

10.3.1 inline, non-replaced elements

width 对于这样的元素不生效,而对于水平方向的 margin-left: auto | margin-right: auto 的 computed value 为 0.

对于行内元素,垂直方向上的 margin 是不生效的

    <span style="width: 100px; background: #678; margin-right: auto;">我爱中国</span><span>我是中国人</span>

效果:

我爱中国我是中国人

width: 100 没有生效, margin-right: auto 也没有产生效果。

但是水平方向的 margin 是对 inline 元素是有效果的。

    <span style="width: 100px; background: #678; margin-right: 20px;">我爱中国</span><span>我是中国人</span>

效果:

我爱中国我是中国人

10.3.2 inline, replaced elements

对于这类型的元素,margin-left, margin-right 属性的 auto 值的 computed value 是 0.

如果 height 和 width 的 computed value 是 auto, 并且元素具有固有的宽度,那么 width 的 used value 就是元素固有的 width

一张实际宽高为 180x180 的图片,不指定宽度或者指定为 auto, 那么最终用于渲染的宽度将是 180

    <img src='../inline_replaced_element.jpg'>

效果如下:

如果 height 和 width 的 computed value 都是 auto, 但是元素没有固有的尺寸,但是有一个固有的宽高比 ratio 和固有的高度 height;又或者 width 的 computed value 为 auto, height 有一个绝对值,而且元素还有一个固定的宽高比 ratio,那么 width 的计算公式如下:

width = height * ratio

还是上面的图片 ratio = 1, 当指定 height: 100px; 那么 width = height * 1

    <img src='../inline_replaced_element.jpg' style='height: 100px;'>

效果如下:

如果 height 和 width 的 computed value 都是 auto, 并且元素有固有的比例,但是都没有固有的宽高,那么在 CSS2 中 width 的 used value 是未定义的。但是建议,在这种场景下,如果元素的 containing block 自身的 width 不依赖于当前元素,那么该元素 width 的 used value 强制与正常文档流中 block-level, non-replaced 类型的元素的 width 一致。

这种情况不好举例

如果 width 的 computed value 是 auto, 还不满足已上的任一条件,那么 width 的 used value 被强制设置为 300px, 如果 300px 对于某些设备太宽,那么用户代理应该按照 2:1 的比例为设置该设备下所能用的最大的宽高

canvas 在不设置宽高的情况下默认的大小就是 300x150

    <canvas style="background: #ddd">

效果如下:

10.3.3 Block-level, non-replaced elements in normal flow

这种情况下,元素水平方向的属性的 used value 满足以下公式:

'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block

如果 width 不是 auto (width 默认值是 auto, 也就是说我们指定了 width 的值),并且 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 任何值不为 auto 的 'margin-left' 或者 'margin-right' 的和大于 containing block 的宽度 (content box 的 width),那么为了满足公式,任何值为 auto 的 margin-left 和 margin-right 的 used value 将被置为 0. 其实在 chrome 的实现中并不完全如此,水平方向的 margin 值将收 direction 的影响。

margin 的默认值为 0,要让 margin 的值为 auto 需手动设置

我们假设 containing block 的宽度为 700,如下代码:

    <div id="outer-div" style="
        padding: 30px 30px 30px 220px;
        background: #ad9;
        direction: ltr;
        width: 700px;">

            <div id="inner-div" style="
                width: 100px;
                height: 100px;
                margin: auto auto auto 0px;
                padding: 10px 310px;
                border: 10px solid;
                background: #340;
                background-clip: content-box;">
            </div>

      </div>

inner-div width:100 + padding-left:310 + padding-right:310 + border-left-width:10 + border-right-width:10 + margin-left:0 = 740 > #outer-div.width:700

在 chrome 中 margin-right 的值不是 0 而是 700 - 740 = -40

效果如下:

如果 containing block direction: rtl, 那么就该是 margin-left 为 -40 了。

如果以上所有属性的 computed value 都不是 auto, 那么 margin-left 和 margin-right 中某一个属性的 used value 将与他们的 computed value 不一致了,如果 containing block 的 direction 为 ltr, 那么将忽略 margin-right 的值,用能是公式成立的值来取代;如果 containing block 的 direction 为 rtl, margin-left 指定的值被忽略,used value 被使公式成立的值取代。

    <div id="outer-div" style="
        padding: 30px 30px 30px 220px;
        background: #ad9;
        direction: ltr;
        width: 700px;">

            <div id="inner-div" style="
                width: 100px;
                height: 100px;
                margin: 10px;
                padding: 10px;
                border: 10px solid;
                background: #340;
                background-clip: content-box;">
            </div>

      </div>

containing block direction: ltr; 所以 margin-right: 10px 被忽略, 渲染后 margin-right = containing block.width:700 - width:100 - margin-left: 10 - padding-left:10 - padding-right:10 - border-left-width:10 - border-right-width:10

效果如下:

如果确实有属性的 computed value 为 auto, 那么最终该属性的 used value 依旧是让等式成立的值。

如果 width 的 computed value 为 auto, 那么其他 computed value 为 auto 的属性的 used value 将是 0, 而 width 的 used value 是让等式成立的值。

如果 margin-right 和 margin-left 的 computed value 都是 auto, 那么它们的 used value 将相等,这样就能够让元素在 containing block 内水平居中。

Note: margin: 0 auto; 只能让元素在 containing block 中水平居中。

    <div id="outer-div" style="
        padding: 30px 30px 30px 220px;
        background: #ad9;
        direction: ltr;
        width: 700px;">

            <div id="inner-div" style="
                width: 100px;
                height: 100px;
                margin: auto;
                padding: 10px;
                border: 10px solid;
                background: #340;
                background-clip: content-box;">
            </div>

      </div>

效果如下:

10.3.4 Block-level, replaced elements in normal flow

这类型元素的 width 的 used value 与 inline, replaced 元素的算法一致,而 margin-left, margin-right 的 used value 的算法与 block-level, non-replaced 元素一致。

  <div style="width: 700px; padding: 30px; background: #adb">
    <img src="http://p6.sinaimg.cn/1642346981/180/92211342365188" alt="" style="display: block; margin: auto">
  </div>

效果如下:

元素的 width 为图片固有的宽度;margin-right, margin-left 的 used value 为 containing block.width - width:180 - padding-left:0 - padding-right:0 - border-left-width:0 - border-right-width

10.3.5 Floating, non-replaced elements

在这种情况下:

如果 margin-left, margin-right 的 computed value 是 auto, 那么 used value 为 0;

如果 width 是 computed value 是 auto, 那么 used value 为 ‘shrink-to-fit’ width (也就是 收缩后适合内容的宽度);

shrink-to-fit width 的计算方法与使用 table 布局是计算单元格的 width 类似。 大体上就是:calculate the preferred width by formatting content without breaking lines other than where explicit line breaks occur, and also calculate the preferred minimum width e.g., by trying all possible lien breaks. (没太理解这句话,特别是 explicit line breaks,其实就是想不到合适的场景,大概写下我的理解:通过不折行的渲染内容,然后计算一个合适的 width). CSS2.2 中没有定义明确的算法。 最后,计算元素 available width (可用的 width: 也就是 shrink-to-fit 的上限 width), 算法是: containing block.width 减去 margin-left, border-left-width, padding-left, padding-right, border-right-width, margin-right 的 used value 和 滚动条的宽度。

所以 shrink-to-fit width = min(max(preferred minimum width, available width), preferred width).

当然如果我们通过 css 规则指定了 width, margin-left, margin-right, 那么就直接使用我们指定的值。

10.3.6 Floating, replaced elements

如果 margin-left, margin-right 的 computed value 是 auto, 那么 used value 将会是 0;

width 的 used value 的算法和 inline, replaced 场景一致。

10.3.7 Absolutely positioned, non-replaced elements

results matching ""

    No results matching ""