CSS实现元素水平垂直居中

December 16, 2014 by sylvenas

水平居中

子元素是行内元素(inline)或者inline-block, inline-table, inline-flex

父元素直接设置text-align:center

子元素是块级元素

  • 子元素直接设置margin:0 auto;

  • 父元素设置position:relative,子元素设置position: absolute;left: 50%;transform: translateX(-50%);

注意这个时候需要子元素需要设置一个宽度(如果子元素不设置宽度的话,块级元素的宽度将是100%,也就不需要所谓的水平居中了)

多个块级子元素

如果有多个块级子元素需要居中对齐,最好的方法就是把多个子元素全部设置为display:inline-block,然后父元素设置text-align:center

垂直居中

子元素是行内元素(inline)或者inline-block, inline-table, inline-flex

  • 单行(一行能够摆开):父元素的line-height设置为和height的值一样,其实这个是利用line box垂直居中行来实现的。

先说一个大家都熟知的现象,有一个空的div,<div></div>,如果没有设置至少大于1像素高度height值时,该div的高度就是个0。如果该div里面打入了一个空格或是文字,则此div就会有一个高度。那么您思考过没有,为什么div里面有文字后就会有高度呢?

这是个看上去很简单的问题,是理解line-height非常重要的一个问题。可能有人会跟认为是:文字撑开的!文字占据空间,自然将div撑开。我一开始也是这样理解的,但是事实上,深入理解inline模型后,我发现,根本不是文字撑开了div的高度,而是line-height

<style>
    .test1{font-size:20px; line-height:0; border:1px solid #ccc; background:#eee;}
    .test2{font-size:0; line-height:20px; border:1px solid #ccc; background:#eee;}
</style>  

<div class="test1">hello world</div>
  <br>
<div class="test2">测试</div>

上面的代码的结果是怎么样的呢?

是不是很震惊,可以看到test1的文字font-size:20px,文字虽然现实出来了,但是隐式行内框hello world的外层div元素的高度却是0;而test2的文字font-size:0,文字没有显示出来,但是隐式行内框hello world却把外层div元素的高度撑起来了。

本质上的原因是行框的高度是由line-height来决定的,而不是行内元素的height决定的,更不是由行内的文字高度决定的。

其实line boxes不是直接的生产者,属于中层干部,真正的活儿都是它的手下 – inline boxes干的,这些手下就是文字啦,图片啊,<span>之类的inline属性的标签啦。line boxes只是个考察汇报人员,考察它的手下谁的实际line-height值最高,谁最高,它就要谁的值,然后向上汇报,形成高度。例如,<span style="line-height:20px;">取手下line-height<span style="line-height:40px;">最高</span>的值</span>。则line boxes的高度就是40像素了。

由上面的知识可以推导出,如果父元素内只有一个inline-*元素,那么只要把父元素的line-height设置为和height的值一样即可。(实质上这种情况下,父元素的height的值已经没用了,因为line-height具有继承性,也就是说子元素的line-height也被设置为了和父元素一样的行高,父元素的高度是由子元素撑起来的,这个时候父元素的height已经没有存在的价值了)

  • 多行

如果行内元素要占据多行的情况下,则稍微麻烦一点,可以使用老式的table布局,不过这个实在是太out了。

现在来说最为时尚的做法是使用伪元素,使用一个100%高度的伪元素进行一个占位,然后让行内元素相对于改伪元素进行垂直居中看代码:

<style>
.ghost-center {
    background: black;
    width: 240px;
    height: 200px;
    margin: 20px;
    color: white;
    resize: vertical;
    overflow: auto;
    padding: 20px;
}

.ghost-center::before {
    content: " ";
    display: inline-block;
    height: 100%;
    width: 1%;
    vertical-align: middle;
}

.ghost-center span {
    display: inline-block;
    vertical-align: middle;
    width: 190px;
    margin: 0;
    padding: 20px;
    background: rebeccapurple;
}
</style>

<div class="ghost-center">
    <span>
        I'm vertically centered multiple lines of text in a container. Centered with a ghost pseudoelement
    </span>
</div>

子元素是已知高度的块级元素

这种情况是最简单的,一般思路是父元素先使用绝对定位,把子元素的左上角拉到父元素的中间位置,然后使用负margin,translateY,等等把子元素往上拉一半的高度:

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  height: 100px;
  margin-top: -50px; /* account for padding and border if not using box-sizing: border-box; */
}

子元素是未知高度的块级元素

其实这个时候整体思路上和上面的是一样的,不同的地方在于,这种情况下不能再使用负margin-top来把元素拉上去,因为没法设定margin-top的具体的值,更不能用百分比,因为margin的百分比是相对于父元素的宽度而言的,这个时候只能用相对于自身高度的translateY:

.parent {
  position: relative;
}
.child {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

conclusion

以上边上常用的水平垂直居中对齐的各种方案,一般能够应付常见的业务场景了。不过用现在浏览器兼容性的程度来说,flex box会是更简单方便的方案了。