本文首发于政采云前端团队博客:关于 vertical-align 你应该知道的一切 https://www.zoo.team/article/vertical-align
vertical-align,写过 CSS 的朋友们肯定都知道这个属性的作用,顾名思义,垂直对齐,主要目的用于将相邻的文本与元素对齐。MDN 中对它的定义如下:
一种简单的 CSS 属性,用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直对齐方式。
我们对于它的直观定义是与 text-align:center
相类似,一个控制水平方向对齐方式,一个控制垂直方向对齐方式。但是在很多情况下,我们发现设置属性之后并没生效。接下来让我们步步深入学习,共同揭开 vertical-align 的神秘 “面纱” 吧。
首先我们先讲一下,要实现垂直居中,我们为什么选择 vertical-align 这样一个不起眼的 CSS 属性。
float
:只能对齐它们的顶部,而且可能导致布局塌陷,需要手动清除position:absolute
:会使元素脱离文档流,以致于它们不能影响周围的元素transform:translateY
:属于 CSS3 新特性,对 IE8、IE9 有一些兼容性的问题使用 vertical-align
你能在不同场景下去进行灵活细微的元素对齐工作,并且它有很好的的兼容性,详情如下图所示:
在认识 vertical-align 属性之前,首先要了解几个基本的概念。
content area
:围绕文字看不见的 Box,其大小与 font-size 有关inline boxes
:不会成块显示,而是并排显示在一行的 boxes ,如 span
、 a
、 em
等标签以及匿名 inline boxes(即不含把标签的裸露的文字)line boxes
:由一个一个的 inline boxes 组成,一行即为一个 line boxcontaining box
:外层盒子模型,包含了其他的 boxesspan
、 img
、 input
、 button
、 td
以及通过 display 改变了显示水平为 inline 水平或者 table-cell 的元素。这也意味着,默认情况下,div
、p
等元素设置 vertical-align 无效值得注意的是:例如 float
和 position: absolute
,一旦设置了这两个属性之一,元素的 display 值被忽略,强制当成 block 方式处理,因此,vertical-align 也就失去了作用。
“除 Inherit 继承 之外,vertical-align 的属性值可以归为以下 4 类
.text
就是和这个字母 x 的下边缘对齐。
在实际应用中我们经常会遇到下图这种情况,你可能会容易的解决这种无法对齐的问题,但是你知道是什么原因导致他们这个样子吗?<ul class="box">
<li>文本</li>
<li></li>
</ul>
<style>
.li {
font-size: 20px;
width: 160px;
height: 160px;
display: inline-block;
border: 1px solid #ccc;
}
</style>
这里就涉及到了 inline-block 基线的定义,inline-block 的基线是正常流中最后一个(行盒子) line box 的基线,但是,如果这个 line box 里面没有 inline boxes 或者其 overflow 属性值不是 visible,那么其基线就是 margin bottom 的边缘。
如上图所示,第一个元素基线是子元素”文本“的基线,而第二个是盒子的底边缘,默认基线对齐,两个元素基线位置不一致,所有会产生上图现象,知道了原因,我们只需设置元素的 vertical-align 属性为 top/bottom/middle 就可以轻松对齐了。
<div class="box">
<span class="f12">12px</span>
<span class="f16">16px</span>
<span class="f20">20px</span>
<img src="./panda.jpg"/>
</div>
<style>
.box {
font-size: 16px;
}
img {
vertical-align: text-top;
width: 100px;
height: 100px;
}
.f12 {
font-size: 12px;
}
.f16 {
font-size: 16px;
}
.f20 {
font-size: 20px;
}
</style>
content-area 即围绕文字看不见的 box,其大小与 font-size 有关,可以看成是鼠标选中文字后高亮的背景色区域,上面的例子中,由于父元素字体设置的是 16px ,所以图片的 vertical-align 设置 text-top 的时候,就可以看成是跟子元素为 16px 元素的内容区域顶部对齐,它与 line-height 无关
super
属性效果相当于 html 标签 <sup></sup>
的效果sub
属性效果 相当于 html 标签 <sub></sub>
的效果需要注意的是:除了 top 与 bottom 是使元素相对于整行垂直对齐外,其他属性值都是相对于父元素。所以,在开发时,我们只需要关注当前元素和父级,两元素前后并没有直接影响。
说到 vertical-align 就要讲到它与 line-height 之间密切的关系,从上面我们都知道百分比类型是根据 line-height 来计算的。但事实是 对于内联元素,vertical-align 与 line-height 虽然看不见,但实际上「到处都是」。其实我们很多时候发现设置 vertical-align 属性无效,这很有可能就是 line-height 的原因了,下面我们来看两个典型的例子。
<div class="box">
<img src="./panda.jpg" />
</div>
<style>
.box {
width: 300px;
border: 1px solid #ddd;
}
img {
width: 100px;
height: 100px;
}
</style>
产生这种现象的原因:空白节点、line-height
和 vertical-align
属性;图片后放置空白节点 X,会发现图片的基线是元素底部,与“空白节点”的基线对齐,那解决这种问题有以下几个方法:
(1)将图片设置为 display:block (利用 vertical-align 的生效前提)
(2)将 vertical-align 设置为 top,bottom,或者 middle 等值(利用属性值的表现行为)
(3)将 line-height 设置为 0 (利用 line-height 为 0 时,基线上移)
(4)将 font-size 设置为 0 (如果 line-height 的值为相对值)
(5)将 img 设置浮动或者绝对定位 (如果布局允许的话)
<div class="box">
<span class="son"></span>
x
</div>
<style>
.box {
width: 300px;
height: 150px;
line-height: 150px;
font-size: 20px;
border: 1px solid #ddd;
position: relative;
}
// 绘制父元素的垂直中心线
.box::after {
content: "";
position: absolute;
display: block;
width: 100%;
height: 1px;
background-color: red;
top: 0;
bottom: 0;
margin: auto;
left: 0;
}
.son {
display: inline-block;
width: 100px;
height: 100px;
vertical-align: middle;
background-color: purple;
position: relative;
}
.son::after {
content: "";
position: absolute;
display: block;
width: 100%;
height: 1px;
background-color: #317ffd;
top: 0;
bottom: 0;
margin: auto;
left: 0;
}
</style>
如图所示(为了更明显我使用了色块来标识),当子元素(图片)设置了 vertical-align:middle
,并不是绝对居中,而只能说是近似居中。子元素的垂直中心线与父级元素基线的位置往上二分之一 X 高度(X 的中心) 所在线对齐,通俗一点讲,就是图中红线表示父元素的垂直中心线,蓝线表示子元素的垂直中心线,可以明显的看到 蓝线 与 X 的中心保持一致,但较红线偏低。如果绝对居中的话,两条线应该完全重合。
为什么会产生这种现象呢?主要原因在于文字具有下沉特性,从而导致蓝线无法绝对与红线对齐。当文字大小足够小时,我们可以忽略。从而近似的实现居中效果。但是文字越大,影响就越明显。
那对于这样的问题我们要怎么解决呢?以下提供几种思路:
1、设置后面的 “空白节点 X ” 的垂直对齐方式也是 vertical-align:middle
,然而,既然称之为 “空白节点” 就表示不会受非继承特性的属性影响,所以,根本没法设置 vertical-align:middle
,除非你自己创建一个显示的内联元素或者伪元素。
2、“空白节点” 可以受具有继承特性的 CSS 属性影响,于是,我们可以通过其他东西来做调整,让字符的中线和字符内容中心线在一起,或者说在一个位置上就可以了。设置父元素 font-size:0
, 因此此时 content area
高度是 0,各种乱七八糟的线都在高度为 0 的这条线上,绝对中心线和中线重合。效果如下:
这种通过 line-height
定高,元素 vertical-align:middle
垂直居中的方法不仅适用于现代浏览器,连 IE 浏览器也是支持的,但是这里只有在 IE7 中需要注意的是图片后面需要换行或者空格,经验证这个不是由于标签闭合引起的,可能只是一个 IE7 的 bug 吧。比较幸运的是,现在很多网站的兼容都是基于 IE9,所以可以忽略这个问题啦。
<div>
<img src="xxx.jpg">
<!-- 这里要折行或空格 -->
</div>
<ul>
<li class="text-container">
<span>我是单行文本我是单行文本</span>
</li>
<li class="text-container">
<span>我是多行文本我是多行文本我是多行文本我是多行文本我是多行文本我是多行文本</span>
</li>
</ul>
<style>
.text-container {
height: 150px;
text-align: center;
vertical-align: middle;
}
.text-container:after {
content: "";
display: inline-block;
width: 0;
height: 100%;
vertical-align: middle;
}
span {
vertical-align: middle;
display: inline-block;
max-width: 90%;
max-height: 100px;
overflow: hidden;
}
</style>
当然实现方式千千万,既然我们知道了 vertical-align 的原理。为什么不学以致用呢?按照之前的讲解,我们可以借助空白节点,空白节点我们看不见,但是如果可以给它设置一个高度,让它与父级高度一致,就解决了这个问题。怎么给高度呢?答案是借助伪元素。那么我们解决这类问题就可以使用以下步骤了:
display:inline-block
+ 辅助元素
来实现。<dl class="container">
<dt><img src="./7.jpg"/></dt>
<dt><img src="./7.jpg"/></dt>
<dt><img src="./7.jpg"/></dt>
<dt><img src="./7.jpg"/></dt>
<dt><i class="justify-fix"></i></dt>
<dt><i class="justify-fix"></i></dt>
<dt><i class="justify-fix"></i></dt>
xxx
</dl>
<style>
.container {
text-align: justify;
width: 400px;
margin: 50px auto;
border: 1px solid #ddd;
line-height: 0;
}
dt {
list-style: none;
display: inline-block;
width: 100px;
}
.container img {
width:100px;
height:100px;
}
.justify-fix {
display: inline-block;
width: 100px;
outline: 1px dashed #317ffd;
vertical-align: middle;
}
</style>
我们会一眼就看到在图片周围到处都是空白空隙,那么这些空隙是什么原因造成的呢?很多时候,复杂问题是由简单问题组合而成的,那么我们可以按照以下想法来简化问题。
我们可以想象整个布局只存在虚线框中的部分。大的部分都是由一块一块的虚线框中部分组合而成的。我们会惊喜的发现这个现象就是上面所说的任意一个块级元素,里面若有图片,则块级元素高度基本上都要比图片的高度高问题,那么产生的原因就知道了,是 line-height 与 vertical-align 之间关系产生的影响。
上面已经讲过如何解决此类问题,我们直接给父元素 line-height:0
,这样每个虚线框中小的空隙就消失了。但是可以明显的看到底部有很大的空隙并没有消除。为了更清楚,我把占位 i
元素 outline
高亮下。并且添加一个空白节点 x。
最后一个 dt 与我们手动添加的空白节点 X 的基线对齐。还记得前面说过的两个 inline-block 排列错位的例子吗? 这个现象就是由 inline-block 基线问题引起的。正如图中红色框展示的,dt 的基线是元素底部,根据上面所讲的,给占位元素i加一个 vertical-align:bottom/top
属性。然后就完美地解决了~
本文讲解了 vertical-align 的基本属性以及各种表现,同时对一些实际应用中 vertical-align 无效现象做了简单的分析阐述,并为解决此类问题提供了思路。本文讲解可能不全面,不成熟,欢迎在评论区留下宝贵评论,共同探讨,共同进步。