CSS 盒子模型详解

5,046 阅读18分钟
原文链接: blog.csdn.net

基本框

CSS假定每个元素都会生成一个或多个矩形框,这称为元素框。各元
素框中心有一个内容区(content area)。这个内容区周围有可选的内边距、边框和外边距。这些项之所以被认为是可选的,是因为它们的宽
度可以设置为0,实际上这就从元素框去除了这些项。
可以用多种属性设置各外边距、边框和内边距,如margin-left或border-bottom。内容的背景(例如某种颜色或平铺图像)也会应用到内边
距。外边距通常是透明的,从中可以看到父元素的背景。内边距不能是负值,但是外边距可以。

包含块

每个元素都相对于其包含块摆放,包含块就是一个元素的布局上下文。
对于正常的西方语言文本流中的一个元素,包含块由最近的块级祖先框、表单元格或行内块祖先框的内容边界(content edge)构成。

正常流

这是指西方语言文本从左向右、从上向下显示,这也是我们熟悉的传统HTML文档的文本布局,注意,在非西方语言中,流方向可能不同。大多数元素都在正常流中,要让一个元素不在正常流中,唯一的办法就是使之成为浮动或定位元素。这里我们谈论的都是在正常流中的元素。
块级元素
这是指段落、标题或div之类的元素。这些元素在正常流中时,会在其框之前和之后生成“换行”,所以处于正常流中的块级元素会垂直摆放。通过声明display:block,可以让元素生成块级框。
行内元素
这是指strong或span之类的元素,这些元素不会在之前或之后生成“行分隔符”,它们是块级元素的后代。通过声明display:inline。可以让元素生成一个行内框。

块级元素

水平格式化

控制盒模型水平方向的有7个值:margin-left、border-left, padding-left、width、 padding-right、border-right和margin-right。
这7个属性的值加在一起必须是元素包含块的宽度,这往往是块元素的父元素的width值。
关于auto
在这7个属性中有3个可以设置为auto:margin-left、width、margin-right。其余的要不必须是确定的值,要不就是默认值0。
有这么几种情况:

  • 3个都不是auto:按CSS的术语来讲,这些格式化属性过分受限,此时总会把margin-right强制为auto来适应父元素的宽度。
  • 2个不是auto:这个最简单,剩下的一个是auto的将自动调整到适应父级元素的宽度。
  • 两个外边距是auto:他们会自动设置为相等的长度,导致此元素在其父级元素中居中。
  • 一个外边距和width是auto:auto的外边距会减为0,width自动填充。
  • 3个都是auto:两个外边距减为0,width自动充满。

注意:由于水平外边距不会合并,父元素的内边距、边距和外边距可能影响其子元素。
负外边距
这个是允许的,也只有它是允许为负值的,这个还有好多用处呢。
百分数
百分数也是可以的,当然还是要遵守加起来为父元素宽度的限制,这里为内外边距,宽度设置的百分比都是相对父元素来说的。边框不能是百分数。
替换元素
刚才说的是非替换块级元素的水平格式化,替换块级元素其他的规则都一样,只有一个例外,当width为auto 时,元素宽度为内容的固有宽度。

垂直格式化

一个元素的默认高度由其内容确定,可以对任何块级元素设置显式高度。如果内容撑不下,根据元素的overflow属性用户代理会做出选择。
垂直属性
也是有7个:margin-top、border-top、padding-top、height、 padding-bottom、border-bottom和margin-bottom。
同样,这7个值的和必须等于其包含块的height。
同样有3个值可以是auto,其他的不行。不过上下外边距设置为auto也没有什么用,因为会被重置为0。
百分数高度
父元素的高度如果没有显式声明,那子元素的高度设置为百分数也会被重置为auto。
利用上下外边距auto垂直居中显然是不可能的。
auto高度的问题
如果块级正常流元素的高度设置为auto,而且只有块级子元素,其默认高度将是从最高块级子元素的外边框边界到最低块级子元素外边框边界
之间的距离。因此,子元素的外边距会“超出”包含这些子元素的元素(由于垂直合并外边距)。

不过,如果块级元素有上内边距或下内边距,或者有上边框或下边框,其高度则是从其最高子元素的上外边距边界到其最低子元素的下外边距边界之间的距离。
合并垂直外边距
垂直格式化的另一个重要方面是垂直相邻外边距的合并。这种合并行为只应用于外边距。如果元素有内边距和边框,它们的垂直外边距绝对不会合并。
负外边距
这个在垂直方向上也是允许的,重叠的时候,如果垂直外边距都设置为负值,浏览器会取两个外边距绝对值的最大值。如果一个正外边距与一个负外边距合并,会从正外边距减去这个负外边距的绝对值。

行内元素

除了块级元素,最常见的就是行内元素了。通过为行内元素设置框属性,可以进入到一个更有意思的领域。非替换元素和替换元素在内联内容方面的处理稍有不同,讨论行内元素的构造时我们将分别进行讨论。

一些概念

匿名文本
匿名文本(anonymous text)是指所有未包含在行内元素中的字符串。因此,在标记(p) I’m (em)so(/em) happy!(/p)中,序列“I’m”和 “happy!”都是匿名文本。注意,空格也是匿名文本的一部分,因为空格与其他字符一样都是正常的字符。
em框
em框在字体中定义,也称为字符框(character box)。实际的字形可能比其em框更高或更矮。在CSS 中,font-size的值确定了各个em框的高度。
内容区
在非替换元素中,内容区可能有两种,CSS2.1规范允许用户代理选择其中任意一种。内容区可以是元素中各字符的em框串在一 起构成的框,也可以是由元素中字符字形描述的框。
在替换元素中,内容区就是元素的固有高度再加上可能有的外边距、边框或内边距。
行间距
行间距(leading)是font-size值和line-height值之差。这个差实际上要分为两半,分别应用到内容区的顶部和底部。行间距只应用于非替换元素。
行内框
这个框通过向内容区增加行间距来描述。对于非替换元素,元素行内框的高度刚好等于line-height的值。对于替换元素,元素行内框的高度则恰好等于内容区的高度,因为行间距不应用到替换元素。
行框
这是包含该行中出现的行内框的最高点和最低点的最小框。换句话说,行框的上边界要位于最高行内框的上边界,而行框的底边要放在最低行内框的下边界。
还有几点要格外注意:

  • 内容区类似于一个块级元素的内容框。
  • 行内元素的背景应用于内容区及所有内边距。
  • 行内元素的边框要包围内容区及所有内边距和边框。
  • 非替换元素的内边距、边框和外边距对行内元素或其生成的框没有垂直效果,也就是说,它们不会影响元素行内框的高度(也不会影响包含该元素的行框的高度)。
  • 替换元素的外边距和边框确实会影响该元素行内框的高度,相应地,也可能影响包含该元素的行框的高度。
  • line-height实际上只影响行内元素和其他行内内容,而不影响块级元素,至少不会直接影响块级元素。也可以为一个块级元素设置line-height值,但是这个值只是应用到块级元素的内联内容时才会有视觉影响。

对于一个行框,其高度确定的基本步骤如下:

  1. 按以下步骤确定行中各元素行内框的高度: 得到各行内非替换元素及不属于后代行内元素的所有文本的font-size值和line-height值,再将line-height减去font-size,这就 得到了框的行间距。这个行间距除以2,将其一半分别应用到em框的顶部和底部。 得到各替换元素的height、margin-top、margin-bottom, padding-top、 padding-bottom, border-top-width和border-bottomwidth值,把它们加在一起。
  2. 对于各内容区,确定它在整行基线的上方和下方分别超出多少。这个任务并不容易:你必须知道各元素及匿名文本各部分的基线的位置, 还要知道该行本身基线的位置,然后把它们对齐。另外,对于替换元素,要将其底边放在整行的基线上。
  3. 对于指定了vertical-align值的元素,确定其垂直偏移量。由此可知该元素的行内框要向上或向下移动多远,并改变元素在基线上方或下 方超出的距离。
  4. 既然已经知道了所有行内框会放在哪里,再来计算最后的行框高度。为此,只需将基线与最高行内框顶端之间的距离加上基线与最低行内 框底端之间的距离。

行内非替换元素

我们来看看如果行中只包含非替换元素(或匿名文本)将如何构造。

建立框
首先,对于行内非替换元素或匿名文本某一部分,font-size值确定了内容区的高度。如果一个行内元素font-size为15px,则内容区的高度为 15像素,因为元素中所有em框的高度都是15像素。
下面再来考虑元素的line-height值,以及它与font-size值之差。如果一个行内非替换元素的font-size为15px,line-height为21px,则相差6 像素。用户代理将这6像素一分为二,将其一半分别应用到内容区的顶部和底部,这就得到了行内框。
假如,font-size为24px,line-height为12px。line-height和font-size之差是-12px,将其除2来确定半间距(-6px),再把这个半间距分别增加到内容区的顶部和底部,就得到了行内框。由于这里增加的都是负数,所以最后行内框高度为12像素。12像素高的行内框在元素内容区(24像素高)中垂直居中,所以行内框实际上小于内容区。 那么行框也就可能小于内容区了,那就意味着内容会重叠到相邻行。

垂直对齐
vertical-align各个关键字值的效果描述如下:

  • top:将元素行内框的顶端与包含该元素的行框的顶端对齐。
  • bottom:将元素行内框的底端与包含该元素的行框的底端对齐。
  • text-top:将元素行内框的顶端与父元素内容区的顶端对齐。
  • text-bottom:将元素行内框的底端与父元素内容区的底端对齐。
  • middle:将元素行内框的垂直中点与父元素基线上0.5ex处的一点对齐。
  • super:将元素的内容区和行内框上移。上移的距离未指定,可能因用户代理的不同而不同。
  • sub:与super相同,只不过元素会下移而不是上移。
  • percentage:将元素上移或下移一定距离。这个距离由相对于元素line-height值指定的一个百分数确定。

管理line-height
在前几节中我们已经了解到,改变一个行内元素的line-height可能导致文本行相互重叠。不过,在所有情况下,这种修改都是针对单个元素的,所以,如何以一种更一般的方式影响元素的line-height而避免内容重叠呢?
一种方法是对font-size有改变的元素结合使用em单位。

Not only does this paragraph have "normal" text, but it also
contains a line in which some big text is found.
This large text helps illustrate our point.

p { font-size: 14px; line-height: 1em; } p span { font-size: 250%; line-height: 1em; }

或者使用line-height数值值会被直接继承而不是计算继承的特性。

p {fpnt-size: 14px; line-height: 1;}
p span {font-size: 250%;}

基线与行高
各行框的具体高度取决于其组成元素相互之间如何对齐。这种对齐往往很大程度上依赖于基线落在各元素(或匿名文本各部分)中的哪个位置,因为这个位置确定了其行内框如何摆放。基线在各em框中的位置对于不同的字体是不同的。这个信息内里在字体文件中,除非直接编辑字体文件,否则无法修改。

缩放行高
设置line-height的最好办法是使用一个原始数字值。之所以说这种方法最好,是因为这个数会成为缩放因子,而该因子是一个继承值而非计算值。假设你希望一个文档中所有元素的line-height都是其font-size的1.5倍,可以如下声明:

body {line-height: 1.5;}

缩放因子1.5在元素间逐层传递,在各层上,这个因子都作为一个乘数与各元素的font-size相乘。

增加框属性
内边距,外边距和边框都可以应用于行内非替换元素,行内元素的这些方面根本不会影响行框的高度。

对于边框来说:
行内元素的边框边界由font-size而不是line-height控制。换句话说,如果一个span元素的font-size为12px,line-height为36px,其内容区就是12px高,边框将包围该内容区。

对于内边距来说:
左右内边距是有作用的。
上下内边距是会影响到元素行内框的高度,如果有边框的话会看到边框外扩了,有背景的话背景也会变大。但是行框的高度是不会改变的,行间距自然也不变。

对于外边距来说:
左右外边距是有作用的。
上下外边距根本不会应用别想了。

要注意的是:
尽管内边距和外边距(以及边框)不影响行高,但是它们确实能影响一个元素内容的布局,可能将文本推离其左右两端。实际上,如果左、右外边距为负,可能会把文本拉近行内元素,甚至导致重叠。上下内边距过大的话背景和边框会扩到另一行。
这个例子可以试试:

Not only does this paragraph have "normal" text, but it also
contains a line in which some big text is found. This Baidu large text helps illustrate our point.

span{ border:1px solid red; line-height:1; padding:4px; margin:4px; background-color: rgba(255,1,1,0.5); }

行内替换元素

一般认为行内替换元素(如图像)有固有的高度和宽度。有固有高度的替换元素可能导致行框比正常要高。这不会改变行中任何元素的line-height值,包括替换元素本身。相反,只是会让行框的高度恰好能包含替换元素(以及所有框属性)。换句话说,会用替换元素整体(包括内容、外边距、边距和内边距)来定义元素的行内框。

Not only does this paragraph have "normal" text, but it also
contains a line in which is found.
This test image large text helps illustrate our point.

p { font-size: 15px; line-height: 18px; } p img { height: 30px; margin: 0; padding: 0; border: none; }

这个例子里line-height是18px,img的height是30px。所以img的行内框是30px。行框的高度也因此被撑高了。尽管是这样,但不论是段落还是图像本身的line-height的有效值并没有因此改变。line-height对图像的行内框没有任何影响。
然而行内替换元素还是需要line-height来作为一个垂直对齐时的基准。vertical-align的百分数值要相对于元素的line-height来计算。

p img {
   vertical-align: 50%;
}

比如刚才的例子再加一个这个,那img就会往上升18*50%px。
还有就是替换元素会继承这个line-height,它本身或它的后代有可能会用到这个值。
增加框属性
在行内替换元素上应用框属性是确实会影响到行框的高度的。因为不同于行内非替换元素,它们会作为替换元素行内框的一部分。
甚至是外边距都会被包含在元素的行内框中,负的外边距也有用哦,会减少替换元素的行内框。
替换元素和基线
默认地,行内替换元素位于基线上,如果向替换元素增加下内边距、外边距或边框,内容区会上移。替换元素并没有自己的基线,所以相对来讲最好的办法是将其行内框的底端与基线对齐。因此,实际上是下外边距边界与基线对齐。

改变元素显示

比如将a标签改为块显示,将li改为行内显示等,使用display属性,我们可以将元素的显示角色改变。不过要注意的是,你改变的只是元素的显示角色,而不是其本质。换句话说,让一个段落生成行内框并不会把这个段落真正变成一个行内元素。因此,尽管链接可以放在一个段落中,但是链接却不建议包围段落。
行内块元素
看上去display:inline-block是一个混合产物,实际上也确实如此。
行内块元素作为一个行内框与其他元素和内容相关。换句话说,它就像图像一样放在一个文本行中,实际上,行内块元素会作为替换元素放在行中。这说明,行内块元素的底端默认地位于文本行的基线上,而且内部没有行分隔符。在行内块元素内部,会像块级元素一样设置内容的格式。就像所有块级或行内替换元素一样,行内块元素也有属性width和height,如果比周围内容高,这些属性会使行高增加。下面的例子可以用作对比:

This text is the content of a block-level level element. Within this block-level element is another block-level element.

Look, it's a block-level paragraph.

Here's the rest of the DIV, which is still block-level.
This text is the content of a block-level level element. Within this block-level element is an inline element.

Look, it's an inline paragraph.

Here's the rest of the DIV, which is still block-level.
This text is the content of a block-level level element. Within this block-level element is an inline-block element.

Look, it's an inline block paragraph.

Here's the rest of the DIV, which is still block-level.
div { margin: 1em 0; border: 1px solid; } p { border: 1px dotted; } div:nth-child(1) p { display: block; width: 6em; text-align: center; } div:nth-child(2) p { display: inline; width: 6em; text-align: center;} div:nth-child(3) p { display: inline-block; width: 6em; text-align: center; }

有时行内块元素很有用,例如:如果有5个超链接,你希望它们在一个工具条中宽度相等。为了让它们分别占其父元素宽度的20%,但是仍保持其为行内元素,可以声明如下:

#navbar a {display: inline-block; width: 20%;}

计算值
display也是有计算值哒,如果元素是浮动元素或定位元素,display的计算值可以改变。如果为一个根元素声明display值,计算值也可以改变。实际上,display、
position和float值会以很有意思的方式相互影响。