CSS实现水平垂直居中

44年前我们把人送上月球,但在CSS中我们仍然不能很好实现垂直居中——@James Anderson

让一个元素水平居中对于CSS来说非常简单:如果是一个内联元素,我们可以在他的父元素上设置text-align:center;,如果是一个块元素,我们可以使用margin:auto;。然而,只要一想到让一个元素垂直居中,让人死的心都有了。

多年来,垂直居中已成为CSS的不朽神话,也是前端专业人士群体中的一个内部笑话。原因是:

  • 经常需要使用
  • 理论上看上去非常简单
  • 过去实战中要实现是极其困难,特别是元素大小固定时

方法一:绝对定位

实现过程

1
2
3
4
5
6
<div class="container">
<h2>完全居中--1.绝对定位</h2>
<div class="center">
内容盒子
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.container{
position:relative;
height:200px;
width: 400px;
background-color: rgba(0,0,255,0.2);
}
.center{
height: 100px;
width: 100px;
position:absolute;
margin:auto;
top:0;left:0;bottom:0;right:0;
background-color: rgba(255,255,0,1);
}

参见 DEMO:完全居中–1.绝对定位

方法二:绝对定位+负margin

这或许是最常用的方法。如果知道了各个元素的大小,设置等于宽高一半大小的负margin值(如果没有使用box-sizing: border-box样式,还需要加上padding值),再配合top: 50%; left: 50%;样式就会使块元素居中。

给容器设置绝对定位(position:absolute),并且定位高度(top:50%)和margin-top为高度的一半(margin-top:-height/2),宽度设置类似。

这就意味着使用这种方法来实现垂直居中的效果,那么元素必须要有一个固定的高度。这样一来,给元素设置了固定高度,如果又设置了overflow:auto,那么当元素内容超过容器后,这样元素的就会出现滚动,而不会自适应内容的高度。

实现过程

1
2
3
4
5
6
<div class="container">
<h2>完全居中--2.绝对定位+负margin值</h2>
<div class="center">
内容盒子
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.container{
position:relative;
height:200px;
width: 400px;
background-color: rgba(0,0,255,0.2);
}
.center{
height: 100px;
width: 100px;
position:absolute;
top: 50%;
margin-top:-50px;
left: 50%;
margin-left:-50px;
background-color: rgba(255,255,0,1);
}

如果使用CSS3中的calc()可以减少两个样式:

1
2
3
4
5
6
7
8
.center{
height:100px;
width:100px;
position:absolute;
top:calc(50% - 50px);/* calc中的`+` `-` 必须被空格包围,`*` `/` 则不需要*/
left:calc(50% - 50px);
background-color: rgba(255,255,0,1);
}

参见DEMO:完全居中–2.绝对定位+负margin值

优点

  • 浏览器兼容性非常好,甚至支持IE6-7
  • 需要的编码量很少

    缺点

  • 这是个非响应式的方法,不能使用百分比的大小,也不能设置min-/max-的最大值最小值。
  • 内容可能会超出容器
  • 需要为padding预留空间,或者需要使用box-sizing: border-box样式。

方法三:模拟表格

这种可能是最好的方法,因为高度可以随内容改变,浏览器支持也不差。主要缺陷是会产生额外的标签,每一个需要居中的元素需要三个额外的HTML标签。

实现过程

设置父元素display:table,子元素display:table-cell; vertical-align:middle

1
2
3
4
5
<div class="table">
<div class="table-cell">
<div class="content">完全居中--3.模拟表格</div>
</div>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.table{
display:table;
width:600px;
height:300px;
}
.table-cell{
display:table-cell;
vertical-align: middle;
background-color: rgba(255,0,0,0.3);
}
.content{
width:50%;
height:50%;
margin:0 auto;
background-color: rgba(0,255,0,0.5);
}

参见DEMO:完全居中–3.模拟表格

优点

  • 内容高度可变
  • 内容溢出则能自动撑开父元素高度
  • 浏览器兼容性好

缺点

  • 需要额外的HTML标签

方法四:inline-box

基本方法是使用display: inline-block,vertical-align: middle样式和伪元素让内容块在容器中居中。

实现过程

1
2
3
4
5
6
<div class="container">
<h2>完全居中--4.inline-block</h2>
<div class="center">
内容盒子
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
.container{
width:600px;
height:200px;
text-align:center;
background-color: rgba(0,0,255,0.2);
}
.center{
width:50%;
height:50%;
display:inline-block;
vertical-align:middle;
background-color: rgba(255,255,0,1);
}

参见完全居中–4.inline-block

优点

  • 内容高度可变
  • 内容溢出则能自动撑开父元素高度
  • 浏览器兼容性好,甚至可以调整支持IE7

    缺点

  • 需要额外容器
  • 依赖于margin-left: -0.25em的样式,做到水平居中,需要为不同的字体大小作调整
    内容区声明的宽度不能大于容器的100% 减去0.25em的宽度

方法五:transform(CSS3)

为内容指定带有厂商前缀的transform: translate(-50%,-50%)top: 50%; left: 50%;样式就可以让内容块居中。

实现过程

1
2
3
4
5
6
<div class="container">
<h2>完全居中--5.transform</h2>
<div class="center">
内容盒子
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.container{
width:600px;
height:200px;
position:relative;
background-color: rgba(0,0,255,0.2);
}
.center{
position:absolute;
width:50%;
height:50%;
top:50%;
left:50%;
-webkit-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
background-color: rgba(255,255,0,1);
}

参见DEMO:完全居中–5.transform

优点

  • 内容高度可变
  • 代码量小

    缺点

  • 不支持IE8
  • 需要写厂商前缀
  • 会和其他transform样式有冲突
  • 某些情况下的边缘和字体渲染会有问题

方法六:flex(CSS3)

CSS未来发展的方向就是采用Flexbox这种设计,解决像垂直居中这种共同的问题。请注意,Flexbox有不止一种办法居中,他也可以用来分栏,并解决奇奇怪怪的布局问题。

实现过程

1
2
3
4
5
6
<div class="container">
<!-- <h2>完全居中--5.flex</h2> -->
<div class="center">
内容盒子
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.container{
width:600px;
height:200px;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;

-webkit-box-align: center;
-moz-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;

-webkit-box-pack: center;
-moz-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;

background-color: rgba(0,0,255,0.2);
}
.center{
width:50%;
height:50%;
background-color: rgba(255,255,0,1);
}

优点

  • 内容可以是任意高宽,溢出也能表现良好
  • 可以用于各种高级布局技巧

    缺点

  • 不支持IE8-9
  • 需要在body上写样式,或者需要额外容器
  • 需要各种厂商前缀兼容现代浏览器
  • 可能有潜在的性能问题

完全居中对照表

所用样式 支持的浏览器 是否响应式 内容溢出后的样式 resize:both 高度可变 主要缺陷
absolute 现代浏览器&IE8+ 会导致容器溢出 是* ‘可变高度’的特性不能跨浏览器
负margin值 所有 带滚动条 大小改变后不再居中 不具有响应式特性,margin值必须经过手工计算
transform 现代浏览器&IE9+ 会导致容器溢出 妨碍渲染
table-cell 现代浏览器&IE8+ 撑开容器 会加上多余的标记
inline-block 现代浏览器&IE8+&IE7* 撑开容器 需要使用容器包裹和hack式的样式
flex 现代浏览器&IE10+ 会导致容器溢出 需要使用容器包裹和厂商前缀(vendor prefix)

参考文献