vertical-align属性与垂直居中

让元素居中对齐是非常常见的需求,首先是水平居中,要实现水平居中行内元素只需要在其父元素上设置text-align: center即可,对于块级元素来说让它的margin-left: automargin-right: auto即可(width不可为auto),那么垂直居中呢?找下css属性发现了vertical-align,感觉就是它了,设置个vertical-align: middle,怎么没有达到预期效果?下面来详细介绍下vertical-align这个属性以及实现垂直居中的若干方法。

vertical-align属性是干什么的

根据W3C Spec中对vertical-align属性的定义:

This property affects the vertical positioning inside a line box of the boxes generated by an inline-level element.

什么是line box?同样来自W3C Spec

The rectangular area that contains the boxes that form a line is called a line box.

这个属性仅影响了单行中行内元素的垂直位置,那么我们会涉及到的元素应该是这样的:

  • inline
  • inline-block

既然我们要垂直居中,垂直居中是相对于垂直高度而言的,然而我们知道height对inline元素无效,那么line box的高度是怎么计算的呢?(还是引用W3C Spec)

The height of a line box is determined by the rules given in the section on line height calculations.

一个line box的高度是的计算方式如下:line box中的每一个行内元素都将加入到计算过程,如果是元素inline的则取其line-height的值,如果元素是inline-block或者是inline-table则取其margin-box的高度,最后这些值的最大值,即为line box的高度了。

好了,然后来看看vertical-align属性可以取哪些值:

可取值 说明
baseline 将元素的基线与父元素的基线对齐
middle 将元素的中线与父元素的基线加上x一半的高度对齐
sub 将元素置于基线下方合适的位置
super 将元素置于基线上方合适的位置
text-top 将元素的顶部与父元素正文区域的顶部对齐
text-bottom 将元素的底部与父元素的正文区域的底部对齐
top 将元素的顶部与line box顶部对齐
bottom 将元素的底部与line box底部对齐
< percentage > 基于基线上(正值)下(负值)移动元素,值通过百分比乘上行高而得
< length > 基于基线上(正值)下(负值)移动元素

这里有两个概念:基线(baseline)和正文区域(content area)。

先来感受下,举个栗子:

http://jsfiddle.net/leozdgao/y4oexshn/6/embedded/result,html,css/

在第一个栗子中,直观地展示了vertical-align属性所有可取属性的表现。

在第二个栗子中,蓝色的线表示的就是基线(记得小时候英语练习本每行的第三根线么),绿色的轮廓表示的就是正文区域,这里故意加了个较大的行高,于是红色的轮廓表示的就是line box的轮廓了。

对于有正文的行内元素而言,它的基线正如上面的栗子中所展示的那样,那么对于没有正文的行内元素(这里指的是有大小的inline-block元素但没有正文或者类似与img video这样的replaced element),它们的基线位于它们margin box的底部。

我们发现在这些可取的值中,大部分都与它们的父元素(通常为line box)的基线有关,那么line box的基线在哪里?或者栗子里的蓝线是根据什么画出来的?在W3C Spec中是这样说的:

CSS 2.1 does not define the position of the line box's baseline

是的,没有定义......不过好在浏览器之间的实现似乎没有什么区别,取的就是正常的一个文本的基线位置,这里就不再多纠结line box的基线是怎么计算的了。

这里额外说明下vertical-align: middle,根据上面的解释,什么是父元素的基线加上x一半的高度?还是来看个直观的栗子:

http://jsfiddle.net/leozdgao/hf3ocgd8/2/embedded/result,html,css/

蓝色的线依然表示基线,橙色的线表示的就是所谓的line box的基线加上x一半高度的位置。同时也展示了为什么仅给元素设置vertical-align: middle是没有办法达到效果的。

不过line box的基线并不是固定不动的,这次用一个实际的栗子来解释,比如在单行中有一张图片和一段文本,我们的需求是让文字垂直居中对齐:(我用一个inline-block的方块来模拟一张图片)

http://jsfiddle.net/leozdgao/pe8t1LLf/1/embedded/result,html,css/

第一个case是没有设置vertical-align时的样子,我们现在知道将文字部分设置vertical-align: middle是不正确的。在第二个case展示了在图片上应用vertical-align: middle,我们发现,为了满足图片中线与line box基线加上半个x高度的位置对齐,而图片位置不能移动了(整个line box就是它撑起来的),所以line box的基线被调整了。那么现在看上去好像是居中了?其实还是差一点,如case3中那样,这时将vertical-align: middle应用与文本上,就可以垂直居中了。

由此我们得到的结论是:对于那些直接影响着line box高度的行内元素来说,vertical-align对元素本身可能无影响,但会调整line box的基线。

垂直居中的若干种方法

垂直居中的需求往往并不限于单行文本,可能会设计多行文本,或者多行文本配图片等等,下面整理的各种垂直居中的方案,并试着分析其优劣。

方法一:

line-height设置为和height一样高,常用于导航栏或者标签页这样的单行文本居中,缺点是这种方法只能用于单行文本,如果还涉及到图片,可以根据上面一部分中提到的方法调整。

http://jsfiddle.net/leozdgao/150et323/embedded/result,html,css/

方法二:

利用css table,设置元素table结构,并应用vertical-align: middle实现垂直居中,这种方法的实现可用于多行文本,要求IE8及以上版本。

http://jsfiddle.net/leozdgao/00dgdbem/embedded/result,html,css/

方法三:

上下设置等高的padding或者margin值,个人觉得最笨的一种方法,唯一的优点是兼容所有浏览器,缺点是很不灵活,大小需要额外计算并写死。(不提供栗子)

方法四:

绝对定位,这种方法并不仅限于『居中』,一种是利用负的margin值来实现,另一种是用CSS3的translate来实现,优点的话同样是可以支持多行文本,只是负的margin会写死大小,仅适合与大小固定的元素,用translate要考虑css3的浏览器兼容问题,最大的局限性是绝对定位本身导致元素脱离文本流normal flow,多数情况下其父元素仅包括它一个子元素时会使用。

http://jsfiddle.net/leozdgao/h34zqLf2/1/embedded/result,html,css/

补充:支持CSS3的浏览器,也可以使用calc,像这样使用:

csstop: calc(50% - (300px / 2));

方法五:

同样是绝对定位,这个方法一般被称为Stretching,把4个定位方向全部设置为0,这种方式不会将大小写死,浏览器兼容性也很不错,但绝对定位的弊端上面也已经提到过了。

http://jsfiddle.net/leozdgao/5a9z676h/1/embedded/result,html,css/

这里简单解释下实现原理:我们发现了margin: auto,是的,我们给4个方向全部设置为0,即为元素提供了与其父元素相同的外边缘。接下来看W3C Spec中提到的:

If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra constraint that the two margins get equal values.

这是就垂直方向而言的:其中的three指的是top height bottom,就是margin值的计算结果会让元素尽可能居中。水平方向同理。

方法六:

隐藏一个浮动元素,这个浮动元素占父元素一半的高度,然后需要垂直居中的元素设置清除浮动,并设置大小为其高度一半的负margin-top,这个方法兼容任何浏览器,但总感觉有点hack的意味,不推荐。

http://jsfiddle.net/leozdgao/zoft69ao/1/embedded/result,html,css/

方法七:

FlexBox布局,浏览器需要支持,在移动端可大肆使用。

http://jsfiddle.net/leozdgao/4my89br7/1/embedded/result,html,css/


leozdgao
知识分享

前端爱好者

4k 声望
736 粉丝
0 条评论
推荐阅读
【译】MVC 在前端已死?
在过去的 4 年里,我看过许多 web 项目并花了大量的时间在前端架构或是为它整合一些框架。在 2010 年前,JavaScript(实现 jQuery 的语言)在传统 web 应用中被广泛用于 DOM 操作以及添加一些简单的东西。人们并...

leozdgao20阅读 12k评论 4

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木60阅读 5.9k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.1k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木39阅读 7k评论 6

CSS 绘制一只思否猫
欢迎关注我的公众号:前端侦探练习 CSS 有一个比较有趣的方式,就是发挥想象,绘制各式各样的图案,比如来绘制一只思否猫?思否猫,SegmentFault 思否的吉祥物,是一只独一无二、特立独行、热爱自由的(&gt;^ω^&lt...

XboxYan41阅读 2.8k评论 14

封面图
还在用 JS 做节流吗?CSS 也可以防止按钮重复点击
举个例子:一个保存按钮,为了避免重复提交或者服务器考虑,往往需要对点击行为做一定的限制,比如只允许每300ms提交一次,这时候我想大部分同学都会到网上直接拷贝一段throttle函数,或者直接引用lodash工具库

XboxYan34阅读 2.2k评论 2

封面图
从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...

乌柏木32阅读 6k评论 9

前端爱好者

4k 声望
736 粉丝
宣传栏