让元素居中对齐是非常常见的需求,首先是水平居中,要实现水平居中行内元素只需要在其父元素上设置text-align: center
即可,对于块级元素来说让它的margin-left: auto
和margin-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
,像这样使用:
css
top: 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/