本文译自https://css-tricks.com/centering-css-complete-guide/ , 讲述了如何利用 css 来实现常见的水平和垂直居中。

人们一直都在抱怨CSS的设计者们,为什么一定要把一个简简单单的居中设计的如此困难。但我认为居中问题并不在于有多难,而是在于居中的使用场景非常多,对应到每种场景下的使用方法也因此各不相同。

那么接下来让我们来制作一个决策树,用来讲清楚什么情况下你应该使用什么方法来实现居中。希望它能有所帮助。

一. 水平居中

1. 行内元素或inline-*元素的居中(比如文本或链接)

如果它的父级是块状元素,你可以很容易的通过以下代码实现:

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

这种方法对 inline、inline-block、inline-table、inline-flex元素有效

2. 单个块状元素的居中

可以通过把它的margin-left和margin-right值都设置为auto来实现(前提是它已经设置好了width宽度,否则元素宽度就是100%,居中也就没有意义了)。代码如下:

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

这种方法无需关心子元素及父级元素的宽度

3. 多个块状元素的居中

如果你想让多个块状元素在同一行居中,那么你最好改变他们的display属性。display:inline-block以及display:flex都可以做到这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<main class="inline-block-center">
<div>
I'm an element that is block-like with my siblings and we're centered in a row.
</div>
<div>
I'm an element that is block-like with my siblings and we're centered in a row. I have more content in me than my siblings do.
</div>
<div>
I'm an element that is block-like with my siblings and we're centered in a row.
</div>
</main>

<main class="flex-center">
<div>
I'm an element that is block-like with my siblings and we're centered in a row.
</div>
<div>
I'm an element that is block-like with my siblings and we're centered in a row. I have more content in me than my siblings do.
</div>
<div>
I'm an element that is block-like with my siblings and we're centered in a row.
</div>
</main>
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
29
30
31
body {
background: #f06d06;
font-size: 80%;
}

main {
background: white;
margin: 20px 0;
padding: 10px;
}

main div {
background: black;
color: white;
padding: 15px;
max-width: 125px;
margin: 5px;
}

.inline-block-center {
text-align: center;
}
.inline-block-center div {
display: inline-block;
text-align: left;
}

.flex-center {
display: flex;
justify-content: center;
}

上述方法不适合这几个块状元素是垂直排列的时候,这种情况下把margin值设置为auto的方式依然是好使的。

二. 垂直居中

在css中,垂直居中比水平居中需要更多的技巧。

1. 行内元素或inline-*元素的居中(比如文本或链接)

单行

有时候行内元素或文字看起来垂直居中,其实仅仅是因为他们的上下内边距相等。

1
2
3
4
.link {
padding-top: 30px;
padding-bottom: 30px;
}

如果padding值不作为垂直居中的一个选项的话,同时你又想让一些不会折行的文字垂直剧中的话,让元素的line-height等于它的高度就可以了。

1
2
3
4
5
<main>
<div>
I'm a centered line.
</div>
</main>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
body {
background: #f06d06;
font-size: 80%;
}

main {
background: white;
margin: 20px 0;
padding: 40px;
}

main div {
background: black;
color: white;
height: 100px;
line-height: 100px;
padding: 20px;
width: 50%;
white-space: nowrap;
}

多行

设置相等的上下内边距同样也可以使多行文本垂直居中,但是如果这个不起作用的话,你可以把文本所在的元素换为table cell,或自由或强制的使之表现的像一个table元素。此时vertical-align属性就可以起作用了,它与我们通常所做的在行上处理元素对齐的方式不同

1
2
3
4
5
6
7
8
9
10
11
12

<table>
<tr>
<td>
I'm vertically centered multiple lines of text in a real table cell.
</td>
</tr>
</table>

<div class="center-table">
<p>I'm vertically centered multiple lines of text in a CSS-created table layout.</p>
</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
table td {
background: black;
color: white;
padding: 20px;
border: 10px solid white;
/* default is vertical-align: middle; */
}

.center-table {
display: table;
height: 250px;
background: white;
width: 240px;
margin: 20px;
}
.center-table p {
display: table-cell;
margin: 0;
background: black;
color: white;
padding: 20px;
border: 10px solid white;
vertical-align: middle;
}

如果你觉得这种方法过时了,或许你可以尝试下flexbox布局。这种方法会非常简单。

1
2
3
<div class="flex-center">
<p>I'm vertically centered multiple lines of text in a flexbox container.</p>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

.flex-center {
background: black;
color: white;
border: 10px solid white;
display: flex;
flex-direction: column;
justify-content: center;
height: 200px;
resize: vertical;
overflow: auto;
}
.flex-center p {
margin: 0;
padding: 20px;
}

请注意上面这种方法只在父级元素有固定高度的时候起作用那个。

如果你觉得以上两种方法都过时了,你可以试试伪元素。在伪元素撑满整个父级的时候,当前元素因为设置了vertical-align:Center而对齐。
(译者备注:这儿的伪元素before其实只是为了定下基线。before伪元素和需居中的元素都设置了display:inline-block,这样,他们的基线会对齐到同一行,又设置vertical-align:center,基线变为居中对齐,因此达到目标)

1
2
3
4

<div class="ghost-center">
<p>I'm vertically centered multiple lines of text in a container. Centered with a ghost pseudo element</p>
</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
29
30
31
32
33
34
35

body {
background: #f06d06;
font-size: 80%;
}

div {
background: white;
width: 240px;
height: 200px;
margin: 20px;
color: white;
resize: vertical;
overflow: auto;
padding: 20px;
}

.ghost-center {
position: relative;
}
.ghost-center::before {
content: " ";
display: inline-block;
height: 100%;
width: 1%;
vertical-align: middle;
}
.ghost-center p {
display: inline-block;
vertical-align: middle;
width: 190px;
margin: 0;
padding: 20px;
background: black;
}

2. 块状元素的居中

已知元素的高度

很多情况下我们是不知道页面布局的高度的,有很多原因:如果宽度改变,文档流会改变对应的高度。文档样式的改变,文档树木的改变同样也会改变高度。有固定宽高比的元素,例如image元素,在resize的时候高度也会随之调整。

但是你确实知道居中元素高度的话,你可以使用如下方法进行居中。

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; */
}

元素高度未知

仍然可以做到居中,你可以用 transform 属性,用 translate 设置 -50%(它以元素当前的宽和高为基础)

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

可以实用flexbox

使用flexbox进行垂直居中就简单多了。

1
2
3
4
5
.parent {
display: flex;
flex-direction: column;
justify-content: center;
}

(译者注 : 这也就意味着,在低版本浏览器里面,对于高度不固定的元素,垂直居中基本上是做不到的)

三. 水平垂直居中

你可以把上面提到的技术结合起来来达到效果。但是我发现总体来说会有三种情况:

1.元素的宽高固定

设定父级容器为相对定位的容器,设定子元素绝对定位的位置 position: absolute; top: 50%; left: 50%,最后使用负向 margin 实现水平和垂直居中,margin 的值为宽高(具体的宽高需要根据实际情况计算 padding)的一半

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

.parent {
position: relative;
}

.child {
width: 300px;
height: 100px;
padding: 20px;

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

margin: -70px 0 0 -170px;
}

2.宽高不固定

如果你不知道它的宽高,你可以使用transform属性,并把它的translate值设为-50%。

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

3.可以使用flexbox

1
2
3
4
5
6
7
8

<main>

<div>
I'm a block-level-ish element of an unknown width and height, centered vertically within my parent.
</div>

</main>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
main {
background: white;
height: 200px;
width: 60%;
margin: 0 auto;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
resize: both;
overflow: auto;
}

main div {
background: black;
color: white;
width: 50%;
padding: 20px;
resize: both;
overflow: auto;
}

4.

1
2
3
4
5
6
7
8
9
10
11
.dad {
position: relative;
}
.son {
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
}