CSS 实现水平垂直居中布局

居中的实现是 CSS 布局中很典型(poster child)的问题。本文试图从不同的应用场景(水平居中、垂直居中、水平垂直居中)入手,尽可能列举常见的解决方案。

[参考]

《Centering in CSS: A Complete Guide》

《Absolute Horizontal And Vertical Centering In CSS》

水平居中

行内元素(inline elements)

  行内元素(包括 inline-block)的水平居中很容易实现,只需要在其父级 block 元素中添加 text-align: cneter 属性即可:

1
2
3
.father-block {
text-align: center;
}

  当父级元素设置了以上属性后,所有行内子元素都会居中,即使有多个子元素。

块级元素

  块级元素的水平居中只需要给该元素的 margin-leftmargin-right 属性的值设置为 auto。当然此时该元素需要设置宽度,不然默认是占满整行的不需要水平居中处理。通常使用简写方式:

1
2
3
.center-block {
margin: 0 auto;
}

多个块级元素的居中

  当有多个块级元素排布在一行或多行,需要水平居中时。可以将这些块级元素设置为行内元素 display: inline-block,然后在父元素中设置 text-align: center

  可以看到这三个子元素水平居中了。由于它们的高度不同,我们可以看出行内元素默认是底部对齐的。我们可以通过设置子元素 vertical-align: middle 使得三个子元素以高度中线对齐。

  使用 flex 弹性布局是个更好的选择:

  使用 flex 布局子元素的高度被统一。

垂直居中

  垂直居中的实现相对水平居中要复杂一些。我们依旧分类讨论:

行内元素

单行

  如果需要垂直居中的元素排布在一行上,可以通过设置父元素的 padding-toppadding-bottom 属性的值相等来使它的子元素垂直居中(其实上面水平居中的例子中,我们已经使用了这种方式使得子元素垂直居中了)。当然这种情况只能适用于父元素的高度未显式指定,而是自动适应的。

  如果父元素的 height 被指定且大于子元素的高度时,上面的方法是不起作用的。这时可以通过设置父元素的 line-height 属性的值使它等于父元素的 height,也能达到垂直居中的效果。

多行

  通过上面的例子我们可以发现,当行内元素仅仅是单行显示的话,可以很简单的设置父元素 line-height 等于父元素的高或上下 padding 值相等的方法使子元素垂直居中。

  在子元素包含多行文本时,通过设置父元素上下 padding 值相等的方法依然能够生效,不过还是会有同样的限制条件。不过设置子元素 line-height 的方法就行不通了。

  这时我们可以使用 table 布局或通过 CSS 模拟 table 布局实现垂直居中:

  table 布局实现垂直居中:

  通过 CSS 模拟设置父元素 display 为 table,设置子元素为 table-cell,那么子元素中的元素将垂直居中。不过这种方法的一个缺点是需要用到三层嵌套。

  table 布局已经不常用了,取而代之的是 flex 布局,在能使用 flex 的时候,flex 大概是最好的选择了。

  除了 table 布局和 flex 布局,还可以使用“ghost element”技术,给父元素添加一个全高度的伪元素 :before,使内部的文本与它垂直中线对齐。

块级元素

已知高度

  如果子元素的高度是固定的,或可预知的,则可以使用绝对定位 + 负外边距的方法实现垂直居中:

1
2
3
4
5
6
7
8
9
.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; */
}

  这种情况很好理解,不再举例。

未知高度

  使用绝对定位 + 2D变换(Y轴平移)的方法在未知高度的情况下也可以正常工作。

1
2
3
4
5
6
7
8
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
transform: translateY(-50%);
}

flex 布局

  毫无疑问像之前一样我们可以使用 flex 布局解决这个问题:

1
2
3
4
.parent{
display: flex;
align-items: center;
}

水平垂直居中

  将上面分别实现水平和垂直居中的方法相结合就能同时实现水平和垂直居中了,其实在上面的例子中已经这么做了,所以下面简单总结,不再举例。

固定宽高

  固定宽高的情况,我们可以直接使用绝对定位 + 负外边距修正的方式实现水平垂直居中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.father {
position: relative;
}

.child {
height: 200px;
width: 400px;

position: absolute;
top: 50%;
left: 50%;

margin-top: -100px;
margin-left: -200px;
}

自适应宽高

  与实现块级元素的垂直居中思路一致,当元素宽高不固定时,可以使用位置变换修正绝对定位的位置。

1
2
3
4
5
6
7
8
9
.father {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

flex 布局

  flex 布局总能轻松地解决各种情形下的居中问题。

  另外,需要注意的是 flex 中 align-itemsalign-content 属性的不同效果。

1
2
3
4
5
.father {
display: flex;
justify-content: center;
align-items: center;
}

table 布局

  像通过 table 布局实现垂直居中一样,我们可以在此基础上通过 margin: 0 auto 等手段再使得元素水平居中即可。

伪元素

  首先我们将需要居中的元素设置为 inline-block,然后给父元素添加满高伪元素,通过 vertical-align: center 的方法使元素垂直居中。只需要再在父元素总添加 text-align: center 属性即可实现水平居中。

grid 布局

  grid 布局在兼容性允许下好像是一个完美的布局方案,请参照 MDN 官方文档。

Absolute Centering

  这个一个基于绝对定位的居中处理方案,并且适用于宽高未知(百分比)的情况,可以实现多种常见布局,具有不错的兼容性。这里只简单介绍如果使用该技术实现垂直水平居中,更多应用场景和详细介绍请查看《Absolute Horizontal And Vertical Centering In CSS》

  其实,这个布局的关键在于设置局对定位后,将 top right bottom left 的值全设置为0,在指定宽高后,同时设置 margin: auto,(不可设置为0)后就可以实现了水平垂直居中。

总结

  我们可以看到实现水平居中往往是一件简单的事情,可以使用 margin: 0 auto,和行内元素的 text-align: center 设置实现水平居中。

  垂直居中通常是一件麻烦的事,我们往往需要根据应用场景来选择合适的方法。我们将垂直居中和垂直水平居中的实现一起总结:

  当元素的高度是固定值时,可以通过绝对定位 + 负外边距修正的方法实现垂直居中,然后通过 margin: 0 auto 再将元素水平居中。甚至当父元素的宽高也固定,我们可以直接计算子元素的居中定位的坐标即可,但实际开发中这种情况很少见。

  元素高度不定时,可以通过绝对定位 + translate 的方法实现居中,思路和负外边距一致。在不知道元素宽高时,可以通过 translate 的百分比值自动获取元素宽高的一半。而 margin 的百分比值是根据父元素的 width 值计算的,我们无法获取子元素的宽高的一半,这就是这里为什么不能通过指定 margin-top: -50%; margin-left: -50% 实现修正的原因。

  元素高度不定时,table 布局也是一个不错的解决方法,它利用了 table-cell 元素会沾满 table 父元素,并且可以使用 vertical-align: middle 使内部元素垂直居中对齐的特点。不过这种方法需要嵌套三层盒子。

  伪元素居中方法,该方法给父元素设置一个满高的伪元素,子元素设置为 inline-block。设置伪元素 vertical-align: middle 使得子元素与伪元素中线对齐,实现垂直居中。设置父元素 text-align: center,实现水平居中。不过这种情况可能需要给伪元素一个负的外边距,因为伪元素占了一定的宽度,导致子元素并非真正的水平居中。同时,子元素的宽度不能太宽(达到100%),不然可能使得子元素与伪元素分列两行。

  当高度不是固定(通过百分比指定)的时候,我们还可以采用绝对定位 + margin: auto 的方法实现垂直水平居中。详见最后一个实例。