你的位置:首页 > 网页设计

[网页设计]CSS2.1SPEC:视觉格式化模型之包含块


原汁原味的才是最有味道的,在阅读CSS标准时对这一点的体会更加深刻了,阅读文档后的一大感觉就是很多看上去理所应当的样式表现也都有了对应的支持机制。本文首先从包含块写起,一方面总结标准中相应的阐述,并且结合具体的实例进行具体分析,特别是对于CSS2.1支持并不完善的IE6/7。由于经验尚浅,文中肯定存在一些问题,希望大家可以多多包涵并且指出问题。
 
阅读本文前,应当对于CSS盒模型以及视觉格式化模型中会产生的各种框(box,也可以成为盒子)以及各种不同的布局方式有比较准确的认识,可以阅读杜瑶大神的两篇文章,这两篇文章也是对标准中相关章节的翻译和阐述。
(1)视觉格式化模型中的各种框
(2)置换和非置换元素
 
或直接阅读CSS2.1 SPEC中的相关章节:
(1)Box model
(2)Visual formatting model
 
在讲包含块之前还是想先简单说一下视觉格式化模型
 
1、CSS视觉格式化模型
标准中对于视觉格式化模型的阐述为:
how user agents  process the document tree for visual media.
即用户代理在视觉媒体下如何处理文档树,用户代理最常见的比如浏览器,而负责页面解析和渲染的就是浏览器的渲染引擎(现在更多地直接称为浏览器内核),文档既可以包括HTML文档,也包括其他通用标记语言所编写的文档。在视觉格式化模型中,文档树中的每个元素都会根据盒模型产生0个或多个框,影响这个框的布局的因素有:
    (1)框的尺寸和类型,比如块级框和行内级框的布局方式就是不同的。
    (2)框的定位方式,display属性绝对了一个元素所产生的框的定位方式,定位方式不同的框在布局时也有不同的规则。
    (3)与其他框的关系,假如有两个框,它们之间是包含关系还是同辈关系而所产生的布局是不相同的。
    (4)其他的额外信息(比如视口的大小,置换元素的固有尺寸等)
从这个角度讲,视觉格式化模型就是对上述这些因素是如何影响一个框的布局而进行的详细描述。
 
2、视口(viewport)
第一部分中出现了视口这个词,直观上看,我觉得视口可以理解为用户代理用来呈现解析和渲染后的文档树的区域,用户通过这个区域可以阅读文档得内容,在浏览器中,视口一般就是浏览器用于呈现网页的区域。
标准中对于视口的阐述为:
 

User agents for continuous media generally offer users a viewport (a window  or other viewing area on the screen) through which users consult a document. 

 
注意加粗的continuous media,我们平时所浏览的网页就属于连续媒体,而通过打印机打印的文档内容必须是分页的,所以就是分页媒体,即paged media(此处的解释略粗糙,仅是个人的简单看法)。网页是连续的媒体,那么问题就来了,视口不可能跟随网页的大小而变化,因此视口在小于文档的尺寸时,必须提供一种滚动机制,最常见的就是浏览器的滚动条,通过滚动条我们就可以浏览超出视口的文档部分,也就是文档中阐述的:
 

When the viewport is smaller than the area of the canvas on which the document  is rendered, the user agent should offer a scrolling mechanism. 

 
 
当然关于视口的知识点可不只是上述的这么简单,比如在移动web中,viewport的相关属性对于移动web的开发至关重要,但本文暂不涉及其他关于视口的知识点。
 
3、包含块(containing block)
一个网页是由一个一个的框(box)所组成的,每一个框在页面中都有自己的尺寸、位置以及其他的渲染属性(如背景色,字体等),那么这些框在布局的时候,它们的位置是如何决定的呢?另外,如果没有为框定义显示的尺寸(width,padding,border,margin)等,那么它们的尺寸又是如何确定的呢?答案的关键点就是包含块,个人认为,包含块是学习CSS布局时基础中的基础,不过还好,这个概念也并不难理解。
首先看一下标准中对于包含块的阐述:
The position and size of an element's box(es) are sometimes calculated relative to a  certain rectangle, called the containing block of the element.
即视觉格式化模型中所产生的各种框,它们的位置和大小往往都是根据一个特定的矩形框边缘来计算得到的, 这个矩形框就是这个box的包含块。

注:如果讲一个框的包含块时,指的是它所处的包含块,而不是它形成的包含块。
 
那么该如何确定一个框的包含块呢?标准中规定了相应的规则:
3.1根元素的包含块
 
根元素(HTML中就是html标签)的包含块称为初始包含块:对于连续媒体(比如网页),它具有视口的尺寸并且定位在画布的原点(可以直观理解为视口的内容起始位置,如果初始包含块的direction属性为ltr,那么起点位置就是视口的左上角,如果为rtl,那么就为视口的右上角)。初始包含块的direction与根元素的direction属性相同,默认为ltr,即从左到右。
 
特别注意:初始包含块具有视口的尺寸,也就是说宽高都是和视口相同的,即使视口中出现了滚动
 
 

3.2元素定位为static(默认值)或relative(相对布局)时的包含块


对于定位属性为static(默认值)或relative(相对布局)的元素,其包含块是由最近的祖先块容器框的内容区域构成。这一点也比较好理解,虽然出现了块容器框和内容区域两个词,对于CSS盒模型和视觉格式化模型中的各种框有所了解后应该就能明白。下面有个简单的例子:
##DEMO 1:static或relative定位的元素的包含块
CSS代码:
.container{
width: 1000px;
margin: 50px auto;
border: 2px solid #000000;
padding: 50px;
}

HTML代码:
<div class="container" style="">
<div class="static-div-1" style="background: #CCCCCC;">
static定位的元素
</div>
</div>
带黑色边框的container有50px的padding,可以看到class为static-div-1元素是基于container的内容区域(content area)来定位的,这一点IE6/7也都是遵循标准的。

 
3.3元素定位为absolute时的包含块
 
对于position为absolute的元素,其包含块为最近的拥有非static定位属性(即position为relative,absolute或fixed)的元素所产生,有下面两种情况:
    <1>如果这个元素为一个块容器框元素,那么包含块由这个元素的内边距边界(padding edge)形成
    <2>如果这个元素是一个行内元素,那么这个包含块由包围该行内级元素的第一个行框和最后一个行框的box形成。如果行内元素被分割成了多行,那么在CSS2.1中,包含块则是未定义的。
     如果没有这样的祖先,那么其包含块就是初始包含块
注:在分页媒体中包含不一样的情况,本文暂不讨论分页媒体中的情况
 
absolute的情况相对复杂一些,看以下例子:
##DEMO 2:绝对定位元素的包含块(1)-块容器框形成包含块
CSS代码:
.container{
width: 1000px;
margin: 50px auto;
border: 2px solid #000000;
padding: 50px;
position: relative;
}
.absolute-div-1{
position: absolute;
left: 0px;
top: 0px;
}
HTML代码:
<div class="container">
<div class="absolute-div-1">
absolute定位的元素
</div>
</div>
效果如下:



可以看到,absolute-div-1的元素是绝对定位,并且定位起点为左侧0px,上部0px,虽然container有50px的padding,但由于绝对定位的元素是根据块容器框的padding edge来定位,所以absolute-div-1还是紧贴左上角显示。##DEMO 3:绝对定位元素的包含块(2)-行内元素形成包含块
DEMO3中,我们把container换成行内元素span,为了更明显地显示效果,我们给body加了一个高度并且设置了背景色,并且为span和absolute-div-1设置了不同的字体颜色,代码如下CSS代码:
body{
margin: 0px;
font-size: 14px;
height: 500px;
background: #a0b3d6;
}
.container{
margin: 50px auto;
position: relative;
color: #eeeeee;
}
.absolute-div-1{
position: absolute;
color: #ff0000;
left: 0px;
top: 0px;
}


HTML代码:
<span class="container">
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
<div class="absolute-div-1">
absolute定位的元素
</div>
</span>

效果:
在IE6/7中效果也相同,另外我们提到过,如果元素是一个行内级元素,那么这个包含块由包围该行内级元素的第一个行框和最后一个行框的box形成。我们来验证标红部分的阐述,即把absolute-div-1的top定位改为bottom,即bottom:0px,效果如下:

这也验证了标准中所阐述的内容。
 
注:在IE6中,绝对定位元素如果只用bottom定位,但是形成包含块的元素没有触发hasLayout时,bottom不会根据包含块的底部来定位,这个bug通过zoom:1等属性触发hasLayout可以解决。
##DEMO 4:绝对定位元素的包含块(4)-行内元素被分割成多行时
视觉格式化模型中描述过,如果一个行内级元素包含了块级元素,那么这个行内级元素就会被分割成2块,并且都成为了块级元素。我们把DEMO3中的container改动一下:
<span class="container">
  行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
<div style="background: #eeeeee">中间出来了一个div</div>
<div class="absolute-div-1">
absolute定位的元素
</div>
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
</span>
此时的效果在不同的浏览器中存在差异:IE6+和firefox中的效果为:

而chrome中的效果为:

可以看出,chrome中的绝对定位元素在定位时是基于被分割后形成的第二个框来定位的,而如果我们把绝对定位元素移动到分割元素之前,即:
<span class="container">
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
<div class="absolute-div-1">
absolute定位的元素
</div>
<div style="background: #eeeeee">中间出来了一个div</div>
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含块行内级包含
</span>

那么chrome中的效果就成了:

可见,chrome中的位置是与绝对定位元素在代码中的位置相关的。 
##DEMO 5:绝对定位元素的包含块(4)-初始包含块作为包含块
如果一个绝对定位元素找到有非static定位属性的祖先,那么初始包含块就作为其包含块,这里需要注意的是初始化包含块的尺寸问题,2.1节中曾经讲过初始包含块具有视口的尺寸,即使存在滚动时,DEMO代码如下:
CSS代码:为了出现滚动,我们为body加了1000px的高度
.body-for-demo4{
height:1000px;
background: #a0b3d6;
}
.absolute-div-2{
  height: 100px;
width: 100px;
background: #03a9f4;
position: absolute;
bottom: 0px;
left:0px;
}
HTML代码:

<body class="body-for-demo4">
<div class="absolute-div-2">
</div>
</body>
效果如下:

可以看到,虽然body的高度超出了初始包含块使得滚动条出现,但是初始包含块的尺寸时没有发生改变的。
如果我们把body设置一个position:relative的属性,那么绝对定位框就会跑到页面底部了。

 
2.4元素定位为fixed时的包含块
 
元素定位属性为fixed时,对于连续媒体,包含块由视口形成,对于分页媒体,则由页面区域形成。
position为fixed的元素特点是不随页面的滚动而滚动,它的包含块由视口形成,这一点也比较好理解。但是IE6不支持fixed的属性,可以使用css表达式或者hack的方式使IE6支持fixed。可参考:
http://caibaojian.com/468.html
 
另外CSS2.1标准的原文第10章第1节中也有包含块的示例,标准中的示例更加基础,同样也可以参考。