沐光

记录在前端之路的点点滴滴

水平垂直居中

前言

在面试中,面试官考察 CSS 方面的内容时,比较常见的一个问题就是水平垂直居中问题。其实不仅仅是面是,在工作中也常常会遇到这样的布局,因此对于一些常见的水平垂直居中的方法,在此做了一番总结,以作备忘。

块级元素的水平垂直居中定位

对于块级元素的垂直居中定位,我们处理的方法有很多,就以下公共模板做讨论:

1
2
3
<div class="parent parent-special">
<div class="child child-common"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.parent {
background-color: black;
position: relative;
height: 400px;
width: 400px;
}

.parent-special {}

.child {}

.child-common {
background-color: red;
}

position + margin

定位配合 margin 来实现的方法比较简单粗暴,由于子元素有定宽定高的限制,因此适用于一些简单的布局,比如弹框之类的(这里仅讨论绝对定位)。

1
2
3
4
5
6
7
8
.child {
height: 100px;
width: 100px;
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -50px;
}

position + transform

前面的方法对于子元素大小固定的情况可以适用,但是对于子元素大小不固定的情况却没法处理,好在 CSS3 的新属性 transform 能帮我们解决这个问题

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

注:虽然 transform 能解决问题,但是当 width 和 height 百分比计算后的值为非偶数时,通过 translate 补正会造成页面字体模糊以及抖动问题(抖动可用 3d 校正,模糊无解),因此这个得视情况使用。

table-cell

回顾比较”久远”的解决方法那就是传统的 table-cell 布局了,父级的 display 属性为 table,子元素设置为 table-cell,配合 vertical-align,就能实现垂直居中,不过此处的居中偏向于行内元素。

1
2
3
4
5
6
7
8
9
10
.parent-special {
display: table;
/* 为了表现得更鲜明一点 */
padding: 30px;
}

.child {
display: table-cell;
vertical-align: middle;
}

注: 这种情况比较适合于子元素内容呈现形式为为行内的情况,子元素此时会占满整个父元素。

margin: auto

我们知道 margin: auto 会自动补充左右两侧的内容,如果能够实现自动补充上下两端的内容那不就能够实现垂直居中了么!因此我们通过定位来“提示”自动补全。

1
2
3
4
5
6
7
8
9
10
.child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 30%;
width: 30%;
}

然而这种写法会让子元素撑满整个父级,适用于仅包含一个元素的情况;如果只想要垂直居中(子元素的上下 margin 会被填满),那么可以这么修改:

1
2
3
4
5
6
7
8
9
10
.child {
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
height: 30%;
width: 30%;
/* 取其一 */
left/right: 0;
}

flex

flex 属性就不做过多说明,这个很好理解,阮一峰的博客有很详细的实例,我就不赘述了(grid 可类比)。

1
2
3
4
5
6
7
8
9
10
.parent-special {
display: flex;
justify-content: center;
align-items: center;
}

.child {
height: 30%;
width: 30%;
}

行内元素的垂直居中定位

行内元素的垂直居中面试中不是特别常见(水平居中谁都会,此处 pass),但工作中还是遇到不少,先前的《浅谈行内元素》一文中也有简单介绍,此处以以下公共模板做讨论:

1
<span class="inline">行内元素内容</span>

line-height + font-size + vertical-align

此为在不改变行内元素属性的情况下,通用的居中方式

1
2
3
4
5
.inline {
font-size: 12px;
line-height: 20px;
vertical-align: middle;
}

注,当该行内元素有同级的行内元素位于其左右时, vertical-align 需统一一下,因为默认的值为 baseline

inline-block + 伪元素

另外比较“骚气”的处理方法就是改变盒子的 display 状态,然后给予一个定位的中心点(默认没有的),以此中心点来影响其余的匿名行盒子的定位。

1
2
3
4
5
6
7
8
9
10
11
12
.inline {
height: 100px;
display: inline-block;
vertical-align: middle;
}

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

注:此方法可行是可行,但是浏览器在渲染空节点时,仍然会占一个空格,也就是说,水平居中总会差那么一点点,如果不是特别挑剔那么就没什么大问题。

参考文章

附录

本篇为我的 segmentfault 中的文章的一部分,因为刚好温故 CSS 时又看到了布局这部分内容,因此再总结一次,记录下一些新的收获,同时修改一下当时学习记录的一些错误点。