深入理解CSS盒子模型
前言:前阵子在做一个项目时,在页面布局方面遇到了一点小问题,于是上stackoverflow上求助。ifaou在帮助我解决我问题的同时,还推荐我阅读一篇有关CSS盒子模型的文章《The CSS Box Model》,阅读之后受益匪浅,才知道自己对盒子模型知识还是如此欠缺。恰逢学期末,项目验收后暂时告一段落,有空闲的时间。于是想把这篇文章翻译出来,一方面再给自己一点挑战和锻炼,另一方面也给大家参考,让更多的人受益。
这篇文章适合初级web设计朋友,让你对盒子模型有更近一步的理解。但是在阅读这篇文章之前你应该对盒子模型html等基础知识是有一定的了解,否则只会让你更加云里雾里。这篇文章不会像很多的中文教程那样,把盒子模型内边距,外边距,如何定位等概念有条不紊的介绍一遍。这篇文章更多的是教你在实战过程中如何自如的控制盒子的宽度,如何用最恰当的方法定位盒子,如何解决IE中盒子的种种bug。
之所以翻译这篇外国文章,是因为我在学习的过程中也发现,国外的教程和过内的相比,它们授人以渔,更授人以鱼 。不同于国内教程有章有节有点有条目,他们的叙述方式更接近谈话式的由浅入深……总而言之,目的在于给更多的初学者提供一点启示和帮助,不必像我一样走了许多弯路。
这是我第一次翻译,有欠缺的地方请大家指正。我尽量把它翻译的通俗易懂,同时也加入了一些自己的语言自己的体会,和帮助理解的图示(为了阅读流畅,也就不另表示了)。附上原文地址:http://reference.sitepoint.com/css/boxmodel
深入理解CSS盒子模型(The CSS Box Model)
如果你了解盒子模型(box model)的概念,了解它是如何决定某一个元素的最终尺寸的话,会有助你理解一个元素如何在网页上定位的。盒子模型主要适用于块级元素。顺便提一个与此相关的概念:行内布局模型(inline layout model)——定义了行内元素是如何定位的,在行内元素格式(InlineFormatting)中有具体说明
盒子的尺寸的计算(Calculating BoxDimensions)
在CSS2.1中,块级元素只能是矩形形状的。当我们需要计算一个块级元素的整体尺寸时,需要同时把内容区域(content area)[注释1]的长宽,连同此元素的外边距,内边距,以及边框都计算在内
[注释1]:此文章中频繁提到content 和content area这两个概念,虽然从字面上都可以理解为盒子中的内容区域(content),但从后文的叙述来看,这两个概念还是有区别的,到这篇文章发布为止,我还是对这两者的区别有迷惑,希望有兴趣阅读原文的朋友能留言告诉我这两者的区别,以便于我更正文中的错误。
我们可以通过声明宽和高来定义一个元素的内容(content)的宽度和高度。如果没有做任何的声明,宽度和高度的默认值将是自动(auto)
w3schools上对于盒子模型的图示如下
在图的下方有一段很重要的话Important: When you specifythe width and height properties of an element with CSS, you are just settingthe width and height of the content area.也就是说当我们在css中设计一个块级元素的width和height属性时比如.box{width :100px; height:100px}时,其中的width 和height仅仅是对content部分设置的,即定义上图中padding下虚线方框内区域的长和宽。而不是内容,内边距,边框的总和(但在IE的早期版本包括IE6中,盒子模型的width和height却恰恰是这样定义的,尽管符合人们思考的逻辑,但是不符合规范,这会造成严重的问题)
对宽度为自动状态的静态(static)定位元素(即无定位),和相对定位(relatively positioned)元素来说,计算宽度的方法是,将他们包含块(containing block)[注释2]的宽度减去此元素的横向的所有外边距,内边距,边框,滚动条。也就是说,从包含块的宽度中除去元素的横向外边距,内边距,边框,滚动条(如果存在的话)的宽度,所剩的值就是了。
[注释2]:包含块(containing block)。如果你知道绝对定位和相对定位的实现原理的话,这个注释可以忽略。包含块可以理解为一个矩形,而这个矩形的作用是为它里面包含的元素提供一个参考,内部元素的尺寸和位置的计算往往是由该元素所在的包含块决定的。例如在绝对定位中,距离它最近的已定位(position为fixed,relative或absolute)的祖先元素即为包含块。这算比较初级的概念,不深究,请百度。
包含块的定位属性和尺寸被作为后代元素定位和尺寸计算的参考。尽管元素的定位必须遵从与他们的块级元素来进行定位,但是他们也非受限于它。后代的元素也可以溢出包含块。在大多数情况下, generated boxes[注释3]通常扮演着子代元素包含块的角色。想要充分了解包含块的大量细节信息请点击ContainingBlock
[注释3] :不知道generated boxes应该如何翻译,有知道的朋友请告知,谢谢
对于浮动(floated)和绝对定位(absolutelypositioned)元素(包括固定定位(fixed)元素)来说, 自动状态的宽度会使generated box收缩到紧贴它内容大小。
我觉得我在这里有必要总结一下前两段话的意思,前两段话都是在说在宽度缺省的情况下(auto),盒子模型自动设置宽度的两种方式。
我们知道,当我们放置一个块级元素于页面上时,并且不设置它的定位属性(relative,absolute,fixed),即position:static,或者设置了position:relative的情况下,块的宽度是延伸自动填充满它的父元素的宽度区域
举个例子:
以下为引用的内容: .box1 { background:black; color:White; height:100px; padding:10px; border:20px solid Red; margin:30px; } |
注意.box1没有设置任何的position属性,也没有宽度属性。这是为了实验相对定位或者无定位的情况
Html代码:
以下为引用的内容: <h2>Static or Relative Box</h2> <div class="box1">For floated or absolutely positioned </div> |
结果为:
即块的宽度是延伸自动填充满它的父元素的宽度区域。
所以当我们在计算一个被包裹中的元素的宽度时,我们只需要用父元素减去这个元素的外边距,边框,内边距,和scrollbar,剩下就是它的宽度,因为它会自动填充。虽然这个基本上没有实际应用可言……
但是浮动floated元素和绝对定位元素,他们的结果却恰恰相反,他们会收缩以致包裹紧贴内容,同样举个例子
以下为引用的内容: .box2 { background:black; color:White; height:100px; padding:10px; border:20px solid Red; margin:30px; position:absolute; } |
这里同样没有设置宽度,但是进行了绝对定位,html代码:
以下为引用的内容: <h2>Absolute or Float</h2> <div class="box2">For floated or absolutely positioned </div> |
结果:
浮动元素和宽度
之前,在CSS2中,没有声明宽度的浮动元素,不会收缩并于紧贴(shrink to wrap)住他们的内容,恰恰相反的是,他们会不断的延伸至与他们父元素最大宽度相当。这种行为在CSS2.1中被收缩紧贴代替了。但是无论如何,在IE6和更早的浏览器版本中,一个没有声明宽度的浮动元素还是会收缩紧贴住它的内容作为一种默认的设置,除非子元素有自己的布局(has a layout)[注释3],比如说在这种情况下,父元素会伸展来适应父元素中的可用内容的宽度
[注释3]:”IE的表现与其它浏览器不同的原因之一是,显示引擎使用了一个称为布局的内部概念……windows上的IE使用布局概念来控制元素的尺寸和定位。那些“拥有布局”的元素负责本身及其子元素的尺寸设置和定位。如果一个元素“没有拥有布局”,那么它的尺寸和位置由最近的拥有布局的祖先元素控制……布局是许多IE显示bug的根源”——摘自Andy Budd、Simon Collision、Cameron Moll的《CSS Mastery Advanced WebStandards Solutions(Second Edition)》(精通CSS 高级Web标准解决方案 第二版)
正如以上所说,对于没有声明宽度的浮动元素,在IE6和之前的版本都是采取收缩紧贴内容的行为,所以这种说法无从考证了,我也在IETest下测试过,的确如此
同样应该被注意的是,当一个浮动元素(没有声明宽度)包含一个向右浮动的子元素时,它同样也会自动的延伸填充,以适应父元素的可用内容的宽度。这种情况在之前的IE浏览器,包括IE7中都是存在的(之前的Firefox,包括2.0版本中也存在这个bug,但是这个问题在Firefox3.0 Alpha 6版本中已经得到了解决)。
我也为以上这段话写了个例子给大家:
以下为引用的内容: <div class="parent"> <div class="containing-block"> </div> |
对照一面那段话:首先是一个浮动元素containing-block,包含一个向右浮动的子元素box,父元素为parent……
以下为引用的内容: .box { background:black; color:White; height:100px; padding:10px; border:20px solid Red; margin:30px; float:right; } .containing-block { background:blue; height:250px; float:right; } .parent { background:gray; height:300px; width:500px; } |
在Firefox中运行结果为:
但是在IETester中用IE5.5运行的效果是:
所以,为了避免上面提及的bug,如果可能的话,尽量为一个浮动元素指定一个具体的宽度值,总是会比较安全一点的。无论如何,只要你总是有留意到以上提及的问题,你或许会发现无宽度的浮动在一些场合下还是非常管用的,比如横向流式(fluid-width)布局菜单
无论内容区域如何定位,如果高度,最小高度(min-height),最大高度(max-height等没有被声明的话,内容区域高度与内容的高度是相等的[注释4]。
[注释4]:这里就是让我对content area和content产生疑惑的地方,不知道有没有朋友能帮我看看,帮我解释一下
所以,当你为了弄清把一个元素放在页面上究竟需要多大尺寸时,把已经声明内边距,边框,外边距值统统与内容区域加上。当然,如果一个元素没有内边距,边框,外边距的话,也就是它的尺寸仅仅是由它的内容来决定
如果一个元素仅仅包含浮动或者绝对定位的元素,它没有一丁点的内容,那么它的高度将为0。我们将在Floating and Clearing做更多讨论
实现盒子模型
说明盒子模型的最好方式是用一个简短的例子。我们用计算来看看在页面上放置一个元素总到底需要多大的尺寸(这里先忽略外边距的叠加效果——以下会有更详细有关这个的说明):
Total width = left margin + left border + left padding + width +
right padding + right border + right margin
Total height = top margin + top border + top padding + height +
bottom padding + bottom border + bottom margin
这里是我们的CSS样例——为了一个类名为’box’的元素声明盒子的各个属性:
以下为引用的内容: .box { width: 300px; height: 200px; padding: 10px; border: 1px solid #000; margin: 15px; } |
以上的元素总共被计算出来的的尺寸是:
Total width = 15 + 1 + 10 + 300 + 10 + 1 + 15 = 352px
Total height = 15 + 1 + 10 + 200 + 10 + 1 + 15 = 252px
以上的计算在图1中给出了具体描述,这是从Firebug元素布局展示中截下的图,
在图1中,我们可以很清晰的看到内容区域处于中央,围绕着内容区域的内边距区域,边框区域,外边距区域。内容区域的外边缘称为内容边缘(content edge),或者是内边缘(inner edge);内边距区域的外边缘被称为外边距边缘(padding edge);边框区域的外边缘被称为边框边缘(border edge);外边距区域的外边缘被称为——你应该可以猜到了——外边距边缘(margin edge)或外边缘(outer edge)
你可以从这个简短的例子中看到,为了使这个元素适应这个页面,我们需要一个至少352px 宽度和252px高度的区域。如果可用的区域小于这个,这个元素会错位,或者溢出它的包含块。注意到IE6或者更早的浏览器版本中,在这种情况下会伸展包含块的以容纳内容区域的多余高度,这很明显会扰乱网页的布局。但其它版本的浏览器会使元素溢出它的边界,同时忽略内容。
这个Bug翻译过来是:“如果元素的内容比元素本身大,那么我们希望内容溢出到元素外。但是,在IE6和更低版本中,拥有布局的元素会错误的拓展以便适应内容的尺寸……这种错误意味着Window上的IE中的width实际上更像是min-width”——Andy Budd、Simon Collision、Cameron Moll的《CSS Mastery Advanced Web Standards Solutions(Second Edition)》(精通CSS 高级Web标准解决方案第二版),给大家演示一下,很简单的:
以下为引用的内容: <div class="IEbug"> </div> |
.IEbug { background:yellow; width:100px; height:100px; } |
火狐下测试结果:
IE6下测试结果:
留意外边距叠加效应
尽管在上面的计算元素所需区域大小的例子中外边距已经在计算中包括在内了,但是需要注意的是纵向的无定位(static)元素的相邻外边距会叠加合成为其中一个较大宽度的外外边距的值,并非两者之和。这就意味当计算实际上需要存放一个元素的区域大小时,并不是从外边距的边缘开始算起,只有最宽的外边距会生效,并且较窄的外边距会与较大的叠加在一起,请阅读CollapsingMargins来了解更多有关这相当复杂的一个主题。
实际盒子模型应用
非常值得注意的一点是,当一个元素的宽度被设置为100%时(也就是说父元素的内容宽度是100%),它不应该有任何的外边距,内边距,或者是边框,这只会使它放置的区域需要更大的面积。这通常会被设计师们所忽略忽略并且很严重的扰乱了页面的布局,这样的话内容要么溢出要么使元素比他们应该的样式更宽
以下为引用的内容: <div class="contain"> |
没有margin和padding,仅有width情况下的CSS:
以下为引用的内容: .box { background:black; width:100%; height:100px; } .contain { background:yellow; height:120px; width:200px; } |
效果如图所示:
可见在没有margin和padding的情况下,100%的内容能恰到好处的填充父元素
一旦加入了margin和padding(margin:10px;padding:10px;)之后效果如图:
在firebug中的布局图:
而加入了margin和padding之后,元素出现了错位,并且只能显示左侧的margin
解决办法是,在大多数情况下,避免给宽属性添加具体的值(绝不是自动),并且只应用外边距,内边距,边框。无定位元素的宽度将会默认为自动。甚至就算定义了内边距,边框,外边距,它仍然会认为可用的内容宽度为全满状态。
当然,这种方法对某些例子可能失效,比如当元素不是无定位元素时,并且它的确需要一个指定的宽度(就像那个浮动元素不会自动填充它父元素的例子一样)。在这些情况下,你有两个选择。
如果可用区域是固定宽度的,你能简单的把每一个属性元素(margin,padding等)的宽度都相加起来以匹配可用的那个固定宽度。举个例子,如果可用的区域宽度是500px,并且你需要一个元素有20px的内边距,简单的把这个元素的宽度设置为460px,内边距为20px(20+460+20=500)。这个办法中先决条件是宽度值和元素盒子属性使用的都是同一测量单位,因为你不希望把混合单位相加起来(200px + 10%,只是举个例子而已,在内容中这样的写法是没有意义的)。
当可用内容区域的宽度是未知的时候——比如在流式布局(fluid layout)中——这个方法是行不通的,因为百分比(percentages)和像素(pixels)不能一起相加。在这种情况下,解决方法应该是为需要的元素声明一个100%的宽度值,并且把内边距,边框,外边距的值都设置到一个嵌套其中的元素中去。这个嵌套元素没有任何的宽度值声明,并且可以在没有干扰父元素的情况下展示需要的内边距,边框,外边距。[注释5]
[注释5]呃,其实这段话我也没有理解……
现在你应该能清晰的理解CSS盒子模型了
后记:由于本人水平有限,可能很多地方翻译的并不是很通俗。只希望能起一个抛砖引玉的作用,有更多的朋友来完善它,指出我翻译和理解不正确的地方。如果时间允许的话,我继续翻译与此相关的一些列文章(即本文中的链接)。
最后向大家推荐Andy Budd、Simon Collision、Cameron Moll写的《CSS Mastery Advanced Web Standards Solutions(Second Edition)》(精通CSS 高级Web标准解决方案第二版),翻译这篇文章时参考了其中不少内容,这也是一本适合CSS进阶类图书,不适合初学者。希望有兴趣的朋友可以参考一下。