水平且垂直居中 CSS 详解

## 自适应的水平垂直居中

旅行商人罗伦斯在马车货台上的麦束堆里,发现沉睡中的少女——自称是丰收之神赫萝的美丽少女,有着狼的耳朵及尾巴。

「虽然奴家长久以来被尊为神,不过,奴家就是奴家,奴家是赫萝。」

相比于水平居中用 margin:auto; 的简便,CSS 没有直接的垂直居中属性,必须通过各种诡异的技巧去实现,确实让人很头疼。以上算是一种实现方式,核心的代码如下:
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
<style type="text/css">
.outer {
text-align: center;
background: #b9f9ff;
height: 300px;
}
.outer:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.inner {
display: inline-block;
width: 300px;
vertical-align: middle;
background: #fffdc6;
}
</style>

<div class="outer">
<div class="inner" >
<p>水平垂直居中</p>
</div>
</div>

这段代码中,最重要的是 .outer:before 这个伪元素。它起到了连接内外元素以及垂直对齐的作用。

需要明白的是,对一个 div 而言,它的伪元素会放在这个 div 包含的任何内容元素的前面,默认为 inline-block 元素(即“行内元素”或者“内联元素”)。示例:

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

<style type="text/css">
.outer {
background: #b9f9ff;
height: 120px;
width: 300px;
margin: auto;
}
.outer:before {
content: ':before 伪元素';
border: 1px solid;
}
</style>

<div class="outer">元素内容</div>

执行结果:

元素内容

当元素内容为其它元素,尤其是块级元素时,则有内容的 :before 会在最顶上。

<p>元素块级内容</p>

再强调一遍,:before 其实是一个元素的子元素,永远排在其它实际子元素的前面,比如:

1
2
3
4
5
6
7
8
9
10
11
12
<style type="text/css">
p {
border: 1px solid;
}
p:before {
content: ':before';
background:#fffdc6;
color: #f00;
}
</style>

<p class="special">是特殊的子元素</p>

的实际执行效果如下:

是特殊的子元素

可以看到,对于 p 元素添加的 border,连 :before 这段文字也被包含在内。

现在再回头来看原始代码,如果把 :before 的部分修改一下,给 :before 元素加个边框,修改内容,使得识别更简单:

1
2
3
4
5
6
7
8

.outer:before {
content: 'B';
border: 1px solid;
display: inline-block;
height: 100%;
vertical-align: middle;
}
垂直居中
这样这段代码的原理就非常清楚了。首先,:before 伪元素和内部元素(通过 display:inline-block 设置为行内元素)共同构成了“一行文字”。其次,所有的行内元素都利用 vertical-align:middle 进行了垂直居中对齐。最后,再设置 :before 的高度使得它上下都顶到父元素的边框。这样,同一”行”内的其它元素在视觉就都相对父元素垂直居中了。其实它们在逻辑上真正对齐的对象是 :before。而 :before 自己,则被“卡”到了正中间的位置。

最后,由于 :before 在没有实际内容(通常用 ” ” 空格填充 content 属性)时其实也有宽度,所以在父元素上使用针对文本的 text-align: center; 虽然可以把左右居中也一起做掉。不过还是用常规的 margin:auto 更精确一点。解决办法是对 :after 伪元素也设置 content:” “。

PS:因为 ie6 不支持伪元素,所以……要不还是用 table 吧。