阿瑞斯的BLOG

行内元素的视觉化表现

关键词
盒子模型(box model) 行盒(line box) 行内盒(inline boxes) 行高(line-height)
行内元素的垂直对齐(vertical-align) font-size font-family 字体度量

前言:

关于行内元素的视觉表现涉及到了很多 CSS 方面的术语,比如:行框,行盒,在不同的书或博客中有使用行框的,有使用行盒的,那么对于刚接触这些概念的我们,往往会有疑问,它们有什么区别?其实是没有区别的,这是由于译者不同的用词习惯导致的,所以今后学习这些新知识有能力最好还是阅读英文文档

译者注:由于不同志愿者的用词习惯,box有时候被翻译成“盒子”,有时候被翻译成“框”。又由于一篇文章有时候由好几位志愿者共同翻译,因此常常在一篇文章中既出现了“盒子”,又出现了“框”,这给读者造成了很大的困扰。在之前几个模块的本地化工作时,我个人认为“框”能更准确表达出box的意思。在W3C上,那里的译者也将box翻译成“框”。但是显然的,“盒子”比“框”更容易区分——“框”容易和“边框(border)”混淆。我在这里特地增加了一个译者注,是希望读者在之后的阅读中能够明白“框”=“盒子”=box,边框=“border”。诸如此类的,padding大部分人翻译成“内边距”,但是padding也有人翻译为“填充”。

来源于 MDN

行盒内元素的排版

IFC布局规律

  • 在一个行内格式化上下文中,盒是一个接一个水平放置的,从包含块的顶部开始
  • 这些盒之间的水平marginborderpadding都有效
  • 盒可能以不同的方式竖直对齐:以它们的底部或者顶部对齐,或者以它们里面的文本的基线对齐

什么是行盒(line box) 以及行盒的形成

包含来自同一行的盒的矩形区域叫做行盒(line box)也叫行框.

line box的高度总是足以容纳它所包含的所有行内框(也叫内联盒子或叫行内盒), 即该行中出现的内联盒子的最高点和最低点的最小框.

换句话说, 行盒的上边界要位于最高内联盒子的上边界, 而行盒的底边要放在最低内联盒子的下边界
行盒高度的定义

line box的宽度由包含块float情况决定,一般来说,line box的宽度等于包含块两边之间的宽度,然而float可以插入到包含块和行盒边之间,如果有float,那么line box的宽度会比没有float时小

特点:

  • 每一行都是一个行盒, 同一段文本从一行变成多行, 行盒的个数也会发生变化.

  • 行盒模型(line box)和行内元素的盒模型(inline box)不是一个东西, 行盒模型这个盒子实际是看不到的, 他仅仅是由一行行内元素所确定的这么一个东西.

  • line box的高度由line-height决定,但不总是等于某个最高的行内元素的 line-height, 因为行内元素的 vertical-align 会影响行内元素的对齐方式而导致最高的行内盒子的底部不一定是 line box 的底部,如上图.

1
2
3
4
5
这里也解决了一个困扰自己的问题:
行盒的定义:<code>line box</code>的高度总是足以容纳它所包含的所有行内框
行盒的高度: 由 line-height 决定(即以为行盒的高度 = 一行内最大的 line-height)
但是现在有一张图片(行内替换元素)和文字(行内非替换元素),line-height 为50px, 那么行盒的高度是不是和行盒的定义冲突了??? 50px < 100px, 行盒怎么能包含图片呢?
其实对于行盒的定义没错,只是行盒的高度并不由line-height决定!!!
  • line box之间的高度各不相同(比如只含文本的line box高度与包含图片的line box高度之间)

  • 行盒的高度是根据子元素的高度来计算,注意这里子元素的高度并不是子元素内容区域(content-area)的高度,这是有很大区别的.

    尽管这听起来可能有些奇怪,行内元素有两个不同高度:content-area(内容区域)高度和virtual-area(虚拟区域)高度(这是我发明的术语virtual-area高度,你在规范中是找不到任何相关的内容).

    • content-area高度是由font-family,font-size来决定的.
      • virtual-area高度是line-height,它的高度用于计算line-box的高度.只是计算,并不等于
        行内元素有两个不同的高度
  • 当一行的行内级盒的总宽度小于它们所在的line box的宽度时,它们在行盒里的水平分布由text-align属性决定。如果该属性值为justify,用户代理可能会拉伸行内盒(不包括inline-tableinline-block盒)里的空白和字(间距)

行内盒(inline box)

  • 一个inline box是一个(特殊的)行内级盒,其内容参与了它的包含行内格式化上下文

  • 当一个inline box超出一个line box的宽度时,它会被分成几个盒,并且这些盒会跨多line box分布。如果一个inline-block无法分割(例如,如果该inline box含有一个单个字符,或者特定语言的单词分隔规则不允许在该inline box里分隔,或如果该inline box受到了一个值为nowrap或者prewhite-space的影响),那么该inline box会从line box溢出

  • 当一个inline box被分割后,marginborderpadding在发生分割的地方(或者在任何分割处,如果有多处的话)不会有可视化效果

  • 同一个line box里的inline box也可能因为双向(bidirectional)文本处理而被分割成几个盒

1.盒子的生成

inline 元素(span, 图片, 文字…)生成各种 inline boxes, 其内容参与了它的包含行内格式化上下文

2.盒子的高度计算(line-height登场)

包括行内非替换元素和行内替换元素的高度计算

3.盒子的摆放(vertical-align登场)

3.1 当设置 vertical-align 属性的时候,谁和谁对齐?对于行盒的 baseline 如何确认?

该属性会影响由一个行内级元素生成的盒的行盒内部的竖直定位

默认情况下,inline,inline-block以及table-cell可以应用vertical-align
vertical-align的属性值

3.1.1 这里反复提到父级盒的基线,那么这个父级盒是指 包含块元素 还是 行盒?

3.1.1.1 如果父级盒是指块级盒

对于包含块,即使没有显示地设置字体属性,它也是有自己的字体的(默认是serif),字体有了那么它的基线位置就确认了,line-height 有了,那么这个字体的内容形成的一个匿名inline box的高度也就有了,那么line box内所有inline box的参照物也就有了


3.1.1.2 如果父级盒是指行盒

(《CSS 权威指南》195页中父级盒就是指行盒),这里父级盒如果是指line box,困扰自己最大的问题是:一个行框内有多个行内框的时, 行框的基线如何确定?

注意

vertical-align:middle属性的表现与否,仅仅与其父标签有关,至于我们通常看到的与后面的文字垂直居中显示那都是假象!

如何理解:因为改变图片的vertical-align属性以后,图片位置会上移或下移,而文字在那里却纹丝不动!,这说明什么?说明vertical-align: middle 带来的图文对齐效果是顺带的,与后面的行内水平的元素不存在直接关系,具体地分析见张鑫旭老师的 vertical-align 属性是如何起作用的

3.2 没有基线的inline box 如何对齐?

比如对于行内替换元素img

  • 对于 行内替换元素(比如img)的基线就是其margin底边缘

  • 对于 inline- block 元素比较复杂:一个inline- block元素,如果里面没有inline内联元素,或者overflow不是visible则该元素的基线就是其margin底边缘,否则,其基线就是元素里面最后一行内联元素的基线,具体地看张鑫旭老师的 inline-block和baseline

4.行盒的确立(定行高)

可以通过这个过程来了解一行的各部分如何共同确定其高度

  1. 确定行中各inline box的高度(注意: inline box的高度 ≠ 内容区域的高度)

    • 对于可替换元素(比如img,svg,input),inline-block元素和inline-table元素占据的高度(即inline box 高度) ,这个值就是其margin box的高度,如果hegiht的值是auto,然后使用line-heightcontent-area严格上等于line-height.

    • 对于非替换元素 inline box 占据的高度不由行高决定,而是内容区域行间距,只不过,正好 content area + vertical spacing = line-height,所以我们就可以认为行高就是非替换元素形成的 inline box 的高度,就像欧姆定律可以计算出电阻,但不能说电压和电流决定了电阻.

    • 内容区域(content area)是一种围绕文字看不见的盒子.

    • content-areafont-size 以及 font-family 相关, 和 line-height 没有任何关系

    • 关于em盒子也叫 em 框 模型,其实就是字体的容器,这个属于 content-area ,这也是为什么算 content-area 的时候即使不算vertical spacing(行间距),文字仍然有上下左右留白的原因.

如果 line-height 没有指定值,如何计算?

没有指定值的 line-height 是有默认值的,默认值为 normal ,而 normal 在不同的浏览器,不同字体下的值是不同的.为了计算出 line-height 的具体值, 我们需要知道 normal 对应的值,然后乘以 font-size 即可求出行内元素的行高.

normal 的计算:
  1. 设置字体的 font-size 为100px, 方便计算
  2. 通过 Chrome 的检查工具获取行内元素的高度,此时因为行内没有别的 font-family 的元素,所以vertical spacing 为0,即 行内元素的高度表现 = content-area = line-height
  3. 高度 / font-size = 该字体normal 值对应的数值
补充

vertical spacing的值可大可小,还可以为负,它的作用只是保证行内盒子的高度正好等同于行高

2.确定替换元素的高度

  • 得到各替换元素的 height margin-top margin-bottom padding-top padding-bottom border-top-width border-bottom-width的值,把它们加起来

3.取该行内最高盒子的ine-height形成高度

3.line box的高度等于最高inline box的上边界加上最低inline box的下边界,或者说是 父级盒基线的位置到最高inline box的上边界的距离加上父级盒基线的位置到最低inline box的下边界的距离.

3.通过行高定基线,基线确定别的线

对于各内容区,确定它在整行基线的上方和下方分别超出多少.这个任务并不容易:你必须知道各元素及匿名文本各部分的基线的位置,然后让它对齐该行基线.另外,对于替换元素,要将其底边放在整行的基线上

《CSS 权威指南》

注意:这段是根据父级盒指行盒产生的理解.即先定行盒的基线再摆放元素,但是这有一个最大的问题,盒子未摆放之前,行盒根本无法确立,所以删掉了这段

4.vertical-align 修饰细节

注意:这段和上一段是一起的,即确立行盒=>确立行盒的基线=>盒子根据默认的对齐方式对齐=>vertical-align进行细节修饰,但是这也存在上面一样的问题:盒子未摆放之前,行盒根本无法确立.并且修改vertical-align会影响行盒的高度,行盒被修改了,那么行盒的基线肯定发生变化造成循环影响,所以删除掉了这段

对于指定了 vertical-align值的元素,确定其垂直偏移量,由此可知该元素的内联盒子要向上或向下移多远,并改变元素在基线上方或下方超出的距离

5.计算多行文本的高度

所有line boxes 的行高加起来,即为行内元素的高度表现.

5.1 结论

所以一个没有设置height属性的包含块高度就是由一个一个line boxes的高度堆积而成的。

6.影响 line box 高度的因素

  • line-height通过影响inline box的高度影响line box的高度

  • 不同的font-family的文字通过baseline位置的不同,影响盒子的对齐而会影响line box的高度

  • font-size通过影响数值单位line-height影响line box的高度

  • vertical-align影响inline box的对齐方式,影响line box的高度

  • 特别注意的是, 设置inline元素的垂直距离无法改变行盒的高度, 视觉表现为文字还待在该待的地方,即垂直方向上的margin、padding、border是不占据空间的,但是唯一和块元素表现相同的是margin还是透明色,padding、border还是能够显示背景色的。同时由于对html文档的渲染是从上到下的,所以可能存在后面的行内元素的垂直方向的背景色覆盖前面的元素,对后面的元素中文字部分不会覆盖。

6.1 line-box计算的小细节:

  • 对于行内非替换元素,padding和border增加了其background区域,但不会增加内容区域高度(甚至是line-box高度)。因此,你在屏幕上看到的不一定就是内容区域。margin-top 和 margin-bottom 对内联元素不生效。

  • 对于行内替换元素,inline-blockblocksified行内元素,paddingmarginborder都会增加高度,所以内容区域和line-box的高度也会增加

7.补充

由于以上例子中使用到的字体可能在用户的系统里并不存在,然后发现设置不同的font-family失败,可以参考:

浏览器默认支持哪些字体(1)

Mac如何给自己下载字体(2)

更多关于CSS来控制字度的度量指标的问题,请参考大漠老师翻译的深入了解CSS字体度量,行高和vertical-align

下面来看几个实际开发中遇到的问题

图文混排

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
div{border:1px solid #ccc;}
a {border: 1px solid;text-decoration: none}
a:hover {color: yellow}
span {padding: 30px;background-color:red;margin: 20px;border: 1px solid blue;}
img {width: 50px;height: 50px;margin: 10px;}
.a {font-size: 30px;line-height: 100px;}
</style>
<div>
<a href="#">
<img src="http://www.baidu.com/img/bd_logo1.png" alt="">
<span>x</span>
<span class="a">x</span>
</a>
</div>

信息:

  1. 包含块不设置高度
  2. inline 元素嵌套并不会让 inline 盒子嵌套
  3. line box内包含 替换元素(img),不同 font-sizeline-heightinline box
  4. 默认的vertical-align
  5. 对于不可替换的行内元素,width和height这两个属性是不能描述一个不可替代的行内元素的,所以说一直都是默认值auto.

图文混排的高度表现

图文混排的包含块高度

行内元素在盒模型中的表现:

按照盒子的生成=>盒子的高度计算=>盒子的摆放=>行盒的确立

  1. 包含块高度112px,行高100px, 说明line box的高度不等于line box内最高 inline box的高度,并且line box的高度并不由line-height决定

  2. 112的由来:包含块2px 的 border,以及line box的高度,其中上边界是 img元素的 margin-top,下边界是.a的inline box的 bottom

2. 不同 font-family 的 inline box 高度表现

1
2
3
4
5
6
7
8
9
<p>
<span class="a">Ba</span>
<span class="b">Ba</span>
<span class="c">Ba</span>
</p>
p { font-size: 100px; }
.a { font-family: Helvetica; }
.b { font-family: Gruppo; }
.c { font-family: Catamaran; }

信息:

  1. 包含块盒子不设置高度
  2. 默认的 line-height
  3. 相同的 font-size
  4. 默认的 vertical-align

可以发现包含块的高度为164px,刚好是font-family: Catamaraninline boxcontent-area,(在不指定行高的情况下,content-area 等于 line-height 的值,而在不指定竖直方向的padding,border的情况下,通过 Chrome 面板获取的元素占据尺寸就是 content-area的值,如图)


不设置高度时包含块的高度

包含块不设置高度时的高度表现


文字尺寸

不设置竖直方向的 padding,border 实元素的占据尺寸


元素的占据尺寸带 padding 和 border

带 padding 和 border的元素的占据尺寸

参考

  1. 《CSS 权威指南》

  2. css行高line-height的一些深入理解及应用 by 张鑫旭

  3. 深入了解CSS字体度量,行高和vertical-align by 大漠

  4. 你真的了解行盒模型吗

  5. 行内格式化上下文(Inline formatting contexts)