转载

CSS深入理解vertical-align和line-height的基友关系

一、想死你们了

几个星期没有写文章了,好忙好痒;个把月没有写长篇了,好忙好想;半个季度没在文章中唠嗑了,好痒好想。

CSS深入理解vertical-align和line-height的基友关系

后面一栋楼有对夫妻在吵架,声音雄浑有力,交锋酣畅淋漓,还以为只有小乡镇才有这架势,哦,突然想起来,我就是住在上海郊外的小乡镇上。

刚刚买了几十股京东的股票,第一次玩这个,看好京东的发展。其实股价21的时候就打算入手了,但是,转外汇的时候,提示,要工作时间。然后一忙二忘,等现在入的时候,已经涨了20%多了,科科,肥皂弄人啊!写到这里的时候,忍不住拿出手机一看,哎呦,不错哦,盈利28刀,孩子的半罐奶粉钱有了,哈哈! CSS深入理解vertical-align和line-height的基友关系

说起肥皂,让我想起了《监狱学院》,科科~

CSS深入理解vertical-align和line-height的基友关系

原来,肥皂和基友的传说已经传播到了11区。岂止啊,除了2次元,代码次元也深受其爱,比方说CSS届的 vertical-alignline-height 就是典型的表面上看上去大相径庭,实际上是大进后庭的断背好基友啊!

CSS深入理解vertical-align和line-height的基友关系

没错,就是这么狗血!

乡下人不打诳语,下面我就好好跟大家八卦下, vertical-alignline-height 之间令人发指的基友关系!

CSS深入理解vertical-align和line-height的基友关系

二、表现明显的断背基情

众所周知, vertical-align 支持很多属性值,足足可以组成一个足球队了:

/* 关键字值 */ vertical-align: baseline; vertical-align: sub; vertical-align: super; vertical-align: text-top; vertical-align: text-bottom; vertical-align: middle; vertical-align: top; vertical-align: bottom;  /* <长度> 值 */ vertical-align: 10em; vertical-align: 4px;  /* <百分比> 值 */ vertical-align: 10%;  /* 全局值 */ vertical-align: inherit; vertical-align: initial; vertical-align: unset;

其中,有个属性值暴露了 vertical-alignline-height 之间的基友关系,大家猜猜看是哪个属性值?

CSS深入理解vertical-align和line-height的基友关系

哇塞,好厉害!居然被大家一眼就看出来了,没错,就是“百分比值”。

vertical-align 的百分比值不是相对于字体大小或者其他什么属性计算的,而是相对于 line-height 计算的。举个简单的例子,如下CSS代码:

{   line-height: 30px;   vertical-align: -10%; }

实际上,等同于:

{   line-height: 30px;   vertical-align: -3px;    /* = 30px * -10% */   }

CSS属性何其多,偏偏跟 line-height 有一腿,这不是有基情那是什么?

//zxx: IE6/IE7浏览器下的vertical-align的百分比值不支持小数line-height

三、背地里无处不在的基友关系

//zxx: 注意vertical-alignline-height 的地下基友关系从HTML5文档声明开始的,因此,以下探讨的现象,都是在页面为HTML5声明前提下,类似下面的doctype:

<!doctype html> <html>

另外,下面很多效果直接就是真实演示,因此,请使用现代浏览器观摩下面的内容。如果发现某些行为与描述不匹配,且浏览器正常,那可能是因为你访问的并不是原出处。

① 基本现象

要八卦 vertical-alignline-height 之间的关系,我们不妨从一个极其简单的现象入手。假设,我们有一个 <div> 标签,然后,里面有一张 <img> 图片,我们的HTML代码就是这样子:

<div><img src="mm1.jpg"></div>

然后,表现就是一张图片呈现,类似下面这样:

CSS深入理解vertical-align和line-height的基友关系

恩,看上去很正常,一切都是理所当然。然而,如果我们给这个 <div> 元素增加一个背景色,例如淡蓝色:

<div style="background-color:#e5edff;"><img src="mm1.jpg"></div>

则会是下面这样:

CSS深入理解vertical-align和line-height的基友关系

会发现图片下面有一段空白空间:

CSS深入理解vertical-align和line-height的基友关系

想必大家都遇到过类似问题,不知大家有没有思考过,为什么图片下面有留有一段间隙呢? CSS深入理解vertical-align和line-height的基友关系

实际上,这段空白间隙就是 vertical-alignline-height 携手搞的鬼!

首先,大家一定要意识到这么一点: 对于内联元素,vertical-align与line-height虽然看不见,但实际上「到处都是」!

CSS深入理解vertical-align和line-height的基友关系

因此,对于内联元素各种想得通或者想不通的行为表现,基本上都可以用 vertical-alignline-height 来解释,以及进行行为矫正,然而,要深入理解这些行为表现,还是需要狠花一番功夫的,因此,下面的内容,请确保你有半小时充足时间细细阅读,别的地方可是看不到的。 CSS深入理解vertical-align和line-height的基友关系

② 幽灵空白节点

「幽灵空白节点」这个概念我自己命名的,注意,是我个人YY出来的,是我自己便于理解某些行为特征提出的概念。规范可能有类似的概念,但名称并非这个。 W3C规范虽然有很多行为的解释和说明,但是,毕竟官方的东西,要求严谨正式,但是,也会有太干太涩的感觉。如果快速掌握和理解这些行为表现呢?就我个人而言,从两方面入手: 1.情感化认知2. 具象化思维

例如,我称 vertical-alignline-height 为好基友(包括以前称浮动和绝对定位是兄弟),就是“情感化认知”;而这里的「幽灵空白节点」就是“具象化思维”。

那「幽灵空白节点」是个什么意思呢?

在HTML5文档声明下,块状元素内部的内联元素的行为表现,就好像块状元素内部还有一个(更有可能两个-前后)看不见摸不着没有宽度没有实体的空白节点,这个假想又似乎存在的空白节点,我称之为“幽灵空白节点”。 //zxx: 自己捣腾的概念,不是权威,欢迎其他小伙伴反馈权威解释

抽象了这个概念,绝对定位与 text-align 的一些行为表现,以及这里的行为表现,就好理解了。

还是上面的图片下边缘留空隙的例子,实际上,这种行为表现,就跟图片前面或者后面有一个宽度为0的空格元素表现是一致的。但是,空格是透明的,为了便于大家理解,我就直接使用很明显的匿名inline box, 也就是字符代替。如下,大家会发现,图片下面的间隙,依旧是那个间隙。

CSS深入理解vertical-align和line-height的基友关系 zxx

下面要解释这个间隙就好解释了。下面,我们让新增的文本inline-block化,然后弄个白色背景,显示其占据的高度。

CSS深入理解vertical-align和line-height的基友关系 zxx

会发现,图片下面的间隙,依旧是那个间隙。但是,我们的理解就好理解了。回答下面几个问题,我们就知道表现的原因了:

  1. vertical-align 默认的对齐方式是?
  2. 后面zxx文字的高度从何而来?

上面2个问题就很简单了:

  1. vertical-align 默认值是 baseline , 也就是基线对齐。而基线是什么,基线就是字母X的下边缘(参见“ 字母’x’在CSS世界中的角色和故事 ”一文)。所以,妹子图片的下边缘就和后面zxx中的字母 x 下边缘对齐(见下图)。而字符 zxx 本身是有高度的,对吧,于是,图片下面就留空了。
    CSS深入理解vertical-align和line-height的基友关系
  2. zxx 文字的高度是由行高决定的。

因此,简单的图片下面留白行为表现,本质上,就是 vertical-alignline-height 背地里搞基造成的。

知道了问题的原因,我们就可以对症下药,准确搞定图片下面我们不希望看到的间隙。怎么搞呢?一对基友, vertical-alignline-height 我们随便搞定一个就可以了。

比方说 vertical-align .

1. 让vertical-align失效

图片默认是 inline 水平的,而 vertical-align 对块状水平的元素无感。因此,我们只要让图片 display 水平为 block 就可以了,我们可以直接设置 display 或者浮动、绝对定位等(如果布局允许)。例如:

img { display: block; }

则妹子就会变这样:

CSS深入理解vertical-align和line-height的基友关系

下面的空隙不见了。

2. 使用其他vertical-align值

告别 baseline , 取用其他属性值,比方说 bottom / middle / top 都是可以的。

CSS深入理解vertical-align和line-height的基友关系 zxx

3. 直接修改line-height值

下面的空隙高度,实际上是文字计算后的行高值和字母x下边缘的距离。因此,只要行高足够小,实际文字占据的高度的底部就会在x的上面,下面没有了高度区域支撑,自然,图片就会有容器底边贴合在一起了。比方说,我们设置行高5像素:

div { line-height: 5px; }

CSS深入理解vertical-align和line-height的基友关系 zxx

4. line-height为相对单位,font-size间接控制

如果 line-height 是相对单位,例如 line-height:1.6 或者 line-height:160% 之类,也可以使用 font-size 间接控制,比方说来个狠的, font-size 设为大鸡蛋 0 , 本质上还是改变 line-height 值.

div { font-size: 0; }

CSS深入理解vertical-align和line-height的基友关系 zxx

③ 基本现象衍生:垂直居中

由于「幽灵空白节点」的存在,因此,我们可以进一步衍生,实现其他更实用的效果,比方说任意尺寸的图片(或者内联块状化的多行文字)的垂直居中效果。就是借助本文的两位男主角, vertical-alignline-height

你想啊,图片后面(前面)有个类似空格字符的节点,然后就能响应 line-height 形成高度,此时,图片再来个 vertical-align:middle ,当当当当,就可以和这个被行高撑高的「幽灵空白节点」(近似)垂直对齐了。

例如:

div { line-height: 240px; } img { vertical-align: middle; }

然后就会这样子:

CSS深入理解vertical-align和line-height的基友关系

不过上面的效果并不是完全的垂直居中,只是近似(稍微仔细看可以看出来)。为什么只是近似呢?那是因为「幽灵空白节点」还是默认的基线对齐。因此,实际上,此时的效果是图片的中线对齐后面幽灵字符的基线。

嘛嘛,单纯的文字还是太苍白了,截个图示意下吧:

CSS深入理解vertical-align和line-height的基友关系

因此,要想完全垂直居中,最先想到的方法就是让后面的“幽灵字符”也是 vertical-align:middle ,然而,呵呵,既然称之为“幽灵”就表示不会受非继承特性的属性影响,所以,根本没法设置 vertical-align:middle ,除非你自己创建一个显示的内联元素。

我们就没有办法了吗?当然不是,“幽灵字符”可以受具有继承特性的CSS属性影响,于是,我们可以通过其他东西来做调整,让字符的基线和中线在一起,或者说在一个位置上就可以了。有人可能要疑问了,这能行吗?啊,是可以的。

对于字符而言,基线的定义是什么?是字符x的下边缘,而中线的定义是什么,是基线往上1/2字符x的高度,于是,我们就可以列个公式,已知:x = x – 1/2 * x, 求x? 显然, x 就是 0 ,于是,我们只要让字符 x 的肉眼视觉高度为 0 就可以了。怎么搞?很简单, font-size:0 , 于是,完全垂直居中就是:

div { line-height: 240px; font-size: 0; } img { vertical-align: middle; }

结果是:

CSS深入理解vertical-align和line-height的基友关系

处女座的你,是不是看过去舒服多啦!? CSS深入理解vertical-align和line-height的基友关系

这种通过 line-height 定高,元素 vertical-align:middle 垂直居中的方法不仅适用于现代浏览器,连IE7浏览器也是支持的:

CSS深入理解vertical-align和line-height的基友关系

不过和其他浏览器再使用上还是有些需要注意的地方,就是,HTML不能这样:

<div><img src="mm1.jpg"></div>

而是需要在图片标签结束处留下空格后者换行:

<div><img src="mm1.jpg"><!-- 这里要折行或空格 --> </div>

④ 复杂现象

多年前曾分享过“ text-align:justify下列表的两端对齐布局 ”的技术,其中,为了让任意个数的列表最后一行也是对齐排列,在列表最后会辅助列表等宽的空标签元素来占位,类似下面红色高亮HTML代码:

.justify-fix { display: inline-block; width: 128px; }
<div style="text-align: justify;">  <img src="img/mm1.jpg" width="128">  <img src="img/mm1.jpg" width="128">  <img src="img/mm1.jpg" width="128">  <img src="img/mm1.jpg" width="128">  <i class="justify-fix"></i>  <i class="justify-fix"></i>  <i class="justify-fix"></i> </div> 

为了节约空间,我就使用小图示意:

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系

同样的,在白色背景下,似乎看上去效果还不赖,但是,如果给 div 容器加个背景色~~

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系

会惊讶的发现,下面多了很大一块间隙(如下截图):

CSS深入理解vertical-align和line-height的基友关系

为了便于大家看其究竟,我把占位 i 元素 outline 高亮下,于是,效果如下:

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系

结果会发现,上面巨大的空隙是由占位 i 元素上面和下面的间隙共同组成的。

下面问题来了:上面的间隙是如何产生的?下面的间隙是如何产生的?如果去除这些间隙呢?

很多时候,复杂问题是由简单问题组合而成的,实际上,这里的间隙现象的始作俑者和上面的简单现象一样,都是 vertical-alignline-height 搞基带来的不好的影响。

按照之前问题解决方法,我们可以直接来个 line-height:0 解决垂直间隙问题:

div { line-height: 0; }

结果图片和图片之间的间隙是没有了,但是,图片和最后的占位元素之间依然有个几像素的间距, CSS深入理解vertical-align和line-height的基友关系 ,啊啊啊啊,这究竟是什么鬼?

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系

简单现象的背后往往有大的学问,接下来是本文的高潮了,究其原因,要说到inline-block元素和基线baseline之间的一些纠缠的关系。

⑤ inline-block和baseline

CSS2的可视化格式模型文档中有一么一段话:

The baseline of an ‘inline-block’ is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its ‘overflow’ property has a computed value other than ‘visible’, in which case the baseline is the bottom margin edge.

英文看得眼睛大,于是我中文直译了下:

‘inline-block’的基线是正常流中最后一个line box的基线, 除非,这个line box里面既没有line boxes或者本身’overflow’属性的计算值而不是’visible’, 这种情况下基线是margin底边缘。

这段文档中出现了很多专有名词 line box , line boxes 等,这些是内联盒子模型中的概念,是CSS进阶必备知识。我在“浮动深入理解(一)”一文的中间穿插介绍了该模型。 //zxx: 我现在后悔了,内联盒子模型当初应该直接独立成一篇文章,这样其他文章可以很干净地引用,所谓文章的模块化书写

如果大家没有足够精力去学习之,可以先看下面这张图:

CSS深入理解vertical-align和line-height的基友关系

由于上面的译文是直译的,理解起来还是有些拗口,我使用通俗的话描述就是: 一个inline-block元素,如果里面没有inline内联元素,或者overflow不是visible,则该元素的基线就是其margin底边缘,否则,其基线就是元素里面最后一行内联元素的基线。

纳尼,还是没反应过来?

那我们看下面这个例子,应该就知道什么意思了。

两个同尺寸的 inline-block 水平元素,唯一区别就是一个空的,一个里面有字符,代码如下:

.dib-baseline {   display: inline-block; width: 150px; height: 150px;   border: 1px solid #cad5eb; background-color: #f0f3f9; }
<span class="dib-baseline"></span> <span class="dib-baseline">x-baseline</span>

结果,科科:

x-baseline

会发现,明明尺寸、display水平都是一样的,结果呢,两个却不在一个水平线上对齐,为什么呢?哈哈,上面的规范已经说明了一切。第一个框框里面没有内联元素,因此,基线就是容器的margin下边缘,也就是下边框下面的位置;而第二个框框里面有字符,纯正的内联元素,因此,第二个框框就是这些字符的基线,也就是字母x的下边缘了。于是,我们就看到了框框1下边缘和框框2里面字符 x 底边对齐的好戏。框框2有个小彩蛋,点击可以toggle其 innerHTML ,会发现,如果框框2里面没文字,就和框框1举案齐眉了。

下面我们要做一件很有必要的事情,用来帮助我们理解上面复杂例子在 line-height 值为 0 后的表现,什么事情呢?哈,同境界模拟,我们也设置框框2的 line-height 值为 0 ,于是,就会是下面这样的表现:

x-baseline

知道框框2为何又下沉了一点吗?

因为字符实际占据的高度是由行高决定的,当行高变成0的时候,字符占据的高度也是 0 ,此时,高度的起始位置就变成了字符content area的垂直中心位置,于是,文字就一半落在看看2的外面了。

CSS深入理解vertical-align和line-height的基友关系

由于文字字符上移了,自然基线位置(字母 x 的底边缘)也往上移动了,于是,两个框框的垂直落差就更大了。

OK,明白了上面的简单例子,也就能明白上面的复杂例子。紧接着,如果我们在最后一个占位的 <i> 元素后面新增同样的 x-baseline 字符,则:

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 x-baseline

大家是不是就可以明白原因所在啦!

额~居然还有小伙伴皱眉头,那我再用文字解释下:

现在行高 line-height0 , 则最后的 x-baseline 的垂直中线就和上面一列的图片对齐,而基线呢,就在中线下面差不多半个x的高度地方,而这个高度落差就是最后图片和容器的间隙高度值,因为前面的 <i class="justify-fix"> 是个空元素,基线是自身的底部,哈哈,造业啊!

CSS深入理解vertical-align和line-height的基友关系

OK,一旦知道了现象的本质,我们就能轻松对症下药了!要么改造占位 <i> 元素的基线、要么改造“幽灵空白节点”的基线位置、要么使用其他 vertical-align 对齐方式~

首先,来个最有意思的方法,对吧, 改造占位 <i> 元素的基线 。这个很简单,对吧,只要在空的 <i> 元素里面随便放几个字符就可以了,例如,里面有个 x

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 x x-baseline

会发现,间隙没有了! CSS深入理解vertical-align和line-height的基友关系 为什么呢?哈哈,因为 <i> 元素的基线和“幽灵空白节点”的基线位置现在一致了,没有了错位,自然就不会有间隙啦!

改造“幽灵空白节点”的基线位置,哈哈,使用 font-size ,字体足够小时,基线和中线会重合在一起,什么时候字体足够小呢,就是 0 . 于是,CSS代码( line-height 如果是相对值, line-height:0 也可以省掉):

div { font-size: 0; }

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系

使用其他 vertical-align 对齐方式 ,就是让两端对齐的列表元素 vertical-align:top/bottom/... 之类。

div { line-height: 0; } .justify-fix { display: inline-block; width: 128px; vertical-align: top; }

最后的效果是:

CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系 CSS深入理解vertical-align和line-height的基友关系

恩恩,各种方法都完美解决了垂直间隙的问题,来,各个大大的赞!

四、基友关系暴露之后

CSS深入理解vertical-align和line-height的基友关系

至此, vertical-alignline-height 的断背基友关系算是彻底暴露了,而且,从行为表现上来看, line-height 是攻, vertical-align 是个受。而很多内联元素的行为表现,就是这对基友搞七搞八一起搞出来的。

以前,关系处于地下的时候,我们可能不会明白,为何男厕所的卷纸用得比女厕所还快;但是,现在关系暴露了,很多以前我们想不明白的事情一下子就豁然开朗了。

因此,我们要以正确地心态去看待这对好基友,毕竟,他们可以CSS届非常重要的两个主力大将。

本文牵扯的知识点甚多,建议大家如果想在重构领域有所造诣,很多基本的却很深入的东西是很有必要弄透的。篇幅有限,有不少知识点都是一笔带过的,大家若有疑问,可以自己去检索与研究,例如, vertical-align 各个值的规范解释,内联盒子模型,等等。也欢迎各种方式交流。

文章都是周末熬夜写的,你知道的,现在不是当年,眼皮像灌了水银,因此,文章有表述或书写错误的地方在所难免,欢迎指正!

感谢阅读,周末愉快!另外祝贺中国队400米接力获得银牌。

本文为原创文章,包含脚本行为和样式控制,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。本文地址:
正文到此结束
Loading...