CSS学习笔记——视觉格式化模型 visual formatting model

CSS 视觉格式化模型(visual formatting model)是用来处理文档并将它显示在视觉媒体上的机制。他有一套既定的规则(也就是W3C规范),规定了浏览器该怎么处理每一个盒子。以下内容翻译自W3C官方文档,其中加上了自己的一些理解。相关链接:https://www.w3.org/TR/CSS2/visuren.html#block-boxes。

1、可替换元素(Replaced element)

下面的一段话引自MDN:

典型的可替换元素有 <img>、 <object>、 <video> 和 表单元素,如<textarea>、 <input> 。 某些元素只在一些特殊情况下表现为可替换元素,例如 <audio> 和 <canvas> 。 通过 CSS content 属性来插入的对象 被称作 匿名可替换元素(anonymous replaced elements)。

简单的讲,可替换元素就是在源代码中不能直接看出他的要显示的内容的元素。比如<img>元素要通过src属性告诉浏览器显示什么内容的图片。

2、不可替换元素(non-replaced element)

相对的,不是替换元素的元素就是不可替换元素。

3、块级元素和块盒(block-level elements and block boxes)

块级元素(block-level elements)在源文档中格式化为可见的块。“display”属性值为“block”、“list-item”、“table”的元素是块级元素。

一般情况下一个块级元素产生一个块级盒(block-level box),称为主块级盒(principal block-level box),主块级盒用来包含后代盒(descendant boxes )以及后代盒中的内容。主块级盒可以参与任何定位方案(positioning scheme)。

Tips:CSS2.1中的positioning scheme:常规流、浮动、绝对定位。

有些块级元素除了产生主块级盒,也可能产生额外的盒(additional boxes),比如display属性值为“list-item”的元素。产生的额外的盒用来包含项目符号(可以用list-style-position属性设置额外的盒是放在主块级盒内还是外)。

主块级盒参与块级格式上下文(Block formatting context)的布局。

除了表格盒(table boxes)和可替换元素盒外,一个块级盒也可以是一个块容器盒(block container box)。块容器盒有两个功能(只能同时具备一种功能):

1、只用来包含其他块级盒;

2、创建一个行内格式化上下文(inline formatting context),行内格式化上下文只用来包含行内级盒(inline-level boxes)。

display为inline-block的元素不能嵌套block-level element,例子如下:

 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
<HEAD>
<TITLE>Example of inline flow on several lines</TITLE>
<STYLE type="text/css">
*{
padding:0;
margin:0;
}
#inlineblock{
background-color: red;
display:inline-block;
margin:30px;
}
#block{
background-color: blue;
display:block;
margin:30px;
}
div{
background-color: yellow;
width:50px;
height:50px;
}
</STYLE>
</HEAD>
<BODY>
<p id="p1">This value causes an element to generate an <span id="block">inline-level block container</span>. The inside of an inline-block is formatted as a block box, and the element itself is formatted as an atomic inline-level box.This value causes an element <span id="inlineblock">(e.g., LI in HTML)<div></div></span> to generate a principal block box and a marker box. For information about lists and examples of list formatting, please consult the section on lists.The 'position' and 'float' properties determine which of the CSS 2.1 positioning algorithms is used to calculate the position of a box.</p>
</BODY>
</HTML>

在调试工具中查看代码,看看浏览器是如何处理上面的HTML文档的:

CSS学习笔记——视觉格式化模型 visual formatting model

不是所有的块容器盒都是块级盒。不可替换行内块(non-replaced inline blocks)和不可替换表单元格(non-replaced table cells)是块容器盒,但不是块级盒。如果一个块级盒同时又是一个块容器盒,那么我们可以叫这个块级盒为块盒(block boxes)。

块级盒和块容器盒是两个并列的概念,他们的关系如下图:

CSS学习笔记——视觉格式化模型 visual formatting model

更准确的图示:

CSS学习笔记——视觉格式化模型 visual formatting model

3.1 匿名块盒(Anonymous block boxes)

在下面这样一个文档中:

<div id="niming">
远方不远,就在车轮滚动的那一刻。
<p id="nimingchild">我从海边来,要到大漠去。</p>
要说一个两万里长的故事给你听。
</div>

假设<div>元素和<p>元素的display属性值均为block),<div>元素似乎包含了行内内容和块内容,为了更容易得明确格式化,假设分别有一个匿名块盒(anonymous block box)在“远方不远,就在车轮滚动的那一刻。”和“要说一个两万里长的故事给你听。”的周围。如下图:

CSS学习笔记——视觉格式化模型 visual formatting model

换句话说,如果一个块容器盒(例如上例中<div>元素产生的块级盒)包含一个块级盒(例如上例中的<p>元素产生的块级盒),那么强制让这个块容器盒只包含块级盒。

当一个行内盒(inline box)包含一个处在文档流中的块级盒时,这个行内盒(和他的在同一个行盒[line box]内的祖先)会被这个块级盒(当然还有紧跟这个块级盒,或者被可折叠的空白隔开的,或者是脱离文档流的同胞块级元素[盒])打断拆分成两个盒(即使拆分成的两个盒中有一个为空),这两个盒会分布在这个块级盒的两边。

在断点两边的行盒(line box)分别被放入一个匿名块盒(anonymous block boxes),同时打断行内盒的块级盒会成为这两个匿名块盒的兄弟。当这个行内盒被相对定位影响时,任何的移动效果都会影响这个包含在行内盒里的块级盒。

例子如下:

 p { display: inline;
background-color: rgb(157,206,230);
border: 1px solid black;
}
span { display: block;
background-color: yellow;
}
<P>
This is anonymous text before the SPAN.
<SPAN>This is the content of SPAN.</SPAN>
This is anonymous text after the SPAN.
</P>

结果如下(不用管粉红色的背景):

CSS学习笔记——视觉格式化模型 visual formatting model

例子中P元素包含了一段匿名文本,紧接着是一个块级元素,再接着又是另外一段匿名文本。最终形成的盒子表现得就像是body元素一样,包含第一个匿名块盒,一个SPAN块盒,然后是另外一个匿名块盒。

匿名块盒的属性值,可继承的就继承自包含他的非匿名盒子,不能继承的就是初始值。比如第一个例子中,匿名块盒的字体继承自<div>元素,但是外边距为0。

在上面的例子中给<p>元素添加了边框。可以看到,虽然行内盒被截断了,但<p>元素的边框依然有效,而且边框在截断处不是闭合的,而是开放的(<p>元素形成的是行内盒,在他外面还有包含他的行盒line box,在行盒外面还有包含行盒的匿名块盒)。

当要计算匿名块盒的子元素盒的百分比值时,匿名块盒会被忽略,而取最近的非匿名祖先盒代替。比如,如果匿名块盒包含在一个div中,而匿名块盒的子元素的高度是一个百分比值,该子元素的实际高度需要通过他的包含块的高度来计算,那么就用这个div产生的包含块的高度来计算,而不是匿名块盒的高度。

4、行内级元素和行内盒(Inline-level elements and inline boxes)

行内级元素是那些在源文档中不形成新的内容块,而分布在行中的元素(例如段落里的强调文本,行内图片等)。display属性的值为“inline”、“inline-table”、“inline-block”的元素是行内级元素。

行内级元素产生行内级盒(inline-level boxes),行内级盒参与行内格式化上下文(inline formatting context)的布局。

行内盒(inline box)是一种行内级盒,他的内容参与他包含的行内格化式上下文的布局。display值为“inline”的不可替换元素产生行内盒。不是行内盒的行内级盒叫做原子行内(级)盒(atomic inline-level boxes)。原子行内盒作为一个不可拆分的整体参与行内格式上下文的布局(不能拆分为多个盒跨行显示)。

以上三种盒的关系如下图:

CSS学习笔记——视觉格式化模型 visual formatting model

4.1 匿名行内盒(Anonymous inline boxes)

任何不是包含在行内元素(inline element),而是直接包含在块容器元素(block container element)中的文本,都必须被当做匿名行内元素(anonymous inline element)。

如下HTML代码:

<p>Some <em>emphasized</em> text</p>

<p>元素产生一个块盒,里面包含了几个行内盒。包含“emphasized”的是一个由行内元素<em>产生的行内盒,但是包含“Some”和“text”的行内盒是由块级元素<p>产生的。后者被称作匿名行内盒(anonymous inline boxes),因为没有任何行内级元素与之相关联。

这些匿名行内盒从他们的父级块盒继承可继承的属性,不可继承的属性则使用初始值。比如上例中,匿名行内盒的color继承自<p>元素,但是他的background是transparent。

空白内容根据“white-space”的值进行折叠,不产生任何匿名行内盒。

当格式化表格时将产生更多类型的匿名盒。

5、display 属性

display属性的值有很多:

block | list-item |

inline-block | inline |

table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | none | inherit

初始值(默认值)为:inline

可应用于:所有元素

block:该值使一个元素产生一个块盒(block box)不严谨,准确说应该是产生块级盒

Inline-block:这个值使一个元素产生一个行内级块容器(inline-level block container)。行内块(inline-block)的内部被格式化为一个块盒(block box),这个元素自身被格式化为一个原子行内盒。不严谨,准确说应该是产生行内块级盒

inline:这个值使一个元素产生一个或多个行内盒(inline boxes)

list-item:这个值使一个元素产生一个主块级盒和一个标记盒(marker box)。(表现得就像一个<li>元素一样)

none:这个值使一个元素不出现在格式化结构中(在可视化媒体中,该元素不产生盒也不影响布局)。他的后代元素也不产生任何盒子。这个元素和他的内容被完全地从格式化结构中移除。在他的后代元素中重写display的值不会对已有的行为有影响。

注意:display属性值为“none”的元素不产生任何盒子,即使是不可见的盒子。但是在CSS的机制中可以让一个不可见的元素在格式化结构中产生一个可以影响格式(或者说布局)的透明的盒子。使用visibility属性的hidden值实现。

tableinline-tabletable-row-grouptable-columntable-column-grouptable-header-grouptable-footer-grouptable-rowtable-cell, and table-caption

这些值使一个元素表现得像是表格元素。

虽然display属性的初始值为inline,但是用户代理的默认样式表会重写这个的值。

6、定位方案(positioning schemes)

在CSS 2.1中有三种定位方案:

1、常规流(normal flow) 常规流包含块级盒的块格式化,行内级盒的行内格式化、块级盒和行内级盒的相对定位(relative positioning)。

2、浮动(float) 在浮动模型中,一个盒子首先按照常规流布局,然后将其从常规流中剔除,并尽可能地向左或向右位移。其他盒子的内容沿着浮动元素的一边布局。

3、绝对定位(absolute positioning) 在绝对定位模型中,盒子完全地被从常规流中移除(他不会对其后面的兄弟元素的布局产生任何影响),盒子本身相对于其包含块定位。

假如一个元素是浮动的,或者是绝对定位的,或着本身是根元素(root element),那么称这个元素脱离文档流(常规流,原文是out-of-flow)。

假如一个元素没有脱离文档流,那么称这个元素在文档流内(在常规流内,原文 in-flow)。

元素A的布局流(排版流,原文是the flow of an element A)由元素A和所有在文档流内的元素组成,这里的所有在文档流内的元素必须满足一个条件,就是他们的最近的脱离文档流的祖先元素是A元素。

6. 1 position属性

position属性和float属性决定了CSS2.1 使用哪一种定位算法计算一个盒子的位置。

p属性的值可取:

static | relative | absolute | fixed | inherit

初始值为:static

可用于:所有元素

static:这个值让一个盒子表现为正常盒子,按照常规流布局。top、right、bottom、left属性不可用。

relative: 这个值先让一个盒子的位置按照常规流计算(计算出的位置称为这个盒子在常规流中的位置),然后这个盒子相对于他在常规流中的位置位移。假如盒子B被相对定位,跟在盒子B后面的盒子布局时会认为盒子B没有位移,而是还处在盒子B在常规流中的位置上。The effect of table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined.

absolute: 设置了该值的盒子的位置和可能的尺寸通过他的top、right、bottom、left属性确定。这四个属性规定了这个盒子相对于他的包含块(containing block)的位移。绝对定位的盒子脱离了文档流,也就是说这些盒子对跟在他们后面的兄弟盒子的布局没有任何影响。绝对定位的盒子的外边距不会与其他盒子的外边距合并。

fixed: 设置了这个值的盒子的位置按照绝对定位(position属性值为absolute)模型计算,他相对于一些参照物固定位置。在绝对定位模型中,盒子的外边距不会与其他外边距合并。在手持 handheld 、投影 projection 、屏幕 screen 、打字机 tty 、电视 tv 媒体类型中,盒子相对于视口(viewport)固定,即使发生滚动。在打印 print媒体类型中,即使通过视口(viewport)查看页面(比如打印预览),盒子也会被渲染到每一页上,并且相对于页面(页盒 原文page box)固定。在除了上述媒体类型之外的其他设备类型中,没有规定盒子该如何展示。开发者可以根据媒体类型的不同指定不同的position属性值。比如,开发者可能希望一个盒子在屏幕设备中保持在视口的顶部,但是又不想让这个盒子出现在每一张打印页的顶部,可以使用媒体查询(@media)在在不同设备上分别指定。

注意:当一个元素的position属性被指定为absolute或fixed时,该元素的display属性会被重写为block。但是,此时仍然可以把该元素的display属性指定为flex(弹性盒模型是对于该元素的子元素而言的)。

像下面这样:

@media screen {
h1#first { position: fixed }
}
@media print {
h1#first { position: static }
}

用户代理不能分页显示固定定位盒子的内容。

用户代理可以将根元素(root element)的position属性值视为static。

注意:只有static定位的盒子的left、top、right、bottom属性不可用。

7、常规流(normal flow)

常规流中的盒子在块格式化上下文(block formatting context)或行内格式化上下文(inline formatting context)中布局,但是不能同时在两个上下文中。块级盒在块格式化上下文中布局,行内级盒在行内格式化上下文中布局。

7.1 块格式化上下文

浮动元素、绝对定位元素、不是块盒(block boxes)的块容器盒(block container boxes,例如inline-block,table-cell,table-caption)、以及overflow属性不是visible的块盒(该值传播到视口 viewport的情况除外),会对他们内部的内容产生新的块格式化上下文。

下面是MDN上列举的可以产生新的块格式化上下文的情况:

  • 根元素或其它包含它的元素
  • 浮动 (元素的 float 不为 none)
  • 绝对定位元素 (元素的 position 为 absolute 或 fixed)
  • 行内块 inline-blocks (元素的 display: inline-block)
  • 表格单元格 (元素的 display: table-cell,HTML表格单元格默认属性)
  • 表格标题 (元素的 display: table-caption, HTML表格标题默认属性)
  • overflow 的值不为 visible的元素——这句话不太准确
  • 弹性盒子 flex boxes (元素的 display: flex 或 inline-flex)——CSS3中新增

在块格式化上下文中,盒子在垂直方向上从包含块的顶部 top开始一个接一个地摆放。两个兄弟盒子之间的垂直间隔由各自的margin属性决定。在同一个块格式化上下文中,两个相邻块级盒的垂直外边距 margin会合并。

在块格式化上下文中,每一个盒子的左外边界(left outer edge)与该盒子的包含块(containing block)的左边缘(left edge)接触(从右向左的格式化时,与右边缘接触)。即使存在浮动的盒子(虽然浮动的盒子会让其他盒子内的行盒 line box收缩),其他盒子的左外边界也会与包含块的左边缘接触,除非其他盒子创建一个新的块格式化上下文(这个创建了新的块格式化上下文的盒子可能会因为浮动盒子的存在而变窄)。(利用块格式化上下文,可以使一个元素对内清除子元素的浮动)

7.2 行内格式化上下文(inline formatting context)

在行内格式化上下文中,盒子在水平方向上从包含块的顶部 top开始一个接一个地放置,他们在水平方向上的外边距 margin、边框 border、内边距 padding会在布局时被计算在内。这些盒子在垂直方上可能会以不同的方式对齐:底部对齐、顶部对齐、文本基线对齐。包含这些盒子的矩形区域形成一个行,称为行盒(line box)。

行盒的宽度 width取决于包含块和是否存在浮动元素。行盒的高度 height取决于行高的计算规则(line height calculations)。

一个行盒至少要足够高以能够完全包裹他包含的所有盒子,甚至有可能比他所包含的最高的那个盒子还要高(比如,行盒内的盒子以基线对齐)。当一个盒子B的高度 height小于包含他的行盒的高度 height时,盒子B在垂直方向上的对齐方式取决于vertical-align属性的值。

当几个行内级盒(inline-level boxes)不能水平地放在同一个行盒内时,他们会被放置在两个或多个垂直堆叠的行盒里。因此,一个段落就是由多个垂直堆放的行盒组成的。默认情况下,行盒堆放时没有垂直间距(除非在其他地方特别指定)而且从来不会互相覆盖。

一般情况下,行盒的左边缘 left edge与他的包含块的左边缘left edge接触,右边缘与他的包含块的右边缘接触。然而,浮动的盒子可能出现在行盒的边缘 edge和行盒包含块的边缘 edge之间。当有浮动元素存在时,尽管所有的行盒都在同一个行内格式化上下文中,但是他们的宽度却不一定都相等(通常等于其包含块的宽度),因为浮动元素会减少行盒在水平方向上的可利用空间。在同一个行内格式化上下文中的行盒也会有不同的高度 height,比如有的行盒可能包含一个非常大的图片,而其他的行盒只包含文本。

当一行内的所有行内级盒(inline-level boxes)的总宽度 width小于包含他们的行盒的宽度时,这些行内级盒在行盒里的水平分布取决于text-align属性的值。假如text-align属性的值为justify,用户代理会拉伸行内盒(inline boxes,不包括inline-table和inline-block产生的行内盒)中的空格和单词,以适应行盒的宽度。

当一个行内盒(inline box)超过了行盒的宽度,这个行内盒会被拆分成多个盒子,拆分后产生的盒子会分布在多个行盒中。如果行内盒不能被拆分(比如这个行内盒包含一个单独的字符,或者语言规定的断字规则不允许断字,或者white-space的值为nowrap或pre),那么这个行内盒就会溢出行盒。

当一个行内盒被拆分时,margin、border、padding在拆分处不会产生任何视觉上的影响。

在同一个行盒内,行内盒也可能因为双向文本处理而被拆分为多个盒。

在一个行内格式化上下文中,行盒被按需创建来包含行内级内容(inline-level content)。

不包含文本,不包含preserved white space,不包含有非零margin或非零padding或非零border的行内元素,不包含其他处在文档流里的内容(例如image、inline block、或者inline table),不以一个preserved newline结尾的行盒,如果是为了决定他内部的元素的定位,那么这个行盒必须以零高度对待(zero-height),除此之外的用途,这个行盒会被当做不存在。

例子:

<P>Several <EM>emphasized words</EM> appear
<STRONG>in this</STRONG> sentence, dear.</P>

例子中P元素产生了一个块盒(block box),这个块盒包含5个行内盒(inline box),其中三个是匿名的。如下:

  • Anonymous: "Several"
  • EM: "emphasized words"
  • Anonymous: "appear"
  • STRONG: "in this"
  • Anonymous: "sentence, dear."

为了格式化这个段落,用户代理将上面五个盒子放入行盒。在这个例子中,P元素生成的盒子为行盒创建包含块。假如这个包含块足够宽,所有的行内盒将会自适应地进入一个单独的行盒。如下:

Several emphasized words appear in this sentence, dear.

假如这个包含块不够宽,这些行内盒将会被拆分并分布在多个行盒里。如下:

Several emphasized words appear

            in this sentence, dear.

或者如下:

Several emphasized

                words appear in this

sentence, dear.

这时,EM元素格式化成的盒子被拆分成两个EM盒子(EM box,叫他们split1和split2)。Margin、border、padding、或者文本装饰在split1后和split2前不会产生明显的影响。

看下面的例子:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
<HEAD>
<TITLE>Example of inline flow on several lines</TITLE>
<STYLE type="text/css">
EM {
padding: 2px;
margin: 1em;
border-width: medium;
border-style: dashed;
line-height: 2.4em;
}
</STYLE>
</HEAD>
<BODY>
<P>Several <EM>emphasized words</EM> appear here.</P>
</BODY>
</HTML>

根据P元素的宽度,显示结果可能如下:

CSS学习笔记——视觉格式化模型 visual formatting model

EM元素的margin分别被插入到“emphasized”的前面和“words”的后面。

EM元素的padding被插入到“emphasized”的前面,上面和下面以及“words”的后面,上面和下面。虚线的边框也分别描绘到emphasized”和“words”三个侧面。

7.3 相对定位(relative positioning)

一旦一个盒子被按照常规流或者浮动布局,那么这个盒子可能会相对于这个位置被移动,这种机制被称为相对定位(relative position)。盒子B1按照相对定位机制发生位移不会对跟在他后面的盒子B2产生影响:盒子B2会认为盒子B1没有发生位移,B2也不会重新调整自己的位置。这意味着相对定位会引起盒子们的相互重叠。如果相对定位让一个overflow属性为auto或scroll的盒子(父盒子)溢出,用户代理必须通过创建滚动条允许用户访问溢出的内容(在偏出的位置),而这可能会影响布局。(这句话是说子盒子相对定位而溢出父盒子,父盒子根据overflow的值决定是否创建滚动条。)

一个相对定位的盒子,为其保留他在常规流中的尺寸,断行效果和原始位置。同时,相对定位的盒子对内产生一个新的包含块。

对于相对定位的元素而言,left和right属性使这个元素的盒子水平移动,但是不改变他的尺寸。Left属性使盒子向右移动,right属性使盒子向左移动。因为盒子在移动过程中没有被拆分或拉伸,所以不管是向左移还是向右移,left始终等于-right。

如果left和right均为auto,则这两个属性的值会重写为0,即盒子待在他原来的位置上。

如果left是auto,那么left的值等于-right,盒子向左移。

如果right是auto,那么right的值等于-left。

如果left和right都不为auto,那么盒子的位置过分受限,left和right中的一个必须被忽略。忽略的规则是:如果包含块的direction属性为ltr,left胜出,right等于-left;如果包含块的direction属性为rtl,right胜出,left等于-right。

例如,下面三条规则是等价的:

           div.a8 {
position: relative;
direction: ltr;
left: -1em;
right: auto;
}
div.a8 {
position: relative;
direction: ltr;
left: auto;
right: 1em ;
}
div.a8 {
position: relative;
direction: ltr;
left: -1em;
right: 5em ;
}

top和bottom属性在垂直方向上移动相对定位元素,同时不改变其尺寸。top属性向下移动盒子,bottom属性向上移动盒子。因为在移动时盒子不会被拆分或拉伸,所以top的值始终等于-bottom。

如果top和bottom都为auto,那么他们的值会被重写为0;

如果两者之中有一个为auto,那么其中一个就会等于另外一个的相反数。

如果两者都不为auto,bottom将被忽略(bottom的值等于-top)。

注意:在脚本环境中动态地移动相对定位的元素会产生动画效果(参见visibility属性)。尽管相对定位可以用来制作上标或下标,但是行高不会自动调整以包含上标或下标的位置(意思是行高的计算忽视上标或下标,可以参见行高的计算规则)。

8、浮动(floats)

Tips:浮动的影响仅限于他所在的BFC,而且只对在他之后的盒子的布局产生影响。

一个盒子被移动到当前行的左边或右边称为浮动。浮动最有趣的特性是内容会沿着浮动盒子的边缘布局(可以通过clear属性阻止这种特性)。如果盒子左浮动,内容沿着他的右边缘布局;如果盒子右浮动,内容沿着盒子的左边缘布局。下面介绍一部分浮动和内容流的特性,其他的规则在float属性中介绍。

1) 一个浮动的盒子会一直向左或向右移动直到他的外边缘碰到包含块的边缘或者碰到另一个浮动元素的外边缘。假如存在行盒,浮动盒子的上外边缘会与当前行行盒的顶部对齐。

2) 如果水平方向上没有足够的空间容纳浮动的盒子,他会向下移动,直到能够容纳他或没有其他的浮动盒子。

3) 因为浮动元素脱离了文档流,在浮动盒子之前或之后创建的非定位的块盒 (non-positioned block boxes)在布局时会无视浮动盒子(当浮动盒子不存在)(这句话的意思是在浮动盒子之后形成的非定位的盒子会无视浮动盒子而直接与浮动盒子之前的盒子进行外边距合并)。但是,当前的行盒和随后的紧挨着浮动盒子创建的行盒会被缩短以给浮动盒子的外边距留出足够的空间(非定位的盒子虽然会无视浮动盒子,但他里边的行盒不会无视浮动盒子)。

4) 紧挨着浮动盒子的行盒在垂直方向上的位置满足全部以下四条规则(定义了什么样的行盒才算是紧挨着浮动盒子的行盒 a line box is next to a float):

a、在行盒的顶部或顶部之下

b、在行盒底部或底部之上

c、在浮动盒子上外边距边缘之下

d、在浮动盒子下外边距边缘之上

根据我的理解画的图示:

CSS学习笔记——视觉格式化模型 visual formatting model

注意:这意味着如果浮动盒子的外部高度(outer height)为零或为负,行盒不会被缩短。

5)  假如一个缩短后的行盒太小而不能包含任何内容,那么他被向下移动(宽度也重新计算),直到没有浮动盒子或刚好能够能容纳这个行盒的内容。当前行中,浮动盒子之前的任何内容被重新放在浮动盒子另一侧的同一行中。也就是说,如果一个行内级盒在浮动元素之前被放在了当前行中,而且当前行盒中的内容在水平方向上没有充满包含块,剩下的空间刚好能够容纳一个左浮动盒子。这个浮动盒子布局时,他的上外边界会与当前行盒的顶部对齐,当前行盒中的那个行内级盒相应地会被移动到浮动盒子的右侧(如果盒子右浮动,行内级盒被移动到浮动盒子的左侧)。如果格式化方向是rtl,道理相同。

6) 表格、块级可替换元素、或者在常规流中能创建新的块格式化上下文的元素(比如overflow不为visible的元素),以上三种元素的边框盒(border box)一定不能与任何和他们处在同一个块格式化上下文中的浮动元素的外边距盒(margin box)重叠。如果有必要,用户代理在实现时应该把上述元素放在任何出现过的浮动元素的下面,但是,如果有足够的空间,也可以放在邻近的浮动元素的一侧。浮动元素也许会使上述三种元素的边框盒(border box)比规范10.3.3章节中规定的还要窄。CSS2没有明确规定用户代理什么时候可以把上述三种元素放在邻近的浮动元素旁边,也没有规定上述三种元素可以变得有多窄。(根据这段话可以实现两栏自适应布局)

例子,在下面的文档片段中,包含块太窄而不能包含浮动元素旁边的内容,因此这一部分内容需要移动到浮动元素的下面,他在行盒中的对齐方式根据text-align属性而定。

p { width: 10em; border: solid aqua; }
span { float: left; width: 5em; height: 5em; border: solid blue; } ... <p>
<span> </span>
Supercalifragilisticexpialidocious
</p>

显示结果如下:

CSS学习笔记——视觉格式化模型 visual formatting model

有时候会有好几个浮动元素相邻的情况,上面的模型也适用于这种情况。

下面的规则使所有类名为icon的IMG元素左浮动,同时margin-left为零:

img.icon {
float: left;
margin-left:;
}

思考下面的HTML文档和样式表:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
<HEAD>
<TITLE>Float example</TITLE>
<STYLE type="text/css">
IMG { float: left }
BODY, P, IMG { margin: 2em }
</STYLE>
</HEAD>
<BODY>
<P><IMG src=img.png alt="This image will illustrate floats">
Some sample text that has no other...
</BODY>
</HTML>

IMG box左浮动,跟在他后面的内容被格式化到IMG box的右边,并与IMG box从同一行开始布局。浮动盒子右边的行盒因为浮动盒子的存在而变短,但是在浮动盒子下面的行盒恢复到他们的正常宽度(P元素创建的包含块的宽度)。这个文档可能格式化为下面的样子:

CSS学习笔记——视觉格式化模型 visual formatting model

下面的文档也会被格式化为和上面相同的样子:

<BODY>
<P>Some sample text
<IMG src=img.png alt="This image will illustrate floats">
that has no other...
</BODY>

因为原本在浮动盒子左边的内容被浮动盒子取代,并被重新放置在浮动盒子的右侧。

7)  正如规范8.3.1章节规定的那样,浮动盒子的外边距从来不会和与他们相邻的盒子(请注意第六条规则)的外边距合并。因此,上面例子中,P元素的盒子(P box)与IMG box之间的外边距不会合并。

8) 浮动元素的内容堆放时,就好像浮动元素创建了新的堆叠上下文(stacking context)一样,但是,任何定位的元素和确实创建了新的堆叠上下文并参与浮动元素的父级堆叠上下文的元素除外。在常规流中,浮动盒子可以覆盖其他盒子(比如,一个常规流中的盒子与一个有负外边距的浮动盒子相邻时)。当这种情况发生时,浮动盒子会被渲染到文档流内的非定位的块盒(non-positioned in-flow blocks)的前面,同时渲染到文档流内的行内盒(in-flow inlines)的下边。

下面的例子说明了,当浮动盒子覆盖了常规流中的元素的边框时发生了什么:

CSS学习笔记——视觉格式化模型 visual formatting model

一个浮动的图片挡住了与他重叠的块盒的边框

下面的例子说明了使用clear属性阻止内容在浮动盒子旁边布局。

假设有一条下面的规则:

p { clear: left }

可能被格式化成如下样子:

CSS学习笔记——视觉格式化模型 visual formatting model

所有段落都设置了clear: left,这让第二个段落被向下推到了浮动盒子的下方——通过在第二个段落的上外边距的上面添加clearance实现。

8.1  float属性

float属性的值可取: left | right | none | inherit

初始值为:none

一个盒子根据float属性的值决定左浮动、右浮动还是不浮动。可以给任何元素设置该属性,但是只有能产生盒子的非绝对定位的元素才起作用。

用户代理视根元素的float为none。

以下是严格控制浮动元素行为的规则:

a、左浮动盒子的左外边界不会跑到其包含块的左边缘的左边,右浮动的元素有类似规则;

b、如果在当前左浮动盒子之前存在其他的浮动盒子,那么当前左浮动的盒子要么其左外边界必须在之前浮动盒子的右外边界之右,要么其顶部必须在之前浮动盒子的底部之下。右浮动的盒子有类似规则。

c、左浮动盒子的右外边界不会跑到与他相邻的右浮动的盒子的左外边界的右边。右浮动的盒子也有相似规则。(相邻浮动盒子的margin不合并)

d、浮动盒子的上外边界不会高于他的包含块的顶部。当浮动盒子出现在合并的外边距之间时,他的布局就像是他有一个另外的空的匿名父块参与了常规流的布局。这个父匿名块的位置遵循外边距合并章节中的规则。

e、在源文档中,浮动盒子的上外边界不会高于在他之前的元素产生的任何块盒或浮动盒的上外边界。

f、在源文档中,浮动盒的上外边界不会高于包含了此前元素形成的盒子的行盒的顶部。

g、如果一个左浮动盒子的左边有另外一个左浮动盒子,那么靠右的左浮动盒子的右外边界不会跑到他的包含块的右边缘的右边。(放宽限制:左浮动盒子的右外边界不会超出其包含块的右边缘,除非他已经尽可能地靠左了)。右浮动有相似规则。

h、浮动盒子必须被放得尽可能高(在其包含块内)。

i、左浮动盒子必须尽可能靠左放,右浮动盒子必须尽可能靠右放。更靠左(左浮动)或更靠右(右浮动)的浮动盒子占据更高的位置。

规范中的原文:But in CSS 2.1, if, within the block formatting context, there is an in-flow negative vertical margin such that the float's position is above the position it would be at were all such negative margins set to zero, the position of the float is undefined.

上述规则中提到的相关的其他元素都是和浮动元素处于同一块格式化上下文中的元素。

以下HTML文档片段让“b”字符右浮动。

<P>a<SPAN style="float: right">b</SPAN></P>

如果P元素足够宽,“a”字符和“b”字符将会各在一边,像下面这样:

CSS学习笔记——视觉格式化模型 visual formatting model

8.2 clear属性

Clear属性的值: none | left | right | both | inherit

初始值:none

适用于:block-level elements

这个属性指明了一个元素的盒子的哪一边不允许与浮动盒子相邻。该属性不考虑元素内部的浮动(不考虑子元素的浮动)或者是另一个块格式化上下文中的浮动。

给非浮动的块级盒应用clear属性时,各个值的含义:

left:要求盒子的上边框边缘(top border edge)在之前左浮动盒子下外边界的下面

right:要求盒子的上边框边缘在之前右浮动盒子下外边界的下面。

both:要求盒子的上边框边缘在任何之前左浮动和右浮动盒子的下外边界的下边。

none:盒子的位置相对于浮动盒子没有约束。

不是none的值可能引入clearance。Clearance阻止外边距合并,添加到一个元素的margin-top之上,从而向下推动这个元素垂直地越过浮动元素。

计算设置了clear属性的元素的clearance时,先假设该元素的clear属性为none,获得此时他的上边框边缘(top border edge)的位置。如果元素的上边框边缘在这个位置时没有越过相关的浮动元素,那么clearance就会被引入。引入的clearance的尺寸取下面两者中的较大者:

1、足以让元素的(我觉得前边加个“上”更严谨)边框边缘与要被清除的离      他最近的浮动盒子的下外边界相平。

2、足以让元素的上边框边缘处在clear为none时的位置。

两种方案选一种的话,使用第一种。(这句话好像是给浏览器厂商说的。)

注意:clearance可以是负的或是零。

例1.为了简单起见,假设只有三个盒子,盒子B1的margin-bottom为M1(B1没有后代,没有padding和border),浮动盒子F的height为H,盒子B2的margin-top为M2(没有padding和border,没有后代)。B2的clear为both,内容不为空。

不考虑B2的clear属性时,如下图所示,B1和B2的margin合并。我们假设B1的下边框边缘(bottom border edge)在y = 0处,F的顶部在y = M1处,B2的上边框边缘(top border edge)在 y = max(M1,M2)处,F的底部在y = M1 + H处。

CSS学习笔记——视觉格式化模型 visual formatting model

我们还假设B2不在F之下,也就是说,我们描述了一个需要添加clearance的情形。即:

Max(M1,M2) < M1 + H

我们要计算clearance的高度C两次,分别是C1和C2,而C = max(C1,C2)。

第一次计算时让B2的顶部与F的底部齐平,就是说,由于clearance外边距不再合并:

Bottom of F = top border edge of B2 ⇔

M1 + H = M1 + C1 + M2

C1 = H - M2

第二次计算,让B2的顶部处在max(M1,M2),就是说:

Max(M1,,M2)= M1 + C2 + M2 ⇔

C2 = max(M1,M2) - M1 - M2

假设max(M1,M2) < M1 + H,那么

C2 = max(M1,M2) - M1 - M2 < M1 + H - M1 - M2 = H - M2 ⇒

C2 < H - M2

所以C2 < C1,那么C = C1。

例2.在这个例子中,clearance的值为负,同样假设元素都没有border和padding。

<p style="margin-bottom: 4em">
First paragraph. <p style="float: left; height: 2em; margin: 0"><!--(浮动元素的margin为0是关键点)-->
Floating paragraph.
<!--第三个段落清除浮动后,他的border-top边缘紧贴浮动元素的margin-bottom外边缘,而不会越过。-->
<p style="clear: left; margin-top: 3em">
Last paragraph.

不考虑clear属性时,第一个段落和第二个段落的外边距将会合并,并且最后一个段落的上边框边缘会与浮动段落的顶部齐平。但是clear属性要求最后一个段落的上边框边缘在浮动段落的下面(不一定齐平)。所以必须引入clearance。按照规范,外边距不再合并,所以clearance + margin-top = 2em,clearance = 2em - margin-top = 2em - 3em = -1em。(本例中说的外边距不再合并是第一个段落和第三个段落的外边距不再合并。)

当给浮动元素设置clear属性时,要额外添加一条规则,第十条规则,如下:

10)如果浮动元素的clear属性设为left,那么他的上外边缘 top outer edge必须处在之前所有左浮动元素的下外边缘 bottom outer edge 的下面;如果设为right,则处在之前所有右浮动元素的下外边缘 bottom outer edge 的下面;如果设为both,则处在之前所有左浮动元素和右浮动元素的下外边缘 bottom outer edge 的下面。

注意:在CSS1中,所有元素都可以设置clear属性,在CSS2和CSS2.1中clear属性只能应用于块级元素 block-level elements,所以开发者应该只在块级元素上设置clear属性。行内元素实现清除浮动效果,不是通过设置clearance来实现的,应该在要实现清除浮动效果的行的行盒顶部之上插入一个或多个空的行盒(或者像浮动小节刚开始描述的那样向下移动行盒)从而使这个行盒处在浮动盒子之下。

9、绝对定位(absolute positioning)

在绝对定位模型中,一个盒子相对于他的包含块做精确的位移。

绝对定位的盒子被完全地从常规流中移除,对在他之后的兄弟元素的布局不产生影响。

一个绝对定位的盒子为他的在常规流中的子元素和绝对定位的子孙元素(固定定位fixed除外)创建一个新的包含块。绝对定位元素的内容不会围绕着其他盒子布局,他们会遮盖其他的盒子,或者被其他盒子遮盖,具体要看相互遮盖的盒子的stack level。

本部分提到的绝对定位元素或盒子指的是,position属性为absolute或fixed的元素。

9.1 固定定位fixed positioning

固定定位是绝对定位(absolute positioning)的一个子类。二者唯一的不同是固定定位的盒子的包含块是视口viewport创建的。对于连续媒体,当文档滚动时固定定位的盒子不会随着移动。在这方面,他们和固定的背景图像类似。对于分页媒体,固定定位的盒子被复制到每一页上显示。这对在每一页底部放置署名信息很有用。固定定位的盒子比页面大时,超出的部分会被裁剪掉。相对于初始包含块不可见的部分将不会被打印。

开发者会用固定定位创建展示框架。看下面的框架布局:

CSS学习笔记——视觉格式化模型 visual formatting model

上面的布局可以用下面的HTML文档实现:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
<HEAD>
<TITLE>A frame document with CSS 2.1</TITLE>
<STYLE type="text/css" media="screen">
BODY { height: 8.5in } /* Required for percentage heights below */
#header {
position: fixed;
width: 100%;
height: 15%;
top: 0;
right: 0;
bottom: auto;
left: 0;
}
#sidebar {
position: fixed;
width: 10em;
height: auto;
top: 15%;
right: auto;
bottom: 100px;
left: 0;
}
#main {
position: fixed;
width: auto;
height: auto;
top: 15%;
right: 0;
bottom: 100px;
left: 10em;
}
#footer {
position: fixed;
width: 100%;
height: 100px;
top: auto;
right: 0;
bottom: 0;
left: 0;
}
</STYLE>
</HEAD>
<BODY>
<DIV id="header"> ... </DIV>
<DIV id="sidebar"> ... </DIV>
<DIV id="main"> ... </DIV>
<DIV id="footer"> ... </DIV>
</BODY>
</HTML>

10、display、position、float之间的关系

display、position、float三个属性对盒子的产生和布局的影响遵循以下规则:

1、如果display的值为none,position和float不再起作用,元素不产生盒子。

2、否则,如果position为absolute或fixed,盒子绝对定位,float不再起作用(为none),display的值按下面表格所示重新计算。盒子的位置由其包含块和top、right、bottom、left决定。

3、否则,如果float的值不为none,盒子浮动,display的值按下表重新计算。

4、否则,如果元素是根元素,display按下表重新计算。除了CSS2.1中未定义的值为list-item时,是重新计算为block还是不变。

5、否则,其余的display值计算为指定值。

表格如下:

Specified value

Computed value

inline-table

table

inline,table-row,table-row-group,table-column,table-column-group,table-header-group,table-footer-group,table-cell,table-caption,inline-block

block

others

Same as specified

关于BFC的好文章:

什么是BFC:http://web.jobbole.com/84808/

上一篇:[R语言]关联规则1---不考虑items之间的时序关系


下一篇:visual formatting model (可视化格式模型)【持续修正】