很多程序员知道 vertical-align 的作用,但是对于 vertical-align 到底怎么对齐的和它的适用对象却一知半解,因此经常遇到 vertical-align 不起作用的问题,今天我们就来说说 vertical-align。
不过 vertical-align 属性牵扯到的知识实在是太多了,不是一篇文章就可以讲清楚的,这里我就只说一说重要的和日常用到的知识点,本文不涉及表格单元格(table-cell)元素的垂直对齐方式。
为什么要用 vertical-align
我们经常需要在竖直方向上对齐一些并肩排列的元素。例如表单输入框开头的图标,图片垂直居中,多行文字垂直居中等等。
CSS 为我们提供了一些实现方法。我们可以使用 flex 布局,也可以用 position:absolute,有时候甚至利用手动添加内外边距的方法。
然而 flex 的 PC 兼容性相对较差,IE 要 10,甚至 11 以上才有很好的兼容,绝对定位会使元素脱离文档流,以致于增加页面的复杂度。而使用固定的内外边距,太过死板,需求有了变化就要重新计算 margin,不便于项目以后的更改。
这里还有另一种值得尝试的解决方案,那就是 vertical-align。它能让你在不同场景下使用 vertical-align 去进行灵活细微的元素对齐工作,而你并不需要知道元素的具体尺寸。
然而,vertical-align 不是万能的,当 div 里的子节点过多时,由于 line-box 的基线不确定,子节点越多,布局就越难控制,此时就应该考虑用 position 等等。
浏览器支持
所有浏览器都支持 vertical-align 属性。
注释:任何的版本的 Internet Explorer (包括 IE8)都不支持属性值 "inherit"。
作用
veritical 是垂直的意思,而 align 是对齐的意思,两个合起来就是在垂直方向进行对齐。
MDN 上写到:vertical-align 用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方式
适用对象
vertical-align 用于对齐行内级元素。行内级元素就是 display 属性是下面其中的一种的元素:
inline,inline-block,inline-table(本文不涉及)
base-line(基线)和 outer edge(外边沿)
理解 vertical-align 最关键的一点在于理解相关元素的基线和上下边沿。
在 inline 元素中
上图行高的上下边沿用红线表示,绿线表示字体的高度,蓝线表示基线。
行内元素的外边沿
从上图中你可以看到三种情况。
- 左图中的行高和字体尺寸相同,所以红线和绿线重合在一起了。
- 中间的图片,行高是字体高度的两倍。
- 右图行高是字体高度的一半。
总结:行内元素的外边沿就是它们的行高的上下边沿。
行内元素的基线
行内元素的基线是字符下面的一条线。具体是下图中的蓝线。
在 inline-block 元素中
左图包含 in-flow 内容(一个字母 C)的 inline-block 元素;
中间的图片包含 in-flow 内容并且设置了 over-flow:hidden 属性的 inline-block 元素;
右图没有 in-flow 内容,但是设置了高度属性的 inline-block 元素。
三个例子都设置了 margin 属性。
红线是 margin 的边缘,黄色部分是边框,绿色部分是内边距 padding,蓝色部分是内容。蓝线是各个元素的基线。
inline-block 元素的外边沿
inline-block 元素的外边沿是它们的 margin-box 的上下边沿。也就是图中的红线。
inline-block 元素的基线
inline-block 元素的基线取决于元素是否有 in-flow 内容:
- 包含 in-flow 内容的 inline-block 元素的基线是普通文档流中最后一个元素内容的基线(例如左图)。这最后一个元素的基线如何定位,那就看它是什么类型的元素了。
- 包含 in-flow 内容而且设置了 overflow 属性(值非 visible)的 inline-block 元素,其基线是 margin-box 的下边沿(例如中间的图)。所以它的基线就和自己的底边沿重合了。
- 不包含 in-flow 内容的 inline-block 元素(例如右图)和上者相同,同样是底边线。
在 line-box 中
line-box 简介
inline 元素在一行放不下所有元素的时候,就会在下面创建一个新行。所有的行都有一个叫做 line-box 的东西,它是用来包裹这一行所有内容用的。它包含一条基线,一个 text box,一个上边沿,一个下边沿。line-box 的 base-line(基线)和 outer edge(外边沿),和上面的 inline 元素的 base-line(基线)和 outer edge(外边沿)不一样。
line-box 规定出了我们对齐元素时的活动范围。在 line-box 中 vertical-align 负责每个元素的竖直对齐方式
如下图中所示:line-box 的上下边沿用红线标出。(line-box 的边沿平时是看不到的,画出来是为了方便读者理解)
line-box 的外边沿
line-box 的上边沿和行内最高的元素的上边沿对齐;同样下边沿和行内最低元素的下边沿对齐。这两个边沿也就是图中的红线。
line-box 的基线
line-box 的基线是会变化的:CSS2.1 没有定义 line-box 的 baseline,这就意味着,line-box 的基线是会根据一定的规则动态改变的。
由于 line-box 的基线是不可见的,所以你也不可能准确的说出它到底在哪里。但是能通过一个简单方法知道它在哪。只要在一行内容的行首添加一个字符就行,例如:我加了一个"x"(行首灰色的那个)。如果这个字符没有进行任何人为方式的对齐,那么它的基线就默认是在 line-box 的基线上。
text box
line-box 的基线周围的空间,我们可以称之为 text box,在 W3C 说明中,text box 叫做 strut。
你可以把 text box 看做是一个在 line-box 中没有进行任何对齐的 inline 元素。它的高度等于父元素的字体大小。因此 text box 是用于包裹 line box 中没有设置任何对齐的文字的,换言之,没有设置任何对齐的文字默认是在 text-box 中。
上图中绿线之间的部分就是 text box。由于 text box 是和基线紧密相连的,因此它会随着基线的移动而移动。
line-box 的基线是会移动的
缺点:line-box 的基线的位置受其内部所有元素的影响。所以 div 里面的元素尽量简单,不然布局很难控制。
有一个元素刚好撑满了 line-box
如果有一个比较高的元素刚好撑满了 line-box,vertical-align 对它来说就没用任何影响了。因为它的下面和上面已经没有位置可以让它再移动了。
下面两幅图中矮小的盒子都设置 vertical-align:baseline。左图的高盒子设置为 vertical-align:text-bottom。右图的高盒子设置为 vertical-align:text-top。你就会发现两个矮盒子位置不一样,证明两幅图中 line-box 的基线是不在同一位置,因为矮盒子是跟着基线对齐的。
没有盒子撑满 line-box
将两个比较高的盒子放置在一行,调整它们两个的垂直对齐方式使基线能够同时满足两种对齐。然后 line-box 的的高度就得到了调整(如左图)。再添加第三个元素,它的高度不会超过 line-box 的边缘,因为它的定位方式既不会影响 line-box 的高度,也不会影响基线的位置(如中图)。如果它真的超出了原有 line-box 的边缘,line-box 的高度和基线都会重新调整。这种情况我们的前两个盒子就会下移(如右图)。
取值
我们通过使用 vertical-align 可以把上面我们介绍的那些参考点(基线,上线边沿等)设置成确定的关系
主要分一下三种情况:
相对于 line-box 的基线进行对齐
将元素的基线相对于 line-box 的基线进行对齐
baseline
将元素的基线对齐到 line-box 的基线上;
sub
将元素的基线对齐到 line-box 基线下方;
super
将元素的基线对齐到 line-box 的基线上方;
< percentage >
元素的基线相对于 line-box 的基线进行确定距离的对齐,定位的距离和方向由设置的百分比数值决定,百分比是相对于 line-height 的。
< length >
元素的基线相对于 line-box 的基线进行确定距离的对齐,定位的距离和方向由设置的具体长度值决定。
将元素的外边沿相对于 line-box 的基线进行对齐
middle
使元素的上沿和下沿的中点对齐到字母“x”的基线上边加上一半 x 的高度上(其实就是字母 x 的正中间)。
相对于 line-box 的 text box 对齐
将元素的外边沿相对于 line-box 的 text box 对齐
以下的两种情况,其实也是相对于 line-box 的基线的一种对齐,因为 text-box 的上下边沿也是取决于基线的。
text-top
元素的上边沿对齐到 line-box 的 text-box 的上边沿;
text-bottom
元素的下边沿对齐到 line-box 的 text-box 的下边沿。
相对于 line-box 的外边沿对齐
将元素的外边沿相对于 line-box 的外边沿对齐
top
将元素的上边沿和 line-box 的上边沿对齐;
bottom
将元素的下边沿和 line-box 的下边沿对齐;
应用例子
使图标和文字居中
同学们常常有一个问题:当我让一个图标和它旁边的文字居中对齐时,仅仅对图标使用 vertical-align:middle 似乎并没有真正的使两者居中对齐? 我们来看一个例子: html:
<span class="icon middle"
></span> Centered?
<span class="icon middle"
></span> <span class="middle">Centered!</span></span></span
></span
>
2
3
4
5
6
css:
.icon {
display: inline-block;
}
.middle {
vertical-align: middle;
}
2
3
4
5
6
效果(添加了一些辅助线,实际上看不见):
从图中看出,左图灰色方块离文字的中心稍微下移了,右图的图标和右边的文字就完美的居中对齐了。
原因:
因为左图中的文字没有经过对齐处理,所以它默认是在 text-box 中的,它的基线和 line-box 的基线是重合的。通过给灰色方块设置 vertical-align: middle;,我们就把灰色方块上沿和下沿的中点对齐到字母“x”的基线上边加上一半 x 的高度上(其实就是字母 x 的正中间)。但是 x 字母的正中心并不是文字所在的 text-box 的正中心,而是 text-box 中心稍稍的偏下了一点。
再看右图,我们把“Centered!”包裹起来并为也设置 vertical-align: middle;,就把“Centered!”的上沿和下沿的中点对齐到字母“x”的基线上边加上一半 x 的高度上(其实就是字母 x 的正中间)。这样灰色方块和文字的中点就在同一水平线上了。(上面《line-box 的基线是会移动的》的第二种情况)
使图片垂直水平居中
html:
<div id="wrap"><img src="a.jpg" /></div>
css:
#wrap:after {
content: '';
display: inline-block;
height: 100%;
width: 0px;
vertical-align: middle;
}
#wrap img {
vertical-align: middle;
}
2
3
4
5
6
7
8
9
10
效果(根据图中蓝色的 after 伪类实际上不可见,为了演示而增加):
图片垂直居中了!
原理:
如果没有添加 after 伪类,div 里面添加了一个文本“x”,如下图:
如下图,如果添加 after 伪类,div 里面再添加了一个文本“x”,此时 after 伪类没有 in-flow 内容(但是设置了高度属性),基线在 div 最底下,给设置 vertical-align: middle;,就会将元素的外边沿相对于 line-box 的基线进行对齐,所以“x”向下移,跑到了 after 伪类中间,所以图片也跟着“x”跑到了 after 伪类中间 (上面《line-box 的基线是会移动的》的第一种情况)
消除 inline 元素下面的小空隙
当你想对齐竖直对齐 li 元素的时候。
代码:
<ul>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>
< style type="text/css" > .box { display: inline-block; } < /style >
2
3
4
5
6
7
效果:
正如你所看到的,由于 li 元素默认是和基线对齐的,基线下面是留有一部分空白的,这个空白是可以容纳半个“x”的空间。这就导致了空隙的存在。如何解决?我们可以改变基线的位置,例如给 li 元素设置对齐方式为 vertical-align: middle
效果:
左图下面的空白线消失了!这种情况不会发生在包含文本内容的 inline-block 元素身上,因为文本内容已经把基线的位置抬高了。
多行文本水平垂直居中
html:
<div id="wrap">
<span
>xcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdc dc xcdcdc
xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc
xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc
xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc
xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc xcdcdc xcdcdcxcdcdc xcdcdc
xcdcdc xcdcdc
</span>
</div>
2
3
4
5
6
7
8
9
10
#wrap {
width: 500px;
height: 500px;
background-color: #ff000030;
}
#wrap:after {
content: '';
display: inline-block;
height: 100%;
width: 0px;
vertical-align: middle;
background-color: blue;
}
#wrap span {
vertical-align: middle;
display: inline-block;
text-align: center;
max-width: 495px;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
效果:
line-height、font-size、vertical-align 是设置行内元素布局的关键属性。这三个属性是相互依赖的关系,通过组合三者可以达到很多效果。如果能理解运用本文章,工作中的不少关于 vertical-align 的问题都能在此得到解决,由于篇幅有限,关于 line-height 和 font-size 的探究以后再另开文章,如果想了解的可以关注作者。
参考文章:
你所不知道的 vertical-align
Vertical-Align: All You Need To Know