CSS中的视觉格式化模型

视觉格式化模型

1. 简介

在视觉格式化模型中,文档树中的每个元素都将会根据盒模型产生零到多个盒子。这些盒子的布局由如下因素决定:

  • 盒子的尺寸和类型
  • 定位策略(正常文档流,浮动或者绝对定位)
  • 和文档树中其他元素的关系
  • 额外的信息(比如视口的大小,图片的原始尺寸等)

1.1 视口(viewport)

连续媒体(continuous media)的UA(user agent)通常会给用户提供一个视口,一般为一个窗口或者屏幕上的一片可视区域。用户通过这个视口查看文档(document)。当视口的大小改变时,UA可能会改变文档的布局。
当视口的大小比文档所渲染的区域(画布,canvas)小时,UA需要提供滚动机制。每个画布只能有至多一个视口,但是UA可以将一个文档渲染到多个画布上去(比如提供同一个文档的不同展示方式)。

1.2 包含块

在CSS2.1中,许多盒子的位置和大小都是根据一个叫做包含块(containing block)的长方形盒子的边进行计算的。通常,生成的盒子作为后代盒子的包含块,也就是说一个盒子产生它后台盒子的包含块。“盒子的包含块”指的是包含盒子的包含块,而不是盒子产生的包含块。
每个盒子都根据它的包含块进行定位,但并不会受包含块限制,它可能溢出(overflow)。

2. 控制盒子的生成

下面几节描述了CSS2.1中可能产生的盒子的类型。在视觉格式化模型中,盒子的类型会部分的影响盒子的行为。在CSS2.1中,可以通过"display"属性指定盒子的类型。

2.1 块级元素和块级盒子

块级元素(Block-level element)就是在那些源文档中被格式化成可见的块的元素,比如段落。"display"属性的如下取值将会把元素变成块级元素:"block"、"list-item"和"table"。
块级盒子(Block-level box)就是那些在块格式化上下文(block formatting context)中的盒子。每个块级元素都会产生一个主块级盒子。这个主块级盒子包含后代盒子和其他生成的内容,同时还参与任何的定位策略。有些块级元素,除了主块级盒子以外,还将产生其他的盒子,如"list-item"元素。这些额外的盒子将根据主块级盒子进行布局。
除了表盒子和可替代的元素盒子,块级盒子通常也是块容器盒子(block container box)。一个块容器盒子既可以仅包含块级盒子,又可以建立一个行内格式化上下文,从而包含行级盒子。并不是所有的块容器盒子都是块级盒子,不可替代的行内块以及不可替代的表格单元都是块容器盒子,但他们都不是块级盒子。那些是块容器盒子的块级盒子被叫做块盒子(block box)。
当上下文含义清晰的时候,块级盒子、块容器盒子和块盒子有时被简称为块。

2.1.1 匿名块盒子

在如下的文档中:

<DIV>
  Some text
  <P>More text
</DIV>

假设DIV和P元素都设置了"display:block"。DIV元素既包含行内内容又包含块内容。为了更容易定义格式化,我们假设在"Some text"的周围有个匿名块盒子。

上图展示了例子中的三个盒子,当中有个匿名盒子。
换句话说:如果一个块容器盒子(比如上面的DIV的盒子)有一个块级盒子在当中,那么我们强制这个块容器盒子的所有的盒子都是块级盒子。
当一个行内盒子包含一个流中的块级盒子(in-flow block-level box)时,这个行内盒子(以及它在同一个行内盒子中的祖先)将会被拆断成两个盒子,分放在这个块级盒子的两边(可能有一边为空)。在这个块级盒子前后的行盒子被封在了匿名块盒子中,这个块级盒子和匿名盒子为兄弟关系。如果这样的行内盒子被设置了相对定位(relative position),那么任何的位置变动都将影响行内盒子中的块级盒子。
看个例子:
给下面的html文档,

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HEAD>
<TITLE>Anonymous text interrupted by a block</TITLE>
</HEAD>
<BODY>
<P>
This is anonymous text before the SPAN.
<SPAN>This is the content of SPAN.</SPAN>
This is anonymous text after the SPAN.
</P>
</BODY>

应用如下样式:

p    { display: inline }
span { display: block }

这个P元素包含一个匿名文本块(C1)、一个块级元素以及另一个匿名文本块(C2)。最终的效果是一个显示在BODY中的块盒子,这个盒子包含一个匿名块盒子(包含C1)、一个SPAN块盒子和另一个匿名块盒子(包含C2)。
匿名盒子的属性继承自包含它的非匿名盒子。非继承的属性将使用初始值。例如,之前的DIV的例子,匿名盒子的font属性继承自DIV,但是margin属性为0。
当涉及到百分比值计算的时候,匿名盒子将会被忽略,直到找到最近的非匿名盒子祖先为止。例如之前的DIV,如果匿名盒子的孩子需要知道它的包含块的高度来计算一个百分比的高度时,将会使用DIV产生的包含块的高度,而不是匿名盒子的高度。

2.2 行级元素和行内盒子

行级元素就是那些源文档中不会导致新块的产生的元素,元素的内容分布在行内。例如段落的强调文字部分、行内图片等。"display"属性的如下属性值指定一个元素为行级元素:"inline"、"inline-table"和"inline-block"。行级元素产生行级盒子,这些行级盒子存在于行内格式化上下文中。
一个行级盒子既表现在它是行级的,又表现在它的内容存在于包含他的行内格式化上下文中。设置了"display:inline"的不可替代元素将会产生一个行盒子。不是行盒子的行级盒子(如可替代的行级元素、行内块元素和行内表格元素)被称为原子行级元素,因为这些盒子在他们的行内格式化上下文中表现为一个单一的不透明盒子。

2.2.1 匿名行盒子

任何直接包含在块容器元素(该元素不包含在行元素内)的文本都应该被当成匿名行元素对待。
例如:

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

P元素产生一个块盒子,包含几个行盒子。包含"emphasized"文本的盒子是一个有行元素(<em>)产生的行盒子。但是另外的两个行盒子("Some"和"text")由块级元素(P)产生。后者被称为匿名行盒子,因为他们没有一个相关的行级元素。
这样的匿名行盒子从他们的块级父盒子继承可继承的属性。不可继承的属性将使用初始值。在例子中,匿名盒子的文本颜色继承自P,但是背景确实透明的。
只有空白字符并且可以通过"white-space"属性设置的策略合并的内容不会产生任何的匿名行盒子。

2.3 "display"属性

"display"属性的取值范围是: inline | block | list-item | inline-block | 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
"display"属性的初始值为"inline"。
一下是每种取值的具体含义:

  • block 应用该属性值的元素将会产生一个块盒子。
  • inline-block 应用该属性值的元素将产生一个行级块容器。inline-block的内部被格式化成一个块盒子,元素本身被格式化成一个原子行级盒子。
  • inline 应用该属性值的元素将会产生一到多个行盒子。
  • list-item 应用该属性值的元素(比如html中的li)将会产生一个主块级盒子和一个标签盒子。
  • none 应用该属性值的元素将不会出现在格式化结构(formatting structure)中,也就是说,元素不产生任何盒子,对布局没有任何影响。元素的后代元素也不产生盒子,元素和元素的内容被从格式化结构中完全删除。通过设置后代元素的"display"属性值也不能改变。
    请注意,"display"为"none"时不产生任何不可见的盒子,实际上,根本不产生任何盒子。如果想产生既不可见又影响格式化结构的盒子,请参考visibility属性。
  • table,inline-table,table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, and table-caption
    应用以上属性值的元素将表现得像一个表格元素。

注意,尽管"display"属性的初始值为"inline",但是UA的默认样式表可能覆盖这个值。
以下是一些display属性的例子:

p   { display: block }
em  { display: inline }
li  { display: list-item } 
img { display: none }      /* 不显示图片 */

3. 定位策略(Positioning schemes)

在css 2.1中,一个盒子将经过定位策略来进行定位:

  1. 正常文档流(Normal flow)。 在CSS2.1中,正常文档流包括块级盒子的块格式化上下文,行级盒子的行格式化上下文以及块级盒子和行级盒子的相对定位。
  2. 浮动(Floats)。 在浮动模型中,一个盒子先根据正常文档流定位,然后被从文档流中拿出来尽可能的放到左/右边。上下文中的其他内容将流动到浮动元素的某一边。
  3. 绝对定位(Absolute positioning)。 在绝对定位模型中,盒子被从正常文档流中完全移除(被移除的盒子对它原来的兄弟盒子将不再产生任何影响) 并且根据它的包含块进行定位。

如果一个元素是浮动的、绝对定位的或者是根元素(root element),那么就称这个元素脱离了文档流(out-of-flow)。如果一个元素没有脱离文档流,那么就称这个元素在文档流内(in-flow)。一个脱离文档流的元素A,它的文档流包括元素A本身以及所有A的在文档流内的后代元素。

3.1 position 属性

position属性的取值范围是:static | relative | absolute | fixed | inherit, 初始值为static。每个取值的含义如下:

  • static 应用该属性值的盒子就是正常的盒子,根据正常的文档流来进行定位。top、right、bottom和left属性不生效。
  • relative 应用该属性值的盒子,其位置将根据盒子在正常文档流中初始的位置以及一个偏移量来计算。即盒子的最终位置将相对于原来的位置发生一定的偏移。当盒子B采用相对定位后,盒子B后面的其他盒子的定位将不受影响,就好像盒子B没有采用相对定位一样。
  • absolute 应用该属性值的盒子,其位置由top、right、bottom和left属性来决定,这些属性将根据盒子的包含块来确定偏移量。采用绝对定位的盒子完全脱离正常文档流,也就是说,绝对定位的盒子对其他盒子将不再产生任何影响。采用绝对定位的盒子的外边距(margin)不与其他盒子的外边距重叠(collapse)。
  • fixed 应用该属性值的盒子,其位置将根据绝对定位来计算,另外,盒子还将相对于一定的参照物固定不动。例如在视觉媒体中,采用fixed定位的盒子将相对于视口(viewport)固定不动。

UA可能默认根元素的定位为static定位。

3.2 盒子的偏移: top、right、bottom、left

如果一个元素的position属性值不为static,那么就称这个元素被定位了。被定位的元素将产生被定位的盒子,然后根据如下四个属性进行放置:
top
top属性的取值范围是: 绝对长度值 | 百分比 | auto | inherit, 初始值为auto。
该属性指定一个绝对定位的盒子的外边距上边界(top margin edge)距离这个盒子的包含块的上边界(top edge)的偏移量(距离)。对于采用相对定位的盒子来说,这个偏移量根据盒子本身的上边界计算得出,也就是说,盒子初始采用正常文档流中的位置,然后在那个位置的基础上根据该属性值进行一定的偏移。
right
right属性的取值范围是: 绝对长度值 | 百分比 | auto | inherit ,初始值为auto。
跟top属性一样,该属性指定一个绝对定位的盒子的外边距右边界(right margin edge)距离这个盒子的包含块的右边界(right edge)的偏移量(距离)。对于采用相对定位的盒子来说,这个偏移量根据盒子本身的右边界计算得出。
bottom
botton属性的取值范围是: 绝对长度值 | 百分比 | auto | inherit, 初始值为auto。
跟top属性一样,该属性指定一个绝对定位的盒子的外边距下边界(bottom margin edge)距离这个盒子的包含块的下边界(bottom edge)的偏移量(距离)。对于采用相对定位的盒子来说,这个偏移量根据盒子本身的下边界计算得出。
left
left属性的取值范围是: 绝度长度值 | 百分比 | auto | inherit, 初始值为auto。
跟top属性一样,该属性指定一个绝对定位的盒子的外边距左边界(left margin edge)距离这个盒子的包含块的左边界(left edge)的偏移量(距离)。对于采用相对定位的盒子来说,这个偏移量根据盒子本身的左边界计算得出。

上面这四个属性的具体取值有如下含义:

  • 绝对长度值 相对于参考边的偏移量是个固定的距离且可以为负。
  • 百分比 偏移量是盒子包含块宽度(left、right)或高度(top、bottom)的百分比,可以为负。
  • auto 自动。

4. 正常文档流(Normal flow)

在正常文档流中的盒子都属于一个格式化上下文(formatting context)。这个格式化上下文既可以是块级的,也可以是行级的,但不能两者都是。块级盒子在块格式化上下文(block formatting context)中,行级盒子在行格式化上下文(inline formatting context)中。

4.1 块格式化上下文

浮动的元素、采用绝对定位的元素、不是块盒子的块容器(比如inline-block,table-cell和table-caption)以及设置了overflow且值不为visible(设置了visible的viewport除外)的块盒子将会为他们的内容建立新的块格式化上下文。

在块格式化上下文中,各个盒子从他们的包含块的顶部开始,竖直的顺次放置。相邻的两个盒子的竖直距离有margin属性决定。在同一个格式化上下文中的两个相邻的块级盒子的竖直外边距将会合并。

在块格式化上下文中,每个盒子的左外边界与盒子的包含块的左边界相接(对于从右到左的情况,右外边界与右边界相接)。 即使当有浮动元素存在是也依然使用,除非这个盒子产生了新的块格式化上下文(这样的话,由于浮动,盒子的宽度将变小)。

4.2 行格式化上下文

在行格式化上下文中,每个盒子从他们的包含块的顶部开始,依据每个盒子的水平margin,border和padding,水平的依次放置。这些盒子在竖直方向上,可能依据顶部或者底部对其,也可能依据盒子内部文本的基准线对其。包含这些盒子的来形成行的这个长方形区域就叫行盒子(line box)。

一个行盒子总要有足够的高度来包含它的所有后代盒子。因此,它可能比它包含的最高的盒子还要高(比如,行盒子内部的盒子都依据文本基准线对其)。当盒子B的高度比行盒子低的时候,盒子B在行盒子里的对其方式就由vertical-align属性决定。如果多个行级盒子无法在单一的行盒子中水平放置,那么这些盒子将会被分放到二到多个竖直堆叠(vertically-stacked)的行盒子中。因此,一个段落就是一个行盒子的竖堆。堆叠的行盒子之间没有分隔(除非指定),并且从不重叠。

通常,行盒子的左边界与包含块的左边界相接,右边界与包含块的右边界相接。但是,如果有浮动元素存在的话,那浮动元素将会介乎两者之间。因此,尽管在同一个行格式化上下文中的行盒子通常都与包含块同宽,但是他们的宽度也会因为受到浮动元素的影响而有所差异。在同一个行格式化上下文中的行盒子高度各不相同,比如一个行盒子包含一个较高的图片,而另一个仅仅包含文本。

当行级盒子的宽度小于包含他们的行盒子时,他们水平方向的对其方式将由text-align属性决定。如果text-align属性取值为justify,那么UA可能拉伸和盒子中的文字和间距,但是inline-table和inline-block盒子除外。

如果一个行内盒子的宽度超出了行盒子的宽度,那它将被拆断为多个行内盒子,然后放到多个行盒子中。如果行内盒子不能被拆断(比如行内盒子仅包含单个字符,或者定语言单子截断规则不允许截断,或者使用了white-space,且取值为nowrap或pre),那这个行内盒子将会溢出行盒子。
当行内盒子被拆断时,margin、border和padding对拆断处没有任何视觉上的影响。

当在行格式化上下文中需要包含行内级内容时就会创建行盒子。不包含任何文本、任何保留的空白字符、任何拥有非零margin、padding和border的行内元素以及任何其他在流中的内容(例如图片、行内块或行内表格)并且不以保留的换行符结束的行盒子,它将被视作没有高度的行盒子,以此来保证盒子里的任何元素的的位置计算。

如下是行内盒子构建的一个例子。

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

P元素产生一个块盒子,包含五个行内盒子,其中三个是匿名盒子:

  • 匿名盒子: "Several"
  • EM: "emphasized words"
  • 匿名盒子: "appear"
  • STRONG: "in this"
  • 匿名盒子: "sentence, dear"
    为了将段落进行格式化,UA将这五个盒子顺序放进一个行盒子中。在这个例子中,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盒子(暂成为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元素宽度的限制,各个盒子可能像如下放置:
inline-layout

  • margin被插入在"emphasized"的前面和"words"的后面。
  • padding被插入在"emphasized"的前面、上面、下面和"words"的后面、上面、下面。虚线的边框也同样被渲染在了两个单词的三边。

4.3 相对定位(Relative positioning)

一旦一个盒子被定位或者浮动,它可以相对于这个位置进行一定的偏移。这就叫做相对定位。偏移的盒子(B1)对紧跟着它的盒子(B2)没有任何影响:B2的位置就像B1没有发生偏移一样。因此,相对定位可能导致盒子重叠(overlap)。但是,如果相对定位导致了"overflow:auto"或者"overflow:scroll"的盒子溢出了,那么UA必须允许用户(通过滚动条等)能够访问到盒子的内容。

一个相对定位的盒子保持原有的正常大小,包括换行和一开始就保有的空间。

对于相对定位的元素来说,left和right将盒子不改变大小的水平移动。left将盒子右移,right将盒子左移。因为left或right的结果都不会导致盒子被分隔或拉伸,所以实际上最终使用的值总是: left=-right。

如果left和right的取值都是auto,那最终生效的值都为0(也就是说盒子呆在原来的位置)。
如果left取值为auto,那么最终使用的值是-right(也就是说盒子最终左移了right的数值)。
如果right取值为auto,那么最终使用的值是-left。
如果left和right都不是auto,那么二者之中必须要忽略一个。如果包含块的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。如果两个都是auto,那么最终使用的值都为0。如果其中之一为auto,那么设为auto的那个属性最终使用的值是另一个的负值。如果都不是auto,那么bottom被忽略(也即是说bottom最终使用的值是-top)。

5.浮动

浮动的盒子会被移动到当前行的左边或者右边。浮动最有趣的特点就是其他内容会在浮动的元素的旁边依次放置(或者通过设置clear属性禁止),或者说其他盒子会流动到浮动盒子的旁边。也就是说,其他盒子将沿着向左浮动的盒子的右边、向右浮动的盒子的左边依次放置。

一个浮动的盒子将会一直向左或向右移动,一直到触碰到包含块的边界或者其他浮动盒子的外边界。如果是行盒子的话,那么浮动盒子的外边界的顶部将与当前行盒子的顶部对其。
如果没有足够的水平空间,那么浮动的盒子将移至下一行直到有足够的空间或者行内已经没有其他浮动元素为止。

因为浮动盒子不在文档流中,所以在浮动盒子前后的未定位的块盒子将像浮动盒子不存在一样的竖直依次放置。但是,浮动盒子所在的当前行以及后面相邻的行盒子都会根据浮动盒子缩短宽度来给浮动盒子留下空间。
如果有个竖直的位置满足如下四个条件: (a)在行盒子的顶部或者顶部之下(b)在行盒子的底部或底部之上(c)在浮动盒子的外边界的顶部或者顶部之下(d)在浮动盒子的外边界的底部或者底部之上,那么行盒子将会换行。也就是说,如果浮动的盒子的外边界高度为0或者为负,那么行盒子不会缩短。

如果缩短的行盒子太短而不能包含任何内容,那么这个行盒子将会换行(并且高度会重新计算)直到有足够的空间或者没有浮动盒子为止。当前行内的任何在浮动盒子之前的盒子都将重新流动到浮动盒子的另一边。换句话说,如果一个行内盒子放在了一个向左浮动盒子的前面,那么,向左浮动的盒子将会放在这一行的开始,与行盒子的顶部对其,之前的行内盒子将会移动到浮动盒子的右边。对于rtl和向右浮动的盒子同理。

table、块级可替换元素或者正常文档流中建立新的块格式化上下文的元素的border盒不能与任何在同一个格式化上下文中的浮动盒子的margin盒重叠。

例如,如下片段中,包含块太窄,不能包含浮动盒子的右边的内容,因此,那段内容就移动到浮动盒子的下方。

p { width: 10em; border: solid aqua; }
span { float: left; width: 5em; height: 5em; border: solid blue; }


...


<p>
  <span> </span>
  Supercalifragilisticexpialidocious
</p>

这个文档片段可能是这样的:
supercal

多个浮动的盒子可能彼此相邻,上述模型也同样适用于同一行中的多个相邻浮动盒子。
下面这条规则将所有class="icon"的IMG都浮动到左边:

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

看如下的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盒子浮动到左边,剩余的文档内容被格式化在同一行的浮动元素的右边。浮动盒子右边的行盒子因为浮动盒子的存在而缩短了,但是在浮动盒子的下方又重新恢复了正常的宽度。上面的文档,可能被格式化成下面的样子:
floateg
如果文档被修改成下面这样,最终格式化后的效果依然是相同的:

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

因为左边的文本内容被浮动盒子替换并重新流动到了浮动盒子的右边。
因为浮动盒子的margin不与相邻盒子的margin重叠,所以上面例子中P盒子和IMG盒子的竖直margin不重叠。
浮动盒子可以与正常文档流中的其他的盒子发生重叠(例如浮动盒子旁边的盒子有一个负的margin),此时,浮动盒子在未定位的流内块盒子之前,但在流内行内盒子之后。
下面例子说明当浮动盒子与其他元素的边框重叠时发生了什么。
float2p
浮动的图片遮盖了与他重叠的盒子的边框。

如下示例说明如何通过clear属性禁止内容流向浮动盒子的一边。
假设如下规则:

p { clear: left }

最终文档可能如下:
floatclear

5.1 float属性

float属性的取值范围是: left | right | none | inherit,初始值为none。
该属性值决定元素向左移动还是向右移,或者根本不动。float属性可以被应用于任何元素上,除了绝对定位的元素。float属性的取值有如下含义:

  • left 元素产生一个向左浮动的块盒子。其他内容流向盒子的右边。
  • right 与left相似,盒子浮动到右边,其他内容流向盒子的左边。
  • none 不浮动

如下9条规定了浮动元素的确切行为:

  1. 向左浮动的盒子的外左边界不能超过包含块的左边界。同理应用于向右浮动的盒子。
  2. 对于一个向左浮动的盒子,如果在这个盒子之前还有其他向左浮动的盒子,那么,这个盒子的左外边界不能超过其他任何向左浮动的盒子的右外边界,或者这个盒子的上边界必须比其他盒子的下边界低。同理应用于向右浮动的盒子。
  3. 任何向左浮动的盒子的右外边界不能超过任何与之相邻的向右浮动的盒子的左外边界。同理应用于向右浮动的盒子。
  4. 浮动盒子的上外边界不能比包含块的上边界高。当浮动发生在两个合并的margin中时,浮动的盒子根据一个假设的父的空匿名块盒子定位。
  5. 浮动盒子的上外边界不能比任何其他之前的块盒子或浮动盒子的上外边界高。
  6. 浮动盒子的上外边界不能比任何之前的行盒子的上边界高。
  7. 一个向左浮动的盒子,如果在他之前还有向左浮动的盒子,那么这个盒子的右外边界不能超过包含块的有边界,除非这个盒子已经尽可能的向左了。同理应用于向右浮动的盒子。
  8. 浮动元素要尽可能的往高放置。
  9. 向左浮动的盒子要尽可能向左,向右浮动的盒子要尽可能向右。但是相对于尽可能向左或向右,更高的位置有更高的优先级。

下面的html片段,b浮动到右边:

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

如果P元素的宽度足够,那么a和b将各占一边,如下:
float-right

5.2 clear属性

clear属性的取值范围是: none | left | right | both | inherit, 初始值为none。只应用于块级元素。

该属性决定元素盒子的哪一边不与浮动盒子相邻。clear属性不考虑被应用元素内部的浮动以及其他块格式化上下文中的浮动。

属性取值含义如下:

  • left 应用该属性值的元素盒子的上边界要低于该元素之前的左浮动元素盒子的底外边界。
  • right 应用该属性值的元素盒子的上边界要低于该元素之前的右浮动元素盒子的底外边界。
  • both 应用该属性值的元素盒子的上边界要低于该元素之前的左浮动或右浮动元素盒子的底外边界。
  • none 不浮动。

所有不是none的值都将引入空隙(clearance)。clearance禁止margin合并,表现得就像元素的上外边距外还有一段空间。
要计算应用了clear属性的元素的clearance,首先需要找到元素上边界的假定位置。这个假定位置就是当clear属性值为none时元素实际上的上边界。
如果假定的元素的上边界不能越过相关的浮动元素(past the relevant floats),那么就需要引入clearance。

空隙的大小为如下两个值中的较大值:

  1. 将元素的上边界与最低的浮动元素的底外边界对齐需要的空间大小。
  2. 将元素的上边界放置到它假定的位置需要的空间大小。
    注意,空隙的大小可能为负也可能为0.

举个例子1,假设有三个盒子,块B1的bottom margin为M1(B1没有后代,没有padding也没有border),浮动块F的高度为H,块B2的top margin为M2(没有padding,没有border,也没有后代)。B2设置了clear:both。不考虑B2的clear属性,这三个盒子的如下图。B1和B2的margin重叠了。假设B1的底边界为y坐标原点(y=0),F的顶部在y=M1处,B2的上边界在y=max(M1,M2),F的底部在y=M1+H。
clearance
我们还假设B2不在F的下面,也就是说,我们需要引入clearance。因此:
max(M1,M2) < M1 + H
我们需要计算clearance C两次, C1和C2,然后 C=max(C1,C2)。
第一步,将B2的顶部与F的底部对其,也就是说,在y=M1 + H。考虑到margin不再合并,那么:
F的底 = B2的上边界 <=>
M1 + H = M1 + C1 + M2 <=>
C1 = M1 + H -M1 - M2
= H - M2
第二步计算,就是保持B2在原来的位置,y=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 <=>
C2 < H - M2
因为C1= H - M2,所以
C2 < C1
因此,C= max(C1,C2) = C1。

例2,假设所有元素都没有border和padding,在下面的代码中,clearance是-1em。

<p style="margin-bottom: 4em">
  First paragraph.

<p style="float: left; height: 2em; margin: 0">
  Floating paragraph.

<p style="clear: left; margin-top: 3em">
  Last paragraph.

解释: 如果没有clear,第一段和最后一段的margin将会合并,并且最后一段的上边界将会与浮动段的顶部对齐。但是clear需要上边界低于浮动元素,也就是说要再低2em。这就意味着必须要引入clearance。因此,margin不再合并,并且 clearance + margin-top = 2em, 也就是说clearance = 2em - margin-top= -1em。

当该属性被应用于浮动元素时,浮动元素的上外边界必须要低于之前的左浮动元素(clear:left)、右浮动元素(clear:right)或者左右浮动元素(clear:both)的底外边界。

6. 绝对定位(Absolute positioning)

在绝对定位模型中,盒子被从正常文档流中完全移除,并只相对于它的包含块进行偏移定位。绝对定位的盒子会为它的正常文档流后代盒子以及非fixed的绝对定位后代盒子建立一个包含块。绝对定位的盒子可能会遮盖其他元素,具体取决于堆叠层次。

6.1 固定布局(Fixed positioning)

固定布局是绝对布局的一个子类,唯一的不同在于固定布局的盒子的包含块由视口(viewport)产生。
我们通常通过fixed布局来创建页面的框架,比如:
frame
上图的效果可以通过如下代码采用fixed布局实现:

<!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>

7. 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的取值参照下表。
  5. 否则,display的取值就是指定的值。
**指定的值****最终使用的值**
inline-tabletable
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-blockblock
其他与指定的值相同

8. 正常文档流、浮动和绝对定位的比较

为了说明正常文档流、相对定位、浮动和绝对定位的不同,我们通过几个例子来说明,这些例子都是基于下面这段代码:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
  <HEAD>
    <TITLE>Comparison of positioning schemes</TITLE>
  </HEAD>
  <BODY>
    <P>Beginning of body contents.
      <SPAN id="outer"> Start of outer contents.
      <SPAN id="inner"> Inner contents.</SPAN>
      End of outer contents.</SPAN>
      End of body contents.
    </P>
  </BODY>
</HTML>

我们给上面的文档应用如下样式:

body { display: block; font-size:12px; line-height: 200%; 
       width: 400px; height: 400px }
p    { display: block }
span { display: inline }

在每个例子的说明中,左侧的数字表示正常文档流的位置。

8.1 正常文档流

我们增加如下样式:

#outer { color: red }
#inner { color: blue }

P元素包含所有的行内容:匿名行内文本与两个SPAN元素。因此,所有的内容都在一个行格式化上下文中布局,他们都在P元素建立的包含块之中,效果如下:
flow-generic

8.2 相对定位

我们添加如下样式:

#outer { position: relative; top: -12px; color: red }
#inner { position: relative; top: 12px; color: blue }

outer元素之前的文本正常流动,然后包含outer文本的行内盒子整体上移了12px。作为outer的子元素,inner也要上移12px,但是包含inner元素内容的盒子还需要根据自己的位置再下移12px,回到他们原来的位置。效果如下:
flow-relative
注意,outer元素后面的文本并没有受影响。

8.3 浮动

我们给inner元素添加如下样式,使之向右浮动:

#outer { color: red }
#inner { float: right; width: 130px; color: blue }

inner盒子之前的文本正常流动,inner盒子被从正常文档流中拿出并浮动到右边(已经为inner指定的宽度)。浮动盒子左边的行盒子宽度减少。
flow-float

再看clear属性的作用,我们给文档添加一个sibling元素:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<HTML>
  <HEAD>
    <TITLE>Comparison of positioning schemes II</TITLE>
  </HEAD>
  <BODY>
    <P>Beginning of body contents.
      <SPAN id=outer> Start of outer contents.
      <SPAN id=inner> Inner contents.</SPAN>
      <SPAN id=sibling> Sibling contents.</SPAN>
      End of outer contents.</SPAN>
      End of body contents.
    </P>
  </BODY>
</HTML>

添加如下样式:

#inner { float: right; width: 130px; color: blue }
#sibling { color: red }

让inner盒子浮动到右边,同时文档中剩余的文本流入空出来的区域。 效果如下:
flow-clear
但是,如果给silibling元素应用clear:right,那么sibling元素内容将会流动到float元素的下面。

#inner { float: right; width: 130px; color: blue }
#sibling { clear: right; color: red }

flow-clear2

8.4 绝对定位

最后,我们再看绝对定位:

#outer { 
    position: absolute; 
    top: 200px; left: 200px; 
    width: 200px; 
    color: red;
}
#inner { color: blue }

上面的样式将会使得outer盒子根据他的包含块来定位。一个已经定位的盒子的包含块由离它最近的已经定位的祖先盒子建立,如果这样的祖先盒子不存在的话,那么就是用初始包含块(initial containing block)。所以,outer盒子的上边距离包含块的顶部200px,左边距离包含块左边200px。outer的子元素在outer内部正常流动。效果如下:
flow-absolute

下面的这个例子展示的父元素采用相对定位的绝对定位的盒子。尽管作为父元素outer盒子实际上并没有偏移,但是,设置了position:relative就意味着这个盒子可以作为它的已定位的后代盒子的包含块。因为outer盒子是一个行内盒子并且分散在多行内,所以第一个行内盒子的上边缘和左边缘(图中粗虚线标出)作为top和left的参考。

#outer { 
  position: relative; 
  color: red 
}
#inner { 
  position: absolute; 
  top: 200px; left: -100px; 
  height: 130px; width: 130px; 
  color: blue;
}

效果如下:
flow-abs-rel
如果不给outer盒子设置position属性:

#outer { color: red }
#inner {
  position: absolute; 
  top: 200px; left: -100px; 
  height: 130px; width: 130px; 
  color: blue;
}

那么,inner盒子的包含块就变成了初始包含块。效果如下:
flow-static

其他具体内容请参考http://www.w3.org/TR/CSS2/visuren.html

posted @ 2015-11-08 18:05  tbingooo  阅读(2163)  评论(1编辑  收藏  举报