CSS 当中的 line-height

CSS 当中的 line-height

我们在之前的章节当中介绍过了 CSS 当中的 floatdisplayposition 属性,今天我们接着上回继续来看 CSS 当中另一个使用比较多的的属性,那就是 line-height,关于它的具体内容可以参考 CSS 2.1 规范 9.4.2 inline-formatting,可以参考下面这个例子来看看 line-height 的基本用法(转自 MDN

行高是指行间的距离,而从规范上来说则是两行文字基线之间的距离,行高是作用在每一个行框盒子(line-box)上的,而行框盒子则是由内联盒子组成,因此行高与内联元素可以说是非常紧密,行高直接决定了内联元素的高度(注意,这里的内联元素不包括替换元素),对于块级元素和替换元素,行高是无法决定最终高度的,只能决定行框盒子的最小高度,那么这里就会存在几个问题了

  • 基线是什么?
  • 只有两行文字才会存在两个基线,那么为什么单行文字还具有行高?
  • 行框盒子与内联盒子又是什么?
  • 什么是替换元素?
  • 行高的取值是如何决定的?

不要急,让我们带着这些疑问慢慢往下看

x、x-height 以及 ex

字母 xCSS 里面扮演着一个很重要的角色,因为字母 x 的下边缘就是基线所在的位置,而 x-height 指的就是字母 x 的高度,ex 是一个尺寸单位,其大小是相对字母 x 的来计算的,即 1ex 就表示 1 个字母 x 的高度,如下图所示

我们在平时的开发中很少用到 ex,因为 ex 是个相对单位,对于相对的东西,我们总是感觉很难控制,但这并不表明 ex 就一点用处都没有,我们可以利用 ex 就是一个 x-height 的特性来实现图标与文字的垂直居中,这样如论字体大小如何变化,都不会影响垂直居中的效果,代码如下

1
2
3
4
5
6
7
.icon-arrow {
display: inline-block;
width: 20px;
height: 1ex;
background: url(down.png) no-repeat center;
background-size: contain;
}
1
2
3
4
<div>
<span>我是一段文本</span>
<i class="icon-arrow"></i>
</div>

行内框盒子模型

在深入展开之前,我们先来了解一下行内框盒子模型的概念,先从一个简单的示例开始,如下

1
<p>这是一个单行文字,这里有一个<span>内容区</span>标签。</p>

运行起来以后在页面上的显示是下面这样的

在这里,我们主要了解四个概念,分别是内容区域,内联盒子,行框盒子以及包含盒子,它们的区别如下

  • 内容区域(content area),是围绕文字的盒子,也就是底线和顶线包裹的区域,我们可以看做是图中鼠标选中文字区域的大小,实际中不一定看得到,但确实存在,内容区的大小依据 font-size 的值和字数进行变化(行内元素 display:inline 可以通过 background-color 属性显示出来)
  • 内联盒子(inline boxs),不会让文字成块显示,而是排列成一行,由内联元素包裹的文字如 span 标签包裹的内容区,可以称之为内联盒子,而没有内联元素包裹的部分,我们可以看做是匿名内联盒子,内联盒子可以看是上图当中 span 标签内的内容区区域,而匿名内联盒子可以看做红色虚线包裹的内容
  • 行框盒子(line boxs),每一行都是一个行框盒子,行框盒子由一个个匿名和非匿名内联盒子组成,可以看做上图当中最外面红色实线包裹的区域
  • 包含盒子(containing box),此盒子由一个个行框盒子组成,<p> 标签就代表了一个包含盒子

行高、行距

在了解完行内框盒子模型的概念以后,我们接着来看我们在开头部分提出的疑问,我们在上面曾提到,行高是指行间的距离,而从规范上来说则是两行文字基线之间的距离,而基线(baseline)指的是一行字横排时下沿的基础线,也就是下图当中标注红线的部分,由此我们根据下图推断出

单行文字行高为

1
line-height = 内容区域高度 + 行间距高度

同样的,我们可以得出

1
行间距 = line-height - 内容区域高度

由于行间距是上下均分的,所以从上图当中我们也能看出,半行间距也就是文字上部与行框盒子的顶部之间的距离,但是这里肯定会问了,line-height 明明是两基线之间的距离,单行文字哪来行高,还控制了高度?

这里需要我们慢慢来进行梳理,行高由于其继承性,影响无处不在,即使单行文本也不例外,并且高度的表现本质上也不是行高,而是内容区域和行间距决定的,内容区域高度只与字号以及字体有关,与 line-height 没有任何关系(比如在 SimSun 字体下,内容区域高度等于 SimSun),换句话说就是在 SimSun 字体下的表现如下

1
font-size + 行间距 = line-height

说完了行高,我们再来看看行距,我们还是以上图为例,在图中我们可以发现,上一行的底线和下一行的顶线之间的距离就是行距,那么它的作用到底是什么呢?我们可以想象一下在文字排版的时候,如果行与行之间的间距为 0,则文字是会紧紧贴在一起的,因此行距就是用来协助排版的,也正是因为行距的存在,所以我们给元素设置 margin 值时,要减去相应的半行距值,这样才能比较精确地还原设计图

替换元素

CSS 中,可替换元素(replaced element)的展现效果不是由 CSS 来控制的,这些元素是一种外部对象,它们外观的渲染,是独立于 CSS 的,简单来说,它们的内容不受当前文档的样式的影响,CSS 可以影响可替换元素的位置,但不会影响到可替换元素自身的内容,某些可替换元素,例如 <iframe> 元素,可能具有自己的样式表,但它们不会继承父文档的样式,CSS 能对可替换元素产生的唯一影响在于,部分属性支持控制元素内容在其框中的位置或定位方式

CSS 在某些情况下会对可替换元素做一些特殊处理,比如计算外边距(margin)和一些 auto 的具体值,不过需要注意的是,一部分(并非全部)可替换元素,其本身具有的尺寸和基线(baseline)会被一些 CSS 属性用到,加入计算之中,例如 vertical-align,只有可替换元素才能具有这种自带值

行内替换元素需要使用 line-height 值,从而在垂直对齐时能正确地定位元素,因为 vertical-align 的百分数值是相对于元素的 line-height 来计算的,对于垂直对齐来说,图像本身的高度无关紧要,关键是 line-height 的值,默认地,行内替换元素位于基线上,如果向替换元素增加下内边距、外边距或边框,内容区会上移

替换元素的基线是正常流中最后一个行框的基线,除非该替换元素内容为空或者本身的 overflow 属性值不是 visible,这种情况下基线是 margin 底边缘

某些 CSS 属性可用于指定可替换元素中包含的内容对象 在该元素的盒区域内的位置或定位方式,这些属性的具体定义可以在 CSS Images Module Level 3CSS Images Module Level 4 规范中找到

取值

line-height 的取值有以下这些

取值 描述
normal normalline-height 的默认值,但并不是一个固定的值,而是会受 font-family 的影响,对于微软雅黑,其值为 1.32,而对于宋体,其值为 1.141,由于不同操作系统,不同浏览器所使用的字体不一样,所以最终 line-height 的具体值会不一样,因此这个属性作用不大
<number> 使用数值作为行高,根据当前元素的 font-size 大小计算,大多数情况下,这是设置 line-height 的推荐方法,不会在继承时产生不确定的结果,比如 line-height: 1.5
<length> 长度用的最多的就是 pxemem 跟数字一样,都是相对于 font-size 来计算的,以 em 为单位的值可能会产生不确定的结果,比如 line-height: 1.5emline-height:1.5remline-height: 20px
<%> 使用百分比作为行高值,相对于设置了该 line-height 属性的元素的 font-size 大小计算,与元素自身的字体大小有关,计算值是给定的百分比值乘以元素计算出的字体大小,百分比值可能会带来不确定的结果,比如 line-height: 150%

这里还有一个比较特殊的 inherit 属性,即行高继承,那么这里就存在一个问题,我们在之前提到过,行高默认具有继承性的,但是这里为什么还要多提供一个属性来给我们选择呢?

这是因为控件元素的默认行高是 normal,而不是继承父级元素的行高,比如我们来看下面这个示例,即 line-height: 1.5、line-height: 1.5em;、line-height: 150% 之间的区别,在计算结果上是相同的,但是所影响的元素有区别

  • line-height: 1.5,所有可继承元素会根据 font-size 重新计算行高(也就是说其子元素都会根据自身的 font-size * 1.5 计算行高,每个子元素都要进行一次计算)
  • line-height: 150%/1.5em,当前元素会根据 font-size 先计算行高,然后再继承给下面的元素(当前元素根据 font-size 计算行高,然后将所计算的出来的值继承给后代,也就是说只需要当前元素进行计算,而子元素不需要重新计算,并且此时子元素设置 font-size 对其 line-height 是无影响的)

这里提一点针对于全局数值使用的经验,其实简单来说,比较推荐使用数字来设置 line-height,也就是无单位的使用方式,如果是 blog 以阅读为主的网页,line-height: 1.5/1.6 较为适宜,如果是面向用户,并不是阅读为主的网页,则推荐使用匹配 20 像素的使用经验,如下

1
body { font-size: 14px; line-height:1.4286 }

或者合并形式

1
body { font: 14px / 1.4286 'microsoft yahei' }

line-height 与图片的表现

有一个比较常见的问题,也就是下面这种情况,我们在 div 标签当中嵌入一张图片,没有给 div 元素设置任何属性,仅仅只设置了 background: palegreen,代码如下

1
2
3
div {
background: palegreen;
}

但是在页面展示的效果却是下面这样的(注意观察图片底部绿色部分)

这是因为内联元素默认基线对齐,空白标签内含有空白节点,相当于图片和一个文字对齐,根据 vertical-align: baseline,所以图片底部存在间距,这里的空白节点我们可以理解成一个字母 A,因为是基线对齐,且父元素没有设置固定高度,所以父元素高度由内容填充,由于 A 要与图片基线对齐,所以就会在下边缘,也就是下图这样的情况

这样一来是不是就可以一眼看出问题所在了,针对于这种情况,解决的办法主要有以下三种

  • 将图片块状化,这时就无基线对齐,因为 vertical-align 属性只对内联,内联块状元素有效
  • 使用 vertical-align:bottom 来将图片的底线对齐,或者减小行高(这时基线的位置会上移)
  • 对于父元素使用 line-height: 0 使得行高足够小,使基线上移

另外一种比较常见的用法就是图片水平垂直居中,代码如下

1
2
3
<div>
<img src="https://raw.githubusercontent.com/heptaluan/blog-backups/master/cdn/cover/15.webp">
</div>
1
2
3
4
5
6
7
8
div {
line-height: 300px;
text-align: center;
}

img {
vertical-align: middle;
}

显示效果如下

原理是当设置 text-align 的时候,内联元素文字和图片会居中显示,我们让空白节点的行高与 div 高度一致,这样就可以实现垂直居中,图片和空白节点默认基线对齐,另外我们也可以将其中的图片替换为多行文本从而实现多行文本垂直居中,代码如下

1
2
3
<div>
<span>这里是多行文本 ...</span>
</div>
1
2
3
4
5
6
7
8
9
10
div {
line-height: 300px;
text-align: center;
}

span {
display: inline-block;
line-height: normal;
vertical-align: middle;
}

显示效果如下

多行文本垂直居中的原理和上面图片是一样的,我们可以把 span 看做是图片,这样就需要将 span 的元素 display 设置成 inline-block,并且重置 line-height 既可

参考

# CSS

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×