7

CSS 垂直居中

 3 years ago
source link: https://lotabout.me/2016/CSS-vertical-center/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Table of Contents

简单一句 margin: 0 auto 我们便能搞定水平居中,而正当我们开心地写出 margin: auto 0 时,浏览器却没却无情地拒绝了我们,那我们来看看如何用 CSS 实现垂直居中吧。

要完全理解本文,我们假设你已经对 CSS 较为熟悉,包括 CSS 盒模型(box model), position 的常用方法,伪元素的使用等等。但若只是工作需要,照抄就是!

本文示例用 Jsfiddle 编辑,可能载入较慢,请耐心等待。

垂直居个中,怎么就这么难

正如前言所说,水平居中,通常只需要一句:margin: 0 auto 即可解决。而同样的方法对于垂直居中却没用,下例为证:

原因何在?我们查一下 CSS 2 的标准:计算高度时如果 margin-topmargin-bottom 的值为 auto,则它们的 ‘used value’ 为 0。也就是在计算高度时,margin 根本就没有 auto 的概念。

好吧,那让我们静下来想想,既然 auto 不能用,那我们自己设置不就行了吗?当然这就需要事先知道需要居中的元素的高度,再用 calc 指定 margin-top 就能搞定了。

诶?怎么跟说好的不一样?说好的这回就能居中呢?上网一查,CSS 果然是坑啊。 W3school 就写得明明白白:如果使用百分比作为 margin-top 的值,则百分比的基准是父元素的 宽度 。好吧,三观都粉碎了。但再仔细一查,margin-top 是按父元素的宽度算,但 top/bottom 是按父元素的高度算啊!于是我们想到了用 position: relative + top: calc(50% - height/2) 的手段:

皇天不负苦心人,终于被我们给拿下了!这时身后的设计表示:你再帮我在居中的元素里加点东西吧。WTF?居中元素的高度不准变啊,混蛋!

于是我们下面要处理的就是未知父元素高度,未知子元素高度情况下的垂直居中问题(图来源为 CSS-trick):

Unknown Child

vertical-align + table

尽管垂直居中问题困扰着我们,更让人困扰的是 CSS 里居然有一个属性名为 vertical-align,而且它有个值是 vertical-align: middle!但是用它根本不能垂直居中啊!谁设计的,老实站出来 -_-

好吧,既然不明白为什么,那就继续好好看文档吧:vertical-align 是用来指定内嵌元素(inline element) 和 table-cell 的垂直对齐方式。我们先将 元素转换成 table 来试试对齐。首先为父元素加上 display: table,为子元素加上 display: table-cell 来将它们变成表格的样式,再为子元素加上 vertical-align: middle 即可。如下例所示。

嗯,居中是居中了,而且也跟子元素的实际高度无关,但怎么感觉有点奇怪?嗯,是的,奇怪是因为父元素的宽度变小了,不像原来是 100% 的宽度。原因是 table 本质上也是 inline 元素,因此现在变成 inline 的父元素,它的宽度将与子元素的宽度相同。当然,我们也可以为父元素加上 width: 100% 来强制指定它的宽度。

另一个问题是子元素的高度变得和父元素一样高了。这对读者而言也许是问题,也许不是,就要自己考虑了。

伪元素的救赎

前面说到 vertical-align 可以用于垂直对齐,但它只能用于 inline 元素。比起 table,更为直接 的想法就是把子元素改成 display: inline-block,并加上 vertical-align: middle。只是可惜的是这样并不成功。

原因是 vertical-align 指的是当前 inline 元素自己,与其它 inline 元素如何对齐。而我们现在的情况是,只有一个 inline 元素,那自己跟自己,怎么对齐嘛。

但如果你用过 :before:after 伪元素的话,这就不是一个问题了。我们可以添加伪元素,让它的高度与父元素相同,这样子元素垂直对齐时就能居中了。如下图(来源为CSS-trick

Ghost element

这里要注意的是,为一个元素添加为元素,相当于为当前元素添加了子元素,因此为了生成一个 100% 高度的伪元素,我们需要对父元素添加伪元素。如下例:

嗯,看起来好像很不错了吧!只是不是特别喜欢这种方法,因为如果我们需要使用父元素的伪元素做一些其它的事情,同时又需要居中,那我们就无能为力了。不过 CSS 是在不断发展的,在 CSS3 中,我们又多了一些选择。下面我们介绍两种。

transform 的神力

之前我们想到了用 position: relative + top: calc(50% - height/2) 的方法,但这种方法需要知道子元素的高度,但有了 transform,我们就可以用 translateY(-50%) 来达到 - height/2 的目的,而不需要知道居中元素的高度。

只需要简单的三步:

.center-container {
position: relative;
top: 50%;
transform: translateY(-50%);
}

富人的思虑

现在已经有了许多的方法来实现垂直居中,尽管方法的效果不一,难度各异,可总的来说还是够用了。但一旦拥有的选择多了,反而无从下手了。那么不必着急,让我们看看它们的最后一个痛点:

<div class="container">
<div class="vertical">
<p id="p1"> A paragraph 1 </p>
<p id="p2"> A paragraph 2 </p>
</div>
</div>

如上 HTML 文件,我们为了居中 p1p2,而为它们加了一个层包裹层 .vertical。虽然也不是什么难事,但在某些情形下,我们是不能修改文档的结构的,其中一种可能是文档的内容是动态生成的。也就是,我们希望在现有的文档结构下,让某些内容垂直居中,这也许是最后一个痛点了。

那么下面我们就来看看最终的杀器:flexbox。

终结者 flexbox

flexbox 是 CSS3 为我们带来的瑞士军刀,几乎一切布局相关的问题都能用 flexbox 解决。这里我们先用实例来解决垂直居中的问题。如下:

可以看到,也是简单的3行:

.container {
display: flex;
flex-direction: column;
justify-content: center;
}

需要注意的是 CSS3 的支持问题。例如 IE 需要 IE11 才能支持。

关于 flexbox 如何使用,可以参考 A Complete Guide to Flexbox

可以说,整篇文章就是一句 margin: 0 auto 所引发的血案。而通过一步步地深入,我们也一步步接近关于 CSS 的丑陋真相。以及旧社会(CSS2)下的人们水深火热的生活,但好在社会是在不断发展进步的。我们最终还是迎来的美好的新时代。

由于本人水平有限,难免会有错误,还请不吝赐教。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK