深入理解 line-height 和 vertical-align

所属分类: 网页制作 / CSS 阅读数: 1696
收藏 0 赞 0 分享

几个概念

  • line box:包裹 inline box 的一个盒子,一个或多个 line box 堆叠撑起一个 HTML 元素。
  • inline(-level) box:可以是一个由行内元素包裹的盒子,也可以是一个纯文字的匿名盒子。
  • content area:对于非替换元素来说,content area 的范围由 font-size 以及字体本身决定;对于替换元素来说,由元素自有宽高决定。
  • baseline:一个元素基线的位置由该元素内字母 x 底部所在的位置决定,当然字体不同基线所在的位置也就不同。

通过一段代码可以理解一下:

div {
  background-color: #ccc;
  font-size: 20px;
  color: #fff;
}
span {
  color: red;
}
<div>文字1<span>文字2</span>文字3</div>

白色的文字就是一个匿名 inline box,红色的文字是一个由 span 包裹的 inline box。这三个 inline box 组成一个 line box,可以理解为灰色的区域,因为在这个例子里就是由一个 line box 撑开了 div。如果有多行的文字,那就有多个 line box。

关于 content area,W3C 有一段这样的解释:

CSS 2.1 does not define what the content area of an inline box is (see 10.6.1 above) and thus different UAs may draw the backgrounds and borders in different places.

这篇文章对非替换元素 content area 的定义就是自有宽高加上 margin,padding 以及 border。我认为应该将 content area 理解为 content box。

line box 高度

浏览器会计算 line box 中每一个 inline box 的高度,对于不同的 inline box 计算方式有所不同:

如果是一个替换元素(比如 imginput),inline-* 元素或者是 flexbox 中的子元素,高度由其 margin box 决定;

inline-block 元素:

div {
  background-color: #ccc;
  color: #fff;
}
span {
  display: inline-block;
  height: 30px;
  margin: 10px;
  background: #fff;
  color: red;
}
<div>xxx<span>xxx</span>xxx</div>

这里 span inline box 的高度就是 height + margin 2。如果 height 的值是 auto,高度就是等于 line-height + margin 2。

如果是一个非替换元素,高度由它的 line-height 决定,而不是 content area,虽然有时候看起来像 content area 撑开了 line box 的高度。

div {
  background-color: #ccc;
  font-size: 20px;
  color: #fff;
  font-family: Sana;
}
span {
  background: #fff;
  color: red;
}
<div>xxx<span>xxx</span>xxx</div>

这张图片可以明显地看出撑开 line box 的是 line-height,而不是 content area。

这篇文章用了 virtual-area height 来表示 line-height 撑开的高度,而我的理解其实就是 inline box 的高度。

line box 中所有 inline box 的最高点以及最低点决定了它的高度(该计算包括了 strut 的高度,后文会提到 strut)。

非替换元素的的 margin,padding 以及 border 并不会影响 line box 高度的计算。当一个 inline-level box 的 line-height 小于 content area 的时候,line box 的高度就会小于 content area,此时元素的 background 以及 padding 等就会溢出到 line box 之外。

以下代码可以说明这个问题:

div {
    background: #eee;
    border: 1px solid #000;
    box-sizing: border-box;
    font-size: 50px;
    line-height: 10px;
}
span {
    background: red;
    margin: 10px;
    padding: 10px;
}
<div><span>xxx</span></div>

leading:

content area 的高度与 inline box 的高度差就是 leading,这个 leading 会等分被添加到 content area 的顶部与底部,所以说 content area 永远位于 inline box 的中间(垂直居中)。

strut:

浏览器认为每一个 line box 的起始位置都存在一个宽度为 0,没有任何字符的 匿名 inline box,称为 strut,这个 strut 是会从父元素继承 line-height 的,因此它的高度会影响整个 line box 高度的计算。

一个例子

div { background: #eee; border: 1px solid #000; box-sizing: border-box; }
<div><img src="./image.png" alt=""></div>

在图片中可以看到 img 与外层的 div 存在一个间隙,这就是上文提到的 strut 造成的。

在这个例子中,默认情况下 img 的底边与父元素的基线对齐(img { vertical-align: baseline }),而这个基线实际上就是 strut 基线所在的位置。如下图所示:

strut 其实就相当于一个不可见的字母 x,上文已经提到 strut 本身是具有 line-height 的,所以就导致图片底部多了一段间隙。

总结一下存在间隙原因:

  • strut 存在 line-height
  • vertical-align 默认值为 baseline

对应的解决方案:

  • 修改 strut 的 line-height,因为 strut 的 line-height 不是能够直接设置的,所以需要设置父元素的 line-height,然后让 strut 继承,或者修改 font-size
  • 将 vertical-align 设置为其他值line-height

W3C 中对于 line-height 的解释是这样的:

On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut."

我的简单理解是,对于由行内元素组成的块级元素而言,line-height 决定了 line box 的最小高度,浏览器会假定每一个 line box 以一个宽度为 0 的 inline box (strut)开始,而这个 strut 从父元素继承到 font 以及 line-height。

  • normal 是 line-height 的默认值,W3C 对它并没有一个明确的定义。normal 会将 content area 作为一个计算因素。
  • line-height 并不是两条 baseline 之间的距离。
  • line-height 的值推荐使用数值,而不是使用 em 单位,因为 em 单位会根据从父元素继承到的 font-size 来计算行高。

vertical-align

W3C 对 baseline 以及 middle 的定义如下:

baseline: Align the baseline of the box with the baseline of the parent box. If the box does not have a baseline, align the bottom margin edge with the parent's baseline.

元素基线与父元素基线对齐,如果元素没有基线,比如 img,则使用 margin 底边与父元素基线对齐。

middle: Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.

元素的垂直中点位置与父元素的基线加上一半 x-height 的位置对齐。

参考

Deep dive CSS: font metrics, line-height and vertical-align
https://meyerweb.com/eric/css/inline-format.html
https://www.zhangxinxu.com/wordpress/2015/08/css-deep-understand-vertical-align-and-line-height/
https://www.w3.org/TR/CSS2/visudet.html#inline-box-height

更多精彩内容其他人还在看

js 解决隐藏与显示div的相关问题

我的导航中就有一栏产品中心下面用隐藏个div然后鼠标放上去就显示出来,但是导航那一块div一直出不来,什么都没有,很是郁闷不知道是什么原因
收藏 0 赞 0 分享

关于clearBoth在GOOGLE Chrome中的问题解决方法

下面这段CSS在IE中好好的,但在GOOGLE Chrome中总是不行,我调测了无数次。问题就出在 clearBoth 这个样式上,此问题如何解决,写下来详细介绍
收藏 0 赞 0 分享

用css margin去掉横排图片之间的间距

HTM,CSS,怎样去掉横排图片之间的间距,是我们的一大头疼问题,于是本人搜集整理一下,晒出来和大家分享,希望可以帮助你们
收藏 0 赞 0 分享

关于li:hover的怎么清除浮动问题实现代码

当鼠标移动上去时,周围显示一个方框,但是后面的会向后移动,如何才能使得当鼠标移上去时后面的li不浮动
收藏 0 赞 0 分享

ie6不支持两个连续并列class类名怎么解决

在网页布局中会使用到两个连续的class,但唯独ie6不支持,很郁闷,于是搜索整理下,晒出来和大家分享
收藏 0 赞 0 分享

如何在class内写xsl标记注意事项

想要在class中写一个xsl标记,想要知道应该如何来写出正确的代码语句,请详看本文
收藏 0 赞 0 分享

ie10 css hack 条件注释等兼容方式整理

ie10已经上线一段时间了,相信已经有一部分前端潮人体验过了,截至到现在,在ie6到ie9的浏览器各种各样的古怪行为,开发人员不得不使用条件注释,有条件的类,和其他特定于IE的css hack来解决
收藏 0 赞 0 分享

IE6双倍边距 IE6浏览器会出现双倍边距解决方法

所谓的IE6双倍边距就是指当元素有float属性,又有margin属性时,在IE6下面显示的margin的值是设置值的两倍,这个问题从有css技术时就已经诞生,本文将介绍详细解决方法,需要的朋友可以参考下
收藏 0 赞 0 分享

css 网页背景图片 怎样用CSS实现大背景网页效果

在网页设计制作中经常会遇到这样的问题:用图片做背景时,由于显示器分辨率太大或者图片尺寸太小,在页面的两边或者下部了没有背景图片,使页面变的很僵硬,于是搜集整理一些,晒出来和大家分享
收藏 0 赞 0 分享

CSS字体中英文名称对照表 CSS常用中文字体英文名称对照表

在CSS文件中,我们常看到有些字体名称变成了乱码,这是由于编写者将中文字体的名字直接写成了中文,为了避免这种状况出现,在CSS文件中使用中文字体时,最好使用中文字体的英文名称,需要的朋友可以注意下
收藏 0 赞 0 分享
查看更多