重拾CSS规范之line-height

line-height 是为数不多支持数值可以带单位可以不带单位的属性,但是其效果确实千差万别,今天会用数据来侃一侃其中的差异性。前面讲 IFC的时候说过要好好聊一聊行盒的高度,就在本文中来个大揭秘吧。

前方高能提示,第一大节全部都是原理性的研究,不想看可以略过-_-但你会后悔的,后面是实用性比较强的知识。

定义二三事

行高只能算是 line-height 的中文翻译, CSS 规范 只对 line-height 作出一个稍显模糊的定义。

line-height 值(即行高)就是两行(属性一样的)文本的基线的距离

两个不同字体或者不同大小的文字,它们的基线位置各不相同,基线距离根本就不能得出 line-height 值,这也是为什么我要强调属性一样的文字

基线解释

这里又要懵逼了,什么叫做基线?我怎么知道它在哪里?

我们小学都用过英语四线三格纸,召唤一下它,唤醒我们内涵无限的童年。相信大家一看就知道这四条线分别代表哪个部分了。

四线三格纸

line-height 高度机制

谁 ‘撑开’ 高度

当我们给一个空的 div 添加任意文字时, div 的高度会从 0 变成某一个值,那 div 是由文字 ‘撑开’ 的吗?不,是line-height的作用

看一下我对下面两个 div 设置的 style

1
2
3
<div style="font-size: 40px; line-height: 0; border: 1px solid #ccc;">font-size: 40px; line-height: 0;</div>
<br />
<div style="font-size: 0; line-height: 40px; border: 1px solid #ccc;">font-size: 0; line-height: 40px;</div>


font-size: 40px; line-height: 0;


font-size: 0; line-height: 40px;


第一个 div 虽然字很大,但是由于 line-height0 ,最后落得一个高度为 0 的结果;第二个 div 虽然 font-size0 字儿都显示不出来了,但是愣是让 div 高度变为了 40px

所以, line-height ‘撑开’ 了匿名内联盒的高度,继而 ‘撑开’ 了 div 。如果我们平时开发,遇到想设置文字垂直居中,而设置 line-height = height 时,可以把 height 去掉了,因为 line-height 完全可以替代 height 的作用。

line-height 如何 ‘撑开’

我们说是 line-height ‘撑开’ 了内联盒,其实不够准确。确实与 line-height 有关,但是它更像是一个指挥关,它后面还有一套时刻运转的机制来提供服务。

line-height 高度机制

这里我想强调的是,基线之间的距离只是作为 line-height 的值,不代表 line-height 是这块区域的, line-height 是没有区域之说的,它就是一个值,一个概念。

内联盒真实的区域划分就是如上图所示,内联盒真正的组成就是由内容区域以及上下半行距组成。

顶线与底线之间的区域我们叫做内容区域,内容区域只与字体及字号相关,这个知道就行。上一行文本底线与下一行文本顶线之间的距离叫做行距,这个行距应当分成两半,上一行文本的下半行距,以及下一行文本的上半行距,我在图中在它们中间留了一条缝隙以示区分。

上半行距 + 内容区域 + 下半行距 = line-height

浏览器会根据字形来确定内容区域的高度 h,然后根据 (line-height - h) / 2 来计算出上半行距和下半行距。

如果我们定义了一行文本 line-height30px ,假设内容区域高度为 16px ,那 上半距 = 下半距 = (30px - 16px) / 2 = 7px

其实,到了现在我们可以理解,为什么我们给单行文本设置了 line-height 之后,文本会垂直居中。但是,文本其实没有真正的垂直居中。因为真正垂直居中的是内容区域,而文字在内容区域中并不是垂直居中,通过上面的图就可以发现。但这一点不需要过度计较,因为这个平时压根看不出来。

行盒的高度

我们平时可能听的比较多的版本是,行盒高度是由这一行中最大的 line-height 决定的,这句话其实是错的,因为它完全忽略了 vertical-align 存在的情况。

IFC 一问中已经讲到,行盒由一行内联级盒组成,行盒的高度就是最高的盒(上半边距)顶部到最低的盒(下半边距)底部的距离。

行盒高度计算图示

line-height各属性值

来回顾一下 line-height 可设置的属性值类型:

Value: normal | < number > | < length > | < percentage > | inherit

Tips:不可以对 line-height 设置负值!

  1. normal 没什么太多可讲的,这是 line-height 的默认值。它实际表现等同于 1.1 ~ 1.2
  2. number 指不带单位的数值。它的计算值是 line-height 乘当前元素的 font-size

    假设我现在指定 font-size: 20px; line-height: 1.2;line-height 最后实际值为 20px * 1.2 = 24px

  3. length 就是直接指定带单位的值,如 px , em

  4. percentage 就是指百分比。它的计算值是百分比乘当前元素的 font-size
  5. inherit 因为浏览器实现方式差异较大,故不推荐使用

然后我就在想, line-height: 1.2line-height: 120% 最后计算值都是一样,那为什么要设置两种方式呢?存在必有原因。

最重要的一点:line-height: 1.2 继承给子元素的是 line-height: 1.2 ,而 line-height: 120% 是先将 line-height 计算出来,再将此计算值继承给子元素。

line-height 为 0

line-height 为 0 ,公式 上半行距 + 内容区域 + 下半行距 = line-height 依然成立,设内容区域高度为 h ,此时上下半距都为 -h/2 。但是文本在页面中是不会占据任何空间的。

图片底边距

这里说个题外话。。。 line-heightimg 是不起作用的,但是对同样是可替换元素的 input 却能起到作用,这是为什么呢?后来我想了想,是有原因的,因为 line-height 只能对文本起作用, input 里面是有可以文本的,而 img 中却不可以,同样 object 也是一样。

good luck

瞪大双眼看一看,图片底边与 div 底边并没有完全贴合,还有一个很小的间隙,这个其实是 line-heightvertical-align 共同作用带来的影响。

原因探析

img 默认是 baseline 对齐,看一下图示。

img 与 文字基线对齐

红色的线是基线,绿色的线是底线,底线距离 div 底边还有很小的一点距离,就是下半边距。

我们的文字产生的匿名内联盒自然也是有高度的,基线以下的部分也得显示出来,所以图片下方边距,正是 文本基线到底线的距离 + 下半边距div 的高度就是它所产生的包含块的高度,就是行盒的高度总和(此例中只有一行,所以等于该行盒高),就是 图片高度 + 文本基线到底线的距离 + 下半边距

html5 带来的变化

html4 中,如果没有文字则自然没有上面所说的图片底边距问题,但是情况在 html5 有了变化。

即使没有文本,图片也会当做它身边有文本存在,此时它会带来两种与 html4 不一样的表现。

  • 永远存在图片底边距问题
  • 如果图片高度低于文本高度, div 高度依旧等于文本高度

解决方法

  1. div 设置 line-height: 0 或者 font-size: 0
    文本不占据空间,自然不会凸出来了。但是这个方法显然没有那么美妙,万一我有文字需要添加呢,是吧

  2. img 设置 vertical-align: bottom
    让图片与行盒底部对齐,这样子文字就不会有一部分凸出来了。

  3. img 设置 display: block 或者 float: left
    变成了块盒自然就没有基线对齐这些事了。

热评文章