CSS 基本视觉格式化
1 基本框
CSS假定每个元素都会生成一个或多个矩形框(未来可能包括非矩形框),这称为元素框。各元素框中心有一个内容区(content area)。这个内容区周围有可选的内边距(padding)、边框(border)和外边距(margin),这些元素的宽度都可以设置为0。
外边距通常是透明的,可以看到父元素的背景。内边距不能是负值,但外边距可以。
1.1 包含块
每个元素相对于其包含块摆放,包含块就是一个元素的“布局上下文”。
1
2
3
4
5
<body>
<div>
<p>This is a paragraph.</p>
</div>
</body>
p
元素的包含块是div
元素,div
的包含块是body
元素。
1.2 概念复习
-
正常流:指西方语言从左向右,从上向下显示的传统HTML文档的文本布局(有些语言可能从右向左)。要让一个元素不在正常流中,唯一的办法就是使之成为浮动
float
或定位position
元素。 -
非替换元素:如果元素的内容包含在文档中,则称之为非替换元素。例如,一个段落的文本内容都放在该元素本身之内,这个段落就是一个非替换元素。
-
替换元素:指用作为其他内容占位符的一个元素。经典例子是
img
元素,指向一个图像文件,这个文件将插入到文档流中img
元素本身所在的位置。大多数表单元素可以体会(<input type="radio" />
)。 -
块级元素:指段落、标题或
div
之类的元素,这些元素会在其框之前和之后生成“换行”,所以处于正常流中的块级元素会垂直摆放。通过声明display: block
来让元素生成块级框。 -
行内元素:指
strong
或span
之类的元素,这些元素不会在之前或之后生成“行分隔符”,它们是块级元素的后代。通过声明display: inline
来让元素生成一个行内框。 -
根元素:位于文档顶端的元素。在HTML中是
html
元素,在XML文档中则可以是该语言允许的任何元素。
2 块级元素
一个元素的width
被定义为从左内边界到右内边界的距离,height
则是从上内边界到下内边界的距离。
2.1 水平格式化
width影响的是内容区的宽度
1
<p style="width: 200px;">wideness</p>
这段代码使段落的内容区宽度为200像素。
如果指定了内边距、边框或外边距,这些都会增加到宽度值:
1
<p style="width: 200px; padding: 10px; margin: 20px;">wideness?</p>
可见元素框的宽度现在是220像素,因为内容的左边右边分别增加10像素的内边距。外边距则会在左右在延伸20像素,使整个元素框的宽度为260像素。
有一个简单的规则,正常流中块级元素的水平部分总和就等于父元素的width。
2.1.1 水平属性
7大属性:margin-left
、border-left
、padding-left
、width
、padding-right
、border-right
、margin-right
。
这7个属性的值加在一起必须是元素包含块的宽度,这往往是块元素的父元素的width。这之中有3个可以设为auto:元素内容的width,以及左、右外边距。其余属性必须设置为特定的值,或者默认宽度为0。CSS允许浏览器为width设置一个最小值;块级元素的width不能小于这个值。对于不同浏览器,这个最小值可能不同,因为在规范中对此没有定义。
2.1.2 使用auto
如果设置width、margin-left或margin-right中的某个值为auto,而余下的两个属性为指定的特定的值,那么设置的auto属性会确定所需的长度,从而使元素框的宽度等于父元素的width。
假设父元素的宽度为400px:
1
2
3
4
5
p {
margin-left: auto; /*auto 将会是200px*/
margin-right: 100px;
width: 100px;
}
如果三个都是设置了某个固定值但加起来不到400px,这些属性就会过分受限(overconstrained),此时总会把margin-right强制为auto(从左向右读的语言,如果是从右向左读的语言则是margin-left变成auto)。
1
2
3
4
5
p {
margin-left: 100px;
margin-right: 100px; /*margin-rigth 将会是auto,计算值是200px*/
width: 100px;
}
如果左右边距都设置为100px,width是auto,with值将设置为所需的某个值,从而达到需要的公宽度(即父元素的内容宽度):
1
2
3
4
5
p {
margin-left: 100px;
margin-right: 100px;
width: auto;
}
等价于
p {margin-left: 100px; margin-right: 100px;}
如果左右边距都设置为auto,width是固定值100px,元素将会在父元素中水平居中:
1
2
3
4
5
p {
width: 100px;
margin-left: auto;
margin-right: auto;
}
这不同于text-align只应用于块级元素的内联内容。
设置元素大小的另一种方法是将某个外边距以及width设置为auto。设置为auto的外边距会减为0:
1
2
3
4
5
p {
margin-left: auto;
margin-right: 100px;
width: auto;
}
然后width会设置为所需的值,使得元素完全充填其包含块。
如果三个都设置为auto的话,结果是两个外边距都会设置为0,而width会尽可能宽。注意父元素的内边距、边距和外边距可能影响其子元素。
1
2
3
4
5
6
7
8
9
10
div {
padding: 30px;
background: silver;
}
p {
margin: 20px;
padding: 0;
background: white;
}
2.1.3 负外边距
1
2
3
4
5
6
7
8
9
10
div {
width: 400px;
border: 3px solid black;
}
p.wide{
margin-left: 10px;
width: auto;
margin-right: -50px;
}
效果如下:
子元素的宽度大于父元素,但数学计算并没有错:10px + 0 + 0 + 440px + 0 + 0 - 50px = 400px。440px是width: auto
的实际计算值,因为需要这样的一个数与等式中余下的值相抵,从而使总和为400px。尽管这导致了子元素超出了父元素,但并没有违反规范,因为7个值加在一起仍等于所需的总宽度。这在语义上有点牵强,但确实是合法的行为。
下面再加上一些边框:
1
2
3
4
5
6
7
8
9
10
11
div {
width: 400px;
border: 3px solid black;
}
p.wide{
margin-left: 10px;
width: auto;
margin-right: -50px;
border: 3px solid gray;
}
计算出来的width会减少:10px + 3px + 0 + 434px + 0 + 3px - 50px = 400px
如果进一步设置内边距,width的值会继续减少。与此相反,还有可能将auto右外边距计算为负值。如果其他属性的值要求右外边距的为负,以便满足元素的7项水平属性的总和不能比其包含块更宽。如width设置为500px(大于父元素):10px + 3px + 0 + 500px + 0 + 3px - 116px = 400px。此时右外边距的值为-116px,即使为指定了另一个值,由于元素水平属性过分受限有一个规则,要求重置右外边距(从左向右的语言),这也会得到一个负外边距。注意:内边距、边框和内容宽度(及高度)不可能为负。只有外边距能小于0。
2.1.4 百分数
如果width、内边距和外边距设置为百分数,会应用同样的基本规则(总和等于父元素的内容宽度)。百分数相对于包含块宽度width计算的。应避免混合使用长度和百分数,另外边框的宽度不能使用百分数。
2.1.5 替换元素
前面讨论的非替换元素的所有规则同样适用于替换元素,只有一个例外:如果width为auto,元素的宽度则是内容的固有宽度。下例中的图像宽度是20像素(原图像的宽度):
1
<img src="smile.png" style="display: block; width: auto; margin: 0;"/>
如果实际图像的宽度是100像素,那么元素的宽度也是100像素。
可以为width指定一个特定值覆盖这个规则,但注意同时height也会成比例地变化,除非height自己也显式设置为一个特定值。同样地,如果设置了height,但width保持为auto,则width将随height的变化成比例调整。
2.2 垂直格式化
一个元素的默认高度由其内容决定。高度还会受内容宽度的影响:段落越窄,相应地就会越高,以便容纳其中所有的内联内容。CSS中,可以为任何元素设置显示高度。如果设置的高度大于元素内容的高度,则会产生有“内边距”的视觉效果。如果设置的高度小于显示内容所需的高度,用户代理的具体行为将取决于overflow
属性的值。
像width一样,height定义了内容区的高度,而不是可见元素框的高度。元素框上下的内边距、边距或外边距都会增加到height值。
2.2.1 垂直属性
7个属性:margin-top
、border-top
、padding-top
、height
、padding-bottom
、border-bottom
、margin-bottom
,这些属性如下图所示:
这7个属性的值之和必须等于包含块的height。这往往是块级元素的父元素的height值(因为块级元素的父元素几乎都是块级元素)。
有3个可以设置为auto:元素内容的height以及上、下外边距。上下内边距和边框必须为特定的值,或者默认的0(如果没有声明border-style
)。如果已经设置了border-style,边框的宽度会设置为medium(这个值的定义并不明确)。
值得注意的是:如果正常流中一个块元素的margin-top或margin-bottom设置为auto,它会自动计算为0。遗憾的是,如果值为0,就不能很容易的将正常流元素在其包含块中垂直居中(对于定位元素来说处理可能有所不同)。height必须设置为auto或者是某种类型的非负值。
2.2.2 百分数高度
如果一个块级正常流元素的height设置为一个百分数,这个值则是包含块height的一个百分数。以下标记中相应段落的高度将是3em:
1
2
3
4
5
<html>
<div style="height: 6em;">
<p style="height: 50%;">Half as tall</p>
</div>
</html>
由于将上、下外边距设置为auto时,实际上它们的高度将是0,因此,将元素垂直居中的唯一办法就是把上、下外边距都设置为25%。不过如果没有显示声明包含块的height,百分数高度会重置为auto。如果修改上例,使div的height为auto,段落将与div本身的高度完全相同:
1
2
3
<div style="height: auto;">
<p style="height: 50%;">NOT Half as tall;height reset to auto</p>
</div>
2.2.3 auto高度
最简单情况下,如果块级元素设置为height: auto,显示时其高度将恰好足以包含其内联内容(包括)文本的行盒。
如果块级正常流的元素高度设置为auto,而且只有块级子元素,其默认的高度将是从最高块级子元素的外边框边界到最低块级子元素边框边界之间的距离。因此,子元素的外边距会“超出”包含这些子元素的元素。如果元素有上内边距或下内边距,或者有上边框或下边框,其高度则是从其最高子元素的上外边距边界到其最低的子元素的下外边距边界之间的距离。(注:有点难理解,将在下一节2.2.4说明)
1
2
3
4
5
6
7
<div style="height: auto; background: silver;">
<p style="margin-top: 2em; margin-bottom: 2em;">A paragraph</p>
</div>
<div style="height: auto; border-top: 1px solid; border-bottom: 1px solid; background: silver;">
<p style="margin-top: 2em; margin-bottom: 2em;">Another paragraph!</p>
</div>
2.2.4 合并垂直外边距
垂直格式化的另一个重要方面是垂直相邻外边距的合并。合并行为只应用于外边距。如果元素有内边距,它们不会合并。
1
2
3
4
li {
margin-top: 10px;
margin-bottom: 10px;
}
每个列表项有10px的上外边距和15像素的下外边距。不过,在显示这个列表时,相邻列表之间的距离是15像素,而不是25像素。因为相邻外边距会沿着竖轴“合并”。两个外边距中较小的会被一个较大的合并。也可以用“重叠”来理解合并。下图展示了合并外边距与未合并外边距之间的差别:
如果相邻有多个外边距,也会出现合并。考虑一下规则:
1
2
3
ul { margin-bottom: 15px;}
li { margin-top: 10px; margin-bottom: 20px;}
h1 { margin-top: 28px;}
列表中最后一项的下外边距为20px,ul的下外边距为15px,后面的h1的上外边距为28px。所以一旦合并这些外边距,li的结尾到h1的开始之间有28px的距离,如下图所示。
回到上一节2.2.3的例子,在包含块上设置边框或内边距时,会使其子元素的外边距包含在包含块内。为了解这种行为,可以在前面的例子中为ul增加一个边框:
1
2
3
ul { margin-bottom: 15px; border: 1px solid;}
li { margin-top: 10px; margin-bottom: 20px;}
h1 { margin-top: 28px;}
做了这个改变之后,li元素的下外边距现在放在其父元素内部(ul)。因此,这里只会在ul和h1之间发生外边距合并,如下图所示:
2.2.5 负外边距
负外边距会影响外边距如何合并:
- 如果垂直外边距都设置为负值,浏览器会取两个外边距绝对值的最大值;
- 如果一个正外边距和一个负外边距合并,会从正外边距减去这个负外边距的绝对值。
1
2
3
ul { margin-bottom: 20px;}
li { margin-bottom: -15px;}
h1 { margin-top: -18px;}
两个负外边距中较大的一个(-18px)增加到了最大的正外边距上(20px),这就得到了20px-18px=2px。因此,列表项内容底端与h1内容顶端之间只有2个像素的距离:
由于负外边距而导致元素彼此重叠,很难区分哪些元素在上面。如果使用了背景色,其内容可能会被后面的背景色覆盖。这是一种可预见的行为,因为浏览器总会按从前到后的顺序显示元素,所以文档中后出现的正常流元素可能会覆盖较早出现的元素(如果这两个元素重叠)。
2.3 列表项
列表项有自身的一些特殊规则。这些列表项前面通常有一个标志,如一个圆点或一个数字。这个标志实际上并不是列表项内容区的一部分。
CSS1对这些标志相对于文档布局的放置和效果涉及很少。CSS2引入了专门为解决这个问题而设计的一些属性,如marker-offset
。不过,因为缺少实现,这个属性在CSS2.1中又被去掉了。所以目前标志的放置不在创作人员的控制范围内。
与一个列表项元素关联的标志可能在列表项之外,也可能处理为内容开始处的一个内联标志,这取决于属性list-style-position
的值。
3 行内元素
注:本节介绍的所有行为都不适用于表元素。CSS2对于表和表内容的处理引入了一些新的属性和行为,表元素的表现与块级元素或行内元素大相径庭。
3.1 行布局
对于行内元素来说,布局没有块级元素那么简单和直接,块级元素只是生成框,通常不允许其他内容与这些框并存。为了理解如何生成行,首先来考虑这样一种情况,一个元素包含一个很长的文本行,如下图所示。注意,这里将整行包围在一个span元素中,从而为这一个行加了一个边框,然后为之指定边框样式。
1
span {border: 1px dashed black;}
来看更熟悉的情况,你所要做的只是确定元素应当有多宽,然后断行,使断开的各部分能适应元素的宽度刚好放下。这样就得到了下图所示的结果:
在上图中,每个文本行的边框刚好与各行的顶端和底端吻合(没有设置内边距或外边距)。实际上边框彼此稍有些重叠,因为边框实际上画在各行之外的一个像素上。由于各行紧挨着,所以其他边框会重叠。
如果改变span样式,使其有一个背景色,这些行的具体摆放就很清楚了:
可以看到并不是每一行都达到其父段落内容区的边界。对于左对齐的段落,行都压到段落内容区的左边对齐,各行在换行处结束。右对齐的段落则刚好相反。居中段落中,文本行的中心与段落的中心对齐。在最后一种情况下,即text-align
的值为justify
,各行必须与段落的内容区宽度相等,所以行边界要达到段落的内容边界。要调整各行中字母和字之间的间隔来弥补行实际长度与段落宽度之间的差距。因此,文本两端对齐时word-spacing
的值可能被覆盖(如果letter-spacing
是一个长度值,这个值则不能被覆盖)。
3.2 基本术语和概念
- 匿名文本:指未包含在行内元素内的文本,包括空格:
<p>Anonymous <em>not anonymous text</em> text <p>
。 - em框:在字体中定义,也称为字符框(character box)。实际字体可能比其em框更高。CSS中,
font-size
的值确定了em框的高度。 - 内容区: 可以是元素中各字符的em框串在一起构成的框,也可以是由元素中字符字形描述的框。本书采用前一种即em框的定义。在替换元素中,内容区就是元素的固有高度再加上说可能有的外边距、边框或内边距。
- 行间距: 行间距(leading)是
font-size
值和line-height
值之差。这个差分为两半,分别应用到内容区的顶部和底部。这两部分称为半间距(half-leading)。行间距只应用于非替换元素。 - 行内框: 这个框通过内容区增加行间距来描述。对于非替换元素,元素行内框的高度刚好等于line-height的值。对于替换元素,元素行内框的高度则恰好等于内容区的高度,因为行间距不应用到替换元素。
- 行框: 包含该行中出现的行内框的最高点和最低点的最小框。行框的上边界要位于最高行内框的上边界,而行框的底边要放在最低行内框的下边界。
根据前面介绍的术语和定义,CSS还提供了一组行为和有用的概念:
- 内容区类似于一个块级元素的内容框;
- 行内元素的背景应用于内容区及所有内边距;
- 行内元素的边框要包围内容区及所有内边距和边框;
- 非替换元素的内边距、边框和外边距对行内元素或其生成的框没有垂直效果,也就是说它们不会影响元素行内框的高度(也不会影响行框的高度);
- 替换元素的外边距和边框确实会影响该元素行内框的高度,相应地,也可能影响包含该元素的行框的高度;
- 行内框在行中根据其
vertical-align
属性值垂直对齐。
来看如何逐步构造一个行框,可以通过这个过程来了解一行的各部分如何共同确定其高度:
-
- 按以下步骤确定行中个元素行内框的高度: a. 得到各行内非替换元素及属于后代行内元素的所有文本的font-size值和line-height值,再将line-height减去font-size,得到框的行间距。这个间距除以2,分别应用于em框的顶部和底部
b. 得到各元素的height、margin-top、margin-bottom、padding-top、padding-bottom、border-top-width和border-bottom-width值,把它们加在一起。
-
- 对于各内容区,确定它在整行基线的上方和下方分别超出多少。此操作需要知道各元素及匿名文本各部分的基线的位置,还要知道该行本身基线的位置,然后把它们对齐。另外,对于替换元素,要将其底边放在整行的基线上。
-
- 对于指定了vertical-align值的元素,确定其垂直偏移量。由此可知该元素的行内框要向上或向下移动多远,并改变元素在基线上方或下方超出的距离。
-
- 知道了所有行内框会放在哪里,再计算最后的行框高度。只需将基线与最高行内框顶端之间的距离加上基线与最低行内框底端之间的距离。
下面详细考虑整个过程,这对于聪明地设置内联内容的样式很关键。
3.3 行内格式化
所有元素都有一个line-height
。这个值会显著地影响行内元素如何显示,所以要特别注意。行的高度(或行框的高度)由其组成元素和其他内容(如文本)的高度确定。line-height实际上只影响行内元素和其他行内内容,而不影响块级元素。也可以为一个块级元素设置line-height值,但是这个值只是应用到块级元素的内联内容。从某种程度上讲,块级元素中包含的各文本行本身都是行内元素,而不论是否真正用行内元素包围起来。
1
2
3
4
5
<p>
<line>This is a paragraph with a number of</line>
<line>lines of text which make up the</line>
<line>contents.</line>
</p>
尽管line标记并不存在,但是段落表现得就像有这些标记一样,每个文本行从段落继承了样式。因此只需为块级元素创建line-height规则,而不必显式地为其所有行内元素(也许只是虚构的行内元素)声明line-height。
3.4 行内非替换元素
在前面的格式化知识基础上,来讨论如果行中只包含非替换元素(或匿名文本)将如何构造。
3.4.1 建立框
对于行内替换元素或匿名文本的一部分,font-size值确定了内容区的高度。如果一个行内元素font-size为15px,则内容区的高度为15像素,因为元素中所有em框的高度都是15像素,如下图示:
下面来考虑line-height的值,以及它与font-size值之差。如果一个行内非替换元素的font-size为15px,line-height为21px。用户代理将这6px一分为二,将其一半分别应用到内容区的顶部和底部,这就得到了行内框。
考虑以下代码:
1
2
3
4
5
<p style="font-size: 12px; line-height: 12px;">
This is text, <em>some of which is emphasized</em>, plus other text <br/>
which is <strong style="font-size: 24px;">strongly emphasized</strong> and which is <br/>
larger than the surrounding text.
</p>
对于font-size和line-height都为12px的各部分文本,行内框高度为12px(高度差为12px-12px=0)。对于strong文本,line-height和font-size相差-12px,分两半-6px分别应用到顶部底部,就得到了行内框。由于这里增加的都是负数,所以最后行内框高度为12px(24px + (-6px) + (-6px) = 12px)。12px高的行内框在元素内容区(24px)中垂直居中,所以行内框实际上小于内容区。如果有上下行,可能会发生重叠。
第二行中的行内框尽管大小相同,但它们排列得并不整齐,因为文本都是按基线对齐的:
由于行内框确定了整个行框的高度,其相互位置很重要。行框定义为行中最高行内框的顶端到最低行内框底端之间的距离,而且各行的顶端挨着上一行行框的底端。根据上图的结果,段落将如下图所示:
图中可以看到,中间一行比另外两行要高,不过还是不够大,不能把所有文本都包含在内。匿名文本的行内框确定了行框的底端,strong元素行内框的顶端则设置了行框的顶端。由于行内框的顶端在元素内容区内部,所以元素的内容落在了行框的外面,实际上与其他行框发生了重叠。其结果是,文本行看上去很不规则。
3.4.2 垂直对齐
如果改变行内框的垂直对齐,会应用同样的高度确定原则。假设为strong元素指定垂直对齐为4px:
1
2
3
4
5
<p style="font-size: 12px; line-height: 12px;">
This is text, <em>some of which is emphasized</em>, plus other text <br/>
that is <strong style="font-size: 24px; vertical-align: 4px;">strongly emphasized</strong> and that is <br/>
larger than the surrounding text.
</p>
这个改动会把元素上升4px,这会同时提升其内容区和行内框。由于strong元素的行内框顶端已经是行中最高点,对垂直对齐的这个修改会把整个行框的顶端也向上移4px,如下图所示:
下面来考虑另一种情况,strong文本所在行上还有一个行内元素,其对齐方式未设置为基线对齐:
1
2
3
4
5
6
<p style="font-size: 12px; line-height: 12px;">
This is text, <em>some of which is emphasized</em>, plus other text <br/>
that is <strong style="font-size: 24px;">strong</strong> and
<span style="vertical-align: top;">tall</span> and that is <br/>
larger than the surrounding text.
</p>
注意tall文本是如何对齐的:
在这种情况下,tall文本行内框的顶端与行框的顶端对齐。由于tall文本的font-size和line-height值相等,所以其内容高度与行内框相同。不过,再考虑以下情况:
1
2
3
4
5
6
<p style="font-size: 12px; line-height: 12px;">
This is text, <em>some of which is emphasized</em>, plus other text <br/>
that is <strong style="font-size: 24px;">strong</strong> and
<span style="vertical-align: top; line-height: 4px">tall</span> and that is <br/>
larger than the surrounding text.
</p>
由于tall文本的line-height值小于其font-size,该元素的行内框比其内容区要小。这会改变文本本身的放置,因为其行内框的顶端必须与该行行框的顶端对齐。所以得到结果如下:
当然也可以为tall文本设置line-height的大于font-size的值如18px,这将得到行内框为18像素。这个行内框的顶端与行框的顶端对齐。类似地,如果vertical-align设置为bottom,则把行内元素行内框的底端与行框的底端对齐。
vertical-align
各个关键字值的效果描述如下:
- top:将元素行内框的顶端与包含该元素的行框的顶端对齐。
- bottom:将元素行内框的底端与包含该元素的行框的底端对齐。
- text-top:将元素行内框的顶端与父元素内容区的顶端对齐。
- text-bottom:将元素行内框的底端与父元素内容区的底端对齐。
- middle:将元素行内框的垂直中点与父元素基线上0.5ex处的一点对齐。(注:一个ex是一个字体的x-height。x-height通常是字体尺寸的一半)
- super:将元素的内容区和行内框上移。上移的距离未指定,可能因用户代理的不同而不同。
- sub:与super相同,只不过元素是往下移。
- <percentage>:将元素上移或下移一定距离,这个距离由相对于元素的line-height值指定一个百分数确定。
这些值在第6章做过详细解释。
3.4.3 管理line-height
前面介绍过,改变一个行内元素的line-height可能导致文本行相互重叠。不过,在所有情况下,这种修改都是针对单个元素的。下面介绍几种以一般的方式影响元素的line-height并避免内容重叠的方法。
一种办法是对font-size有改变的元素结合使用em单位。例如:
1
2
3
4
5
6
7
8
9
10
<style type="text/css">
p {font-size: 14px; line-height: 1em;}
big {font-size: 250%; line-height: 1em;}
</style>
<p>
Not only does this paragraph have "normal" text, but it also
<br/>contains a line in which <big> some big text </big> is found.
<br/>This large text helps illustrate our point.
</p>
通过为big元素设置一个line-height,就提高了行框的总高度,从而提高足够的空间来显示这个big元素,而不会与任何其他文本重叠,也不会改变段落中所有行的line-heihgt。这里使用了值1em,所以big元素的line-heihgt将设置为与big的font-size大小相等。要记住line-height相对于元素本身的font-size设置,而不是相对于父元素设置。结果如下所示:
注意,以下样式也会生成上面的效果:
1
2
p {font-size: 14px; line-height: 1;}
big {font-size: 250%;}
除非line-height值作为缩放因子被继承,否则p和big元素的line-height值都为1。因此,行内的高度与内容区高度一致。
另外一种方法是增加段落的line-height。这将会影响整个元素的每一行。由于各行上下都增加了额外的空间,超链接的边框不会覆盖其他行:
1
2
p {font-size: 14px; line-height: 24px}
a:link {border: 5px solid blue;}
如果行中还有另外一些元素改变了行框的高度,边框情况也可能发生变化。考虑以下规则:
1
2
3
p {font-size: 14px; line-height: 24px;}
a:link {border: 5px solid blue;}
big {font-size: 150%; line-height: 1.5em;}
根据这些规则,段落中big元素行内框的高度将是31.5px(14 x 1.5 x 1.5),这也是行框的高度。为了保证基线间隔一致,必须让p元素的line-height等于或大于32px。
基线与行高:各行框的具体高度取决于其组成元素相互之间如何对齐。这种对齐往往很大程度上依赖于基线落在各元素(或匿名文本各部分)中的哪个位置,因为这个位置确定了其行内框如何摆放。基线在各em框中的位置对于不同的字体是不同的。这个信息内置在字体文件中,除非直接编辑字体文件,否则无法修改。因此,要得到一致的基线间隔,这更像是一门艺术而不只是一门科学。如果使用一种单位(如em)来声明所有字体大小和行高,就很有可能得到一致的基线间隔。不过,如果混合使用了不同的单位,就会困难得多,甚至是不可能。
3.4.4 缩放行高
设置line-height的最好办法是使用一个原始数字值,因为这个数会成为缩放因子,而该因子是一个继承值而非计算值。假设你希望一个文档所有元素的line-height都是其font-size的1.5倍,可以如下声明:
1
body {line-height: 1.5;}
缩放因子1.5在元素间逐层传递,在各层上,这个因子都作为一个乘数与各元素的font-size相乘。
1
2
3
4
5
6
7
8
9
10
11
12
<style type="text/css">
p {font-size: 15px; line-height: 1.5;}
small {font-size: 66%;}
big {font-size: 200%;}
</style>
<p>This paragraph has a line-height of 1.5 times its font-size.
In addition, any elements within it <small>such as this small element</small>
also have line-height 1.5 times their font-size...and that includes
<big>this big element right here</big>.
By using a scalling factor, line-heights scale to match the font-size of any element.
</p>
本例中,small元素的行高为15px,而对于big元素则为45px。当然,如果不希望big文本生成太多的行间距,也可以为big指定一个line-height值,这会覆盖继承的缩放因子:
1
2
3
p {font-size: 15px; line-height: 1.5;}
small {font-size: 66%;}
big {font-size: 200%; line-height: 1em;}
还有一种解决方案(可能是最简单的一种方法),即适当地设置样式,使行高恰好能包含行内容,而没有多余的空间。这里可能需要设置line-height的值1.0。这个值乘以每个font-size,其结果与各元素font-size值完全相等。因此,对于每个元素,行内框与内容区相同,这意味着会使用所需的绝对最小大小来包含各元素的内容区。
注意:大多数字体在字符字形行之间还显示有一点空间,因为字符往往比其em框要小。只有script(“cursive”)字体除外,其字符往往大于其em框。
3.4.5 增加框属性
从前面的讨论可以了解到,内边距、外边距和边框都可以应用于行内非替换元素。行内元素的这些方面根本不会影响行框的高度。如果对一个无内外边距的span元素应用某个边框,可能得到如下所示的结果:
行内元素的边框边界由font-size而不是line-height控制。换句话说,如果一个span元素的font-size为12px,line-height为36px,其内容区就是12px高,边框将包围该内容区。
或者,可以为行内元素指定内边距,这会把边框从文本本身拉开:
1
span {border: 1px solid black; padding: 4px;}
注意,这个内边距并没有改变内容区的具体形状,不过它会影响这个元素行内框的高度。类似地,向一个行内元素增加边框也不会影响行框的生成和布局:
至于外边距,实际上,外边距不会应用到行内非替换元素的顶端和低端,它们不影响行框的高度。不过行内元素的两端则是另一回事。
注意:CSS2.1中明确指定了外边距的放置:它定义了margin-top和margin-bottom可以应用到不是行内非替换元素的所有其他元素,而不是简单地说用户代理应当忽略上、下外边距。
可以把行内元素想成是一个纸片,外围有一些塑料边。在多行上显示行内元素就像是把一个大纸片剪成一些小纸片。不过,每个小纸片上不会增加额外的塑料边。小纸片上的塑料边还是最初那个大纸片上的塑料边,所以看上去只是原来纸片(行内元素)最前和最后两端上出现塑料边。
所以,如果行内元素有一个背景,而且边距足够大以至于行背景重叠,就会发生以下情况:
1
2
p {font-size: 15px; line-height: 1em;}
p span {background: #999; padding-top: 10px; padding-bottom: 10px;}
CSS2.1明确指出按文档的顺序绘制:“这会导致后续行的边框在前面行的边框和文本上绘制。”这个原则同样适用于背景,如上图所示。另一方面,CSS2允许用户代理“‘切掉’边框和内边距区(也就是不显示边框和内边距)”。因此,具体结果可能很大程度上取决于用户代理遵循哪一个规范。
3.5 行内替换元素
一般认为行内替换元素(如图像)有固有高度和宽度;例如,一个图像的高度和宽度可能是某个像素数。因此,有固有高度的替换元素可能导致行框比正常要高。这不会改变行中任何元素的line-height值,包括替换元素本身。相反,只是会让行框的高度恰好能包含替换元素(以及所有框属性)。换句话说,会用替换元素整体(包括内容、外边距、边距和内边距)来定义元素的行内框。
1
2
p {font-size: 15px; line-height: 18px;}
img {height: 30px; margin: 0; padding: 0; border: none;}
然而,行内替换元素还是有一个line-height值。在最常见的情况下,行内替换元素需要这个值,从而在垂直对齐时能正确定位元素。例如,vertical-align
的百分数值要相对于元素line-height来计算。所以:
1
2
p {font-size: 15px; line-height: 18px;}
img {vertical-align: 50%;}
1
<p>The image in this sentence <img src="test.gif" alt="test image"> will be raised 9 pixels.</p>
line-height的继承值使图像上升9px(而不是其他数字)。如果没有line-height值,它就无法完成百分数值指定的垂直对齐。对于垂直对齐来说,图像本身的高度无关紧要;关键是line-height的值。
3.5.1 增加框属性
内边距和边框像平常一样应用到替换元素;内边距在具体内容插入空间,边框围绕着内边距。这个过程的不寻常之处在于,内边距和边框确实会影响行框的高度,因为它们要作为替换元素元素的行内框的一部分(不同于行内非替换元素)。
1
2
3
img {height: 20px; width20px;}
img.one {margin: 0; padding: 0; border: 1px dotted;}
img.two {margin: 5px; padding: 3px; border: 1px solid;}
外边距也包含在行框中,不过外边距有自己的问题。设置正外边距没有什么特殊的地方,只是使替换元素的行内框更高。设置负外边距也有类似的效果:这会减少替换元素行内框的大小。如下图示,负外边距会把图像上面的一行向下拉:
1
img.two {margin-top: -10px;}
3.5.2 替换元素和基线
默认地,行内替换元素位于基线上。如果向替换元素增加下内边距、外边距或边框,内容区会上移。替换元素并没有自己的基线,所以相对来讲最好的办法是将其行内框的底端与基线对齐。因此,实际上是下外边距边界与基线对齐,如下图所示:
这种基线对齐会有一个意想不到的后果:如果一个表单元格中只有一个图像,这个图像要让表单元格足够高,从而能把包含该图像的行框包含在内。即使没有具体的文本,甚至没有空白符,包含图像的表单元格中还是会出现这种大小调整。
1
2
3
4
5
<style type="text/css">
td {font-size: 12px;}
</style>
<td><img src="spacer.gif" height="1" width="10"></td>
在CSS行内格式模型中,表单元格将是12px,图像位于单元格的基线上。所以图像下面可能有3像素的空间,上面有8像素的空间(具体要取决于所用的字体系列及其基线的位置)。这种行为不限于表单元格中的图像;只要一个行内替换元素是块级元素或表单元格元素中的唯一后代,都会有这种行为。例如,div中的一个图像也会放在基线上。对于这种情况,最常用的办法是使间隔图像成为块级元素,这样它们就不会生成行框,例如:
1
2
3
4
5
6
<style type="text/css">
td {font-size: 12px;}
img.block {display: block;}
</style>
<td><img src="spacer.gif" height="1" width="10" class="block"/></td>
另一个可取的修正办法是,将包含图像的表单元的font-size和line-height都设置为1px,这会使行框的高度只能放下1px像素的图像。
行内替换元素位于基线上还有一个有意思的效果:如果应用一个负的下外边距,元素实际上会被向下拉,因为其行内框的底端将比其内容区的底端高。因此,以下规则会得到下面的结果:
1
p img {margin-bottom: -10px;}
4 改变元素显示
可以为属性display
设置一个值来影响用户代理显示的方式。
display
值: none | inline | block | inline-block | list-item | run-in | table |
inline-table | table-row-group | table-header-group | table-footer-group |
table-row | table-column-group | table-column | table-cell | table-caption | inherit
初始值: inline
应用于: 所有元素
继承性: 无
计算值: 对于浮动、定位和根元素,计算值可变;否则,根据指定确定
说明:CSS2中还有值compact和marker,不过由于缺乏广泛的支持,在CSS2.1中已经去掉
我们将忽略与表有关的值,它们将在第11章讨论;在此,还忽略了值list-item
,因为这个值将在第12章详细介绍。下面介绍inline-block
和run-in
元素,不过之前先介绍一下改变元素显示角色将如何改变布局。
4.1 改变角色
假设一个div中有一系列链接,需要把这个div布局为一个垂直边栏:
1
2
3
4
5
6
<div id="navigation">
<a href="index.html">WidgetCo Home</a><a href="products.html">Products</a>
<a href="services.html">Services</a><a href="fun.html">Widgety Fun!</a>
<a href="support.html">Support</a><a href="about.html" id="current">About Us</a>
<a href="contact.html">Contact</a>
</div>
可以把所有的链接都放在表格中,或者每个链接都包含在其自己的div中,或者还可以让它们都是块级元素,如下:
1
div#navigation a {display: block;}
这会让导航栏div中的每一个a元素都是一个块级元素。如果在增加一些样式,可以得到如下结果:
还可以让元素作为行内元素。假设有一个无序的人名列表:
1
2
3
4
5
6
7
8
9
10
<ul id="rollcall">
<li>Bob.C</li>
<li>Marcio G.</li>
<li>Eric M.</li>
<li>Kar M.</li>
<li>Tristan N.</li>
<li>Arun R</li>
<li>Doron R.</li>
<li>Susie W.</li>
</ul>
对于以上标记,假设让这些名字成为一系列行内名,期间间隔用竖线间隔(另外在列表的左右两端也有竖线)。为此,唯一的办法就是修改其显示角色:
1
2
#rollcall li {display: inline; border-right: 1px solid; padding: 0 0.33em;}
#rollcall li:first-child {border-left: 1px solid;}
不过有一点要注意,改变的只是元素的显示角色,而不是其本质。换句话说,让一个段落生成行内框并不会把这个段落真正变成一个行内元素。
行内元素可能是一个块级元素的后代,反过来则不行。因此,尽管链接可以放在一个段落中,但是链接却不能包围段落。不论如何设计元素的样式,都是如此。考虑如下标记:
1
2
3
<a href="http://www.example.com/" style="display: block;"></a>
<p style="display: inline;">this is wrong</p>
</a>
这个标记将是无效的,因为块级元素(p)嵌套在一个行内元素(a)中。改变显示角色也不会使以上标记变的合法。display之所以得名就是因为它影响的是元素如何显示,而不影响它是何种元素。
4.2 行内块元素
值inline-block
是一个混合产物。行内块元素(inline-block element)是块级元素和行内元素的混合。这个display的值是CSS2.1新增的。
行内块元作为一个行内框与其他元素和内容相关。换句话说,它就像图像一个放在一个文本行中,实际上,行内块元素会作为替换元素放在行中。这说明,行内块元素的底端默认地位于文本行的基线上,而且内部没有行分隔符。
在行内块元素内部,会像块级元素一样设置内容的样式。就像所有块级或行内替换元素一样,行内块元素也有属性width和height,如果比周围内容高,这些属性会使行高增加。
来考虑下面一些示例标记:
1
2
3
4
5
6
7
8
9
10
11
<div id="one">
This text is the content of a block-level element. Within this block-level element is another block-level element. <p> Look, it's a block-level paragraph.</p> Here's the rest of the DIV, which is still block-level.
</div>
<div id="two">
This text is the content of a block-level element. Within this block-level element is another inline element. <p> Look, it's an inline paragraph.</p> Here's the rest of the DIV, which is still block-level.
</div>
<div id="three">
This text is the content of a block-level element. Within this block-level element is another inline-block element. <p> Look, it's a inline-block paragraph.</p> Here's the rest of the DIV, which is still block-level.
</div>
1
2
3
4
5
div {margin: 1em 0; border: 1px solid;}
p {border: 1px dotted;}
div#one p {display: block; width: 6em; text-align: center;}
div#two p {display: inline; width: 6em; text-align: center;}
div#three p {display: inline-block; width: 6em; text-align: center;}
结果如下所示:
注意,在第二个div中,行内段落格式化为正常的行内内容,这说明width和text-align被忽略了(它们不能应用与行内元素)。不过,对于第三个div元素,作为行内块元素的段落则有这两个属性,因为它们作为一个块级元素被格式化。这个段落还要求文本行更高一些,因为它会影响行,就好像这是一个替换元素一样。
如果行内块元素的width为定义,或者显式生命为auto,元素框会收缩以适应内容。也就是说,元素框的宽度刚好足够包含该内容,而没有多余的空间。行内框也会这样做,不过行内框可能会跨多个文本行,而行内块元素不能。因此,以下规则应用到前面的示例标记时:
1
div#three p {display: inline-block; height: 2em;}
会创建一个较高的框,它的宽度刚好能包含内容,如下图:
4.3 run-in元素
CSS2引入了一个值run-in
,这也是一个有意思的块/行内元素混合,可以使某些块级元素成为下一个元素的行内部分。这种功能对于某些标题效果很有用,这在打印排版中相当常见,即标题作为文本段落的一部分出现。
在CSS中,只需改变元素的display值,并使下一个元素框作为块级元素框,就可以使元素成为run-in元素。注意,这里所说的是框,而不是元素本身。所以不论元素是块级元素还是行内元素都无关紧要,重要的是元素生成的框。设置为display: block
的strong元素会生成一个块级框,设置为display: inline
的段落则会生成一个行内框。
所以,如果一个元素生成run-in框,而且该框后面是一个块级框,那么该run-in元素将成为块级框开始处的一个行内框。例如:
1
2
3
4
<h3 style="display: run-in; border: 1px dotted; font-size: 125%; font-weight: bold;">Run-ni Element</h3>
<p style="border-top: 1px soid balck; padding-top: 0.5em;">
Another interesting block/inline hybrid is the value <code>run-in</code>, introduced in CSS2, which has the ability to take block-level elements and make them an inline part of a following element. This is useful for certain heading effects that are quite common in print typography, where a heading will appear as part of a paragraph of text.
</p>
由于h3后面的元素生成一个块级框,所以h3元素将变成p元素内容开始处的一个行内元素,如下图所示。
注意两个元素的边框是如何放置的。这种情况下使用run-in的效果与使用以下标记完全相同:
1
2
3
4
<p style="border-top: 1px soid balck; padding-top: 0.5em;">
<span style="border: 1px dotted; font-size: 125%; font-weight: bold;">Run-ni Element</span>
Another interesting block/inline hybrid is the value <code>run-in</code>, introduced in CSS2, which has the ability to take block-level elements and make them an inline part of a following element. This is useful for certain heading effects that are quite common in print typography, where a heading will appear as part of a paragraph of text.
</p>
不过,run-in框与上面的的标记之间还存在一个小小的差别。即使run-in框格式化为另一个元素中的行内框,它们仍从文档中的父元素继承属性,而不是说它们放在哪个元素中就从哪个元素继承属性。
要记住重要的一点,只有当run-in框后面是一个块级框时run-in才起作用。如果不是这样,run-in框本身将成为块级框。因此,给定以下标记,h3仍然是(或者会变成)块级元素,因为table元素的display值是table:
1
2
3
4
5
6
7
<h3 stlyle="display: run-in;">Prices</h3>
<table>
<tr><th>Apples</th><td>$0.59</td></tr>
<tr><th>Peaches</th><td>$0.79</td></tr>
<tr><th>Pumpkin</th><td>$1.29</td></tr>
<tr><th>Pie</th><td>$6.99</td></tr>
</table>
4.4 计算值
如果元素是浮动元素或定位元素,display的计算值可以改变。如果为一个根元素声明display值,计算值也可以改变。实际上,display、position和float值会互相影响。
如果一个元素是绝对定位元素,float的值设置为none。对于浮动元素或绝对定位元素,计算值由声明确定,如下表:
声明值 | 计算值 |
---|---|
inline-table | table |
inline, run-in, table-row-group, table-column, | block |
table-column-group, table-header-group, table-footer-group, | |
table-row, table-cell, table-caption, inline-block | |
所有其他 | 根据指定确定 |
对于根元素,如果声明值为inline-block或table,都会得到计算值table,声明为none时则会得到同样的计算值(none)。所有其他display值都计算为block。
5 小结
尽管CSS格式化模型的某些方面乍看起来有些不太直观,不过等你多熟悉一些就会发现这是有道理的。很多情况下,最初看上去没道理甚至荒谬的规则最后看来确实是合理的,它们会防止一些奇怪的或我们不期望的文档显示结果。在很多方面,块级元素都很容易理解,调整其布局通常是一个简单的任务。另一方面,行内元素则可能很难管理,因为有很多影响因素,其中也包括元素是替换元素还是非替换元素。
【本文内容摘自:《CSS权威指南》(第三版) Eric A. Meyer 著 尹志忠 侯妍 译】