w3c系列之CSS(四):深入理解盒模型

盒模型是一个老生常谈的话题,本身并不复杂,它描述了文档树所产生的矩形框以及根据可视化格式模型(visual formatting model)所产生的布局结构。

曾经看到一道面试题:说说CSS中的盒模型?网上的回答都是margin,border,padding,width,height之类的,尼玛都是复制粘贴的吧= =.....要是这篇文章也这么写的话就跟前面三篇没啥区别了,既然要深入理解,那就谈点高大上的东西。

1.要深入,先入门

首先还是得对盒模型做个简单而详细的介绍,想要跑,基础要打好~,盗用w3c一张图:
w3c系列之CSS(四):深入理解盒模型
我们从里到外逐步分析:
  • 最里面是Content,决定Content大小的因素有很多,比如width,height属性,content内部是否还有box,是否是table等,所有的这些都将在可视化模型中详细讲到;
  • 然后是padding内边距,这个没啥好讲的;
  • 再外面是border,这个比较好玩~,虽然内外边距和边框都能单独设置四个方向的尺寸,但是CSS为border提供了更多的设置:border-color,border-style,有兴趣可以去看另外一篇写border的文章:CSS好玩的边框
  • 最外面是margin(注意他是透明的)。一个盒子没啥讲的,许多盒子在一起就有意思了。有可能会出现collapsing margins(外边距坍塌),也就是相邻的两个盒子的垂直外边距会合并(水平外边距是不会合并的)。
这就是盒模型的简单介绍了。不过盒模型可没这么简单,并不是所有元素都支持所有的margin和padding(比如内联元素,设置竖直方向的margin,padding是无效的),而且,虽然前面说了两个盒子竖直排列会发生垂直外边距坍塌,但并不是所有时候都会这样,比如下面这种情况:
	<style>
		body{background:#aaa}
		div{
			margin:50px;
			background:#0ff;
			width:50%;height:50px;
			float:left;
		}
	</style>
	<body>
		<div>
			box1	
		</div>
		<div>
			box2	
		</div>
	</body>
w3c系列之CSS(四):深入理解盒模型

我擦,那到底什么时候会合并,什么时候才不会合并啊?以前看书的时候没说过这种情况啊!/*那是因为你没有去看w3c*/折叠也是有规则的:
    1. 两个或多个毗邻的普通流中的块元素垂直方向上的margin会折叠
    2. 浮动元素/inline-block的元素/绝对定位元素的margin不会和垂直方向上的其他元素的margin折叠
    3. 创建了块级格式化内容(传说中的BFC)的元素,不和它的子元素发生margin折叠
    4. 元素自身的margin-bottom和margin-top相邻时也会折叠
更加详细的折叠计算戳这里:http://bbs.csdn.net/topics/340188875,下面直接进入本文重点。

2.可视化格式模型(Visual formatting model

2.1简介

这中文名字听起来挺别扭的,其实就是浏览器如何将document tree处理成你所看到的页面的过程模型。

/*为了方便以后就叫VFM吧...*/

不过VFM也不是描述了页面处理的全部细节,比如它并未描述letter-spacing的格式算法。(又是一个坑)

在VFM中,每一个DOM元素都会创建0个或多个盒子,这些盒子的布局通常受下面一些因素的影响:

  • 盒子的尺寸和类型
  • 盒子的定位方式(普通流,浮动流,绝对定位)
  • document tree中元素之间的关系
  • 额外因素(比如viewport的大小,图片的固有尺寸等)

viewport是啥?就是你屏幕上所看到的页面区域啦,通常页面区域比viewport大,所以需要一个滚动条来查看下面的内容,就像这篇文章一样。(当然你可以用鼠标滚轮)

2.2盒子的类型

盒模型不只是padding,border,margin,他还有许多不同的类型。这通常取决于盒子的display属性。

2.2.1块级元素(block-level elements )和块框(block boxes)

如果元素的display属性为block,list-item或者table,那么该元素就会成为block-level元素。这里有几个概念要搞清:
  • containing box:包含框。一个元素的包含框就是包含它的框,比如<div><p>test</p></div>,p的包含框就是div,而div为p建造了一个包含框。
  • block-level box:块级框,块级元素所产生的框。块级元素(不包含 table),会形成仅包含块框或仅包含行内框的主块框( principal block box )。主块框会为子孙元素建立包含框,生成内容,并且也是涉及所有定位体系的框。主块框参与BFC。
  • block container box:块容器框。块容器框要么只包含块级框,要么只包含内联框(inline-level boxes)。虽然名字有“块”,但并不是所有块容器框都是块级框:non-replaced inline blocks,non-replaced table cells都是块容器框,但不是块级框。
  • block box:块框,既是块级框,也是块容器框的框。
有些时候,在上下文很明确的条件下,"block-level box", "block container box" 和"block box"都缩写为"block"。

是不是有点晕了= =,慢慢看就理解了,为了避免翻译问题,后面遇到专业术语都用英文表示吧......
这节术语真是多,又出现了一个anonymous block boxes(匿名块框),比如像下面这种情况:
<div>
  Some text
  <p>More text</p>
</div>
假设div,p都是块级框(display:block),div中有内联文本Some text和块级文本More text,但是为了更好地生成格式化模型,会给内联文本外面加一个匿名块框。
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/anon-block.png
换句话说:如果一个block container box包含了一个block-level box,那么标准会强制使里面只包含block-level的boxes(或者run-in boxes)。

2.2.2内联元素(inline-level elements)和内联框(inline boxes)

内联元素就是那些不会生成新的内容块的元素,这些内容都是分布在同一行的。当display属性为inline,inline-table,inline-block时就会生成inline-level boxes,这些boxes会参与IFC。inline box就是内容参与其内部包含的IFC的inline level box。display为inline的non-replaced element会生成inline box。但是inline-level boxes并等于inline boxes(比如inline-table elements,inline-block elements,replaced inline-level elements)。那些不属于inline boxes的inline-level boxes被称作atomic inline-level boxes,这些boxes作为单独的不透明的框参与IFC。
有匿名块框就有匿名内联框(anonymous inline boxes),比如像下面这种情况:

<pre name="code" class="html"><p>some <em>name</em>      </p>

some就是匿名内联框。可以看到与匿名块框不同的是,块级框p里面包含的不是块级框了,而是内联框em,所以会产生匿名的some内联框。之所以叫他们匿名框,是因为没有相应的内联元素与之对应,但是他们的属性还是可以从p那里继承到的。
可以看到em的后面还有很多空格,根据“white-space”的特性这些空格会被压缩,所以这里不会产生匿名内联框。
匿名块框和匿名行内框统称为匿名框。

2.2.3 display属性

可取值:  inline | block | list-item | run-in | compact | marker |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适用于:   所有元素可否继承:   否百分比:   N/A媒介:   所有
display:run-in是CSS3中新添加的属性,添加了该属性的元素会根据上下文机智地作为块级元素或内联元素显示。(怎么个机智法?真坑...)
注意,尽管display初始值为inline,但是UA的初始值可能会覆盖它,比如div,p的display值默认为block。各个元素的默认值,在HTML4中就有告知:

2.3定位机制

CSS中有三大定位体系:normal flow(普通流),floats(浮动流),absolute positioning(绝对定位)。定位方式与postion属性有关:
可取值:   static | relative | absolute | fixed | inherit初始值:   static适用于:   all elements可否继承:   no百分比:   N/A媒体:     visual计算值:   as specified
其中,static,relative属于常规流,absolute和fixed属于绝对定位,如果元素的float属性不为none,那么他就属于浮动流。
绝对定位所谓的绝对其实是相对的,absolute元素是相对其第一个具有relative特性的祖先元素偏移的,如果祖先元素都是static的,那么就相对根元素偏移(UA一般会将根元素设为static)。而fixed是相对于屏幕内容区域定位的,比如有的网站导航栏时钟显示在网页上方,这就是用了fixed定位。
另外,绝对定位是脱离文档流的,他们的位置不会对普通流的定位产生任何影响。
常规流(普通流)中的boxes都属于formatting context(格式化上下文)。/*格式化上下文这翻译真心让人难以理解,context有环境之意,这句话的意思应该就是说常规流中的盒子定位时们都会受到其他盒子的影响*/之前有说到BFC和IFC,BFC就是block formatting contexts,IFC就是inline formatting contexts,看名字就知道是常规流中的一种。relative positioning也是常规流中的一种。
有关这三大定位的单独介绍网上有很多详细的资料,这里不一一展开,这里对这三者进行一个比较。
为了说明三者间差异,我们举个例子:
<!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 }
首先来看普通流,为了区分我们给文字加上颜色:
#outer { color: red }
#inner { color: blue }
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-generic.png
上面说到了,p中只有内联元素span,所以会产生匿名内联框,p中所有的内容都会作为IFC呈现。
再看一下相对定位的情况,我们更改#outer和#inner的属性:
#outer { position: relative; top: -12px; color: red }
#inner { position: relative; top: 12px; color: blue }
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-relative.png
可以看到#outer和#inner都相对于原来位置产生了偏移。
然后再看看float定位是什么效果,继续更改#outer和#inner属性:
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-float.png

最后看下绝对定位的效果:
#outer { 
    position: absolute; 
    top: 200px; left: 200px; 
    width: 200px; 
    color: red;
}
#inner { color: blue }
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-absolute.png

绝对定位和relative一起用:
#outer { 
  position: relative; 
  color: red 
}
#inner { 
  position: absolute; 
  top: 200px; left: -100px; 
  height: 130px; width: 130px; 
  color: blue;
}

效果如下:
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-abs-rel.png

如果不把#outer设为相对定位,就会像下面这样:
http://www.w3.org/TR/2011/REC-CSS2-20110607/images/flow-static.png

如果你能理解上面这些代码产生的效果,那么你就理解了三者之间的差异。

2.4 position,display,float属性之间的关系

这三个属性与盒模型定位息息相关。三者之间的相互影响遵循下面的规则:
  1. 如果display为none,那么position和float失效。因为这种情况下不产生框(注意是不产生框!如果用visibility:hidden或者opacity:0的话那叫隐藏框,虽然你看不见框,但框就在那里...);
  2. 否则,如果display为absolute或fixed,即绝对定位,这时float会失效;
  3. 否则,如果float不为none,那么display属性会发生相应的改变,具体见下表;
  4. 否则,如果元素是根元素,那么display也会按下表相应变化;
  5. 如果以上情况都不满足,就用你在CSS里指定的那个值啦~
Specified value(指定的值) Computed value(计算后的值)
inline-table table
inline, table-row-group, table-column, table-column-group, table-header-group, table-footer-group, table-row, table-cell, table-caption, inline-block block
others same as specified
看吧,如果你想给一个inline元素设置margin-top,那么你可以让它浮动一下~

其实有关定位并不仅仅只涉及到这三个属性,还有z-index也用的比较多。(有关z-index的深入介绍,戳这里:http://readit.sinaapp.com/?p=55

3.总结

这篇坑爹的文章涉及的概念很多,不理解去网上查,或者直接去w3c:http://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html。如果文中有错误请及时指出!一切后果本人概不负责!呵呵!
本文算是对盒模型做了一个比较深入的介绍,但是对visual formatting model却只是作了一个简单的介绍,后面会对visual formatting的更多细节进行学习。(又是一个坑...)

上一篇:历经6年双十一,无任何生产故障,数据管理DMS服务平台诠释DevOps在企业级数据库的最佳实践


下一篇:ERP,SCM,CRM,BRP,OMS,WMS 企业管理的6大核心系统