面试的时候,绝不能只说一种,绝不能说一种解决方案,绝不能停下你吞吞吐吐的嘴

CSS 方面问的最多的问题之一,我想分三种情况,水平居中、垂直居中和水平垂直居中来分析

单单就水平垂直居中而言,大概有以下几种方案:

居中元素不定宽高

  • absolute + transform
  • flex 属性居中
  • flex + 子项 margin auto
  • grid 属性居中
  • grid + 子项 margin auto
  • grid + 子项属性居中
  • -webkit-box 属性居中
  • table-cell + text-align
  • line-height + text-align
  • writing-mode
  • table

仅居中元素定宽高适用:

  • 须知宽高 + absolute + 负 margin
  • 须知宽高 + absolute + calc
  • 须知宽高 + absolute + margin auto

局限性较大的全局居中

  • 须知宽高 + fixed + transform
  • 须知宽高 + fixed + 负 margin
  • 须知宽高 + fixed + calc
  • 须知宽高 + fixed + margin auto

水平居中

text-align: center

1
text-align: center;

需设置 display: inline-block 行内块元素

绝对定位 + transform 位移

1
2
3
position: absolute;
left: 50%;
transform: translateX(-50%);

脱离文档流

宽度+ margin: 0 auto

1
2
width: 100px;
margin: 0 auto;

这里说明下,width:100px 必须是具体的数字,且这个居中是外层居中,宽度中的内容没有居中

垂直居中

绝对定位 + transform 位移

1
2
3
position: absolute;
top: 50%;
transform: translateY(-50%);

与水平方向的居中一样,都是脱离文档流的做法

table-cell + vertical-align

1
2
display: table-cell;
vertical-align: middle;

display: table-cell ,让其标签元素以表格单元格的形式呈现,类似于 td 标签,

vertical-align: middle,用来指定行内元素(inline)或表格单元格(table-cell)元素的垂直居中

水平垂直居中

绝对居中 + transform 位移

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
7
8
9
.father {
position: relative;
}
.son {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}

flex 属性居中

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
.father {
display: flex;
justify-content: center;
align-items: center;
}

flex + margin auto

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
.father {
display: flex;
}
.son {
margin: auto;
}

grid 属性居中

1
2
3
4
5
6
7
<div class="father">123123</div>
// 或者
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
.father {
display: grid;
justify-content: center;
align-items: center;
}

grid 子项属性居中

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
7
.father {
display: grid;
}
.son {
align-self: center;
justify-self: center;
}

grid + margin auto

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
.father {
display: grid;
}
.son {
margin: auto;
}

grid 和 flex 很像,是 flex 的升级版,所以 grid 能做的事情更多

以上绝对定位、flex、grid 关于水平垂直居中的做法,剩下再说居中比较老的布局方法

-webkit-box 属性居中

这是一个已经过时的布局,可以看看这篇文章 CSS3 display: flex 和 display: box 有什么区别?

网友一丝说:

flex 是 2012 年的语法,是以后的标准

box 是 2009 年的语法,已经过时,需要加上对应前缀

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
.father {
display: -webkit-box;
-webkit-box-pack: center;
-webkit-box-align: center;
}

table-cell + text-align

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
7
8
.father {
display: table-cell;
vertical-align: middle;
text-align: center;
}
.son {
display: inline-block;
}

line-height + text-align

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
.father {
height: 200px;
line-height: 200px;
text-align: center;
}

line-heightheight ,行高和高度一样高,自然就垂直方向居中了

writing-mode

1
2
3
4
5
6
7
<div class="father">
<div class="“son”">
<div class="“sonson”">
123123
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.father {
writing-mode: tb-lr;
writing-mode: vertical-lr;
text-align: center;
}

.father .son {
writing-mode: lr-tb;
writing-mode: horizontal-tb;
text-align: center;
width: 100%;
display: inline-block;
}
.father9 .son .sonson {
display: inline-block;
text-align: initial;
}

这个很冷闷,有人介绍过这种情况

table

1
2
3
4
5
6
7
8
9
10
11
<table>
<tbody>
<tr>
<td class="father">
<div class="son">
123123
</div>
</td>
</tr>
</tbody>
</table>
1
2
3
.father {
text-align: center;
}

table 标签自己将它垂直居中了,text-align:center 后就是水平居中了

可以看 demo

元素有宽高的情况,又多了三种方案

须知宽高 + 绝对居中 + margin 负边距

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
.father {
position: relative;
height: 200px;
}
.son {
width: 100px;
height: 100px;
position: absolute;
top: 50%;
left: 50%;
margin: -50px 0 0 -50px;
}

父元素必须要有个高度,这样才能撑开容器。子元素必须要有个宽高,才能计算出 margin 值

须知宽高 + 绝对定位 + calc

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
.father {
position: relative;
height: 200px;
}

.son {
width: 100px;
height: 100px;
position: absolute;
top: calc(50% - 50px);
left: calc(50% - 50px);
}

与 margin 负边距一个道理,父元素需要设置一个高度。子元素必须要有高度,不用 margin,而用 CSS3 中的 calc,计算出要居中位移,兼容性需要支持 CSS3 属性

须知宽高 + 绝对居中 + margin: auto

1
2
3
4
5
<div class="father">
<div class="son">
123123
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.father {
position: relative;
height: 300px;
}

.son {
width: 100px;
height: 100px;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}

同以上两种情况。

这三种需要定位方式来实现水平垂直居中的方法,需要设置父元素的高度(一定要有,撑开画面),子元素需要设置宽高,前两种方法是为了算出它在父元素中的相对位置,后一种方法是为了说明子元素是个容器(如果不设置宽高,就是无)

其他方法

其实,水平垂直居中方面,如果面试官硬要问还有吗?还真的有,用 fixed 定位。但这个方法有缺陷,虽然能实现水平垂直居中,但它是相对于视口(viewport),而非父元素

方法就是以上用 absolute 实现的改成 fixed 即可

  • 须知宽高 + fixed + transform
  • 须知宽高 + fixed + 负 margin
  • 须知宽高 + fixed + calc
  • 须知宽高 + fixed + margin auto

这四种方法,都需要设置子元素的宽高

这里贴一下代码

1
2
3
4
5
<div class="father">
<div class="son">
123123
</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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* transform */
.son {
width: 100px;
height: 100px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: blue;
}

/* 负 margin */
.son {
width: 100px;
height: 100px;
position: fixed;
top: 50%;
left: 50%;
margin-left: -50px;
margin-top: -50px;
background: blue;
}

/* calc */
.son {
width: 100px;
height: 100px;
position: fixed;
top: calc(50% - 50px);
left: calc(50% - 50px);
background: blue;
}

/* margin: auto */
.son {
width: 100px;
height: 100px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
background: blue;
}

总结

随着微软宣布放弃 IE11,现实项目中完全可以使用 flex 布局,grid 部分还不适配,但是以后肯定会取代 flex。

虽然写了很多,但是自己工作中也不会使用 table 、writing-mode、-webkit-box 等过时的布局方式,写这篇文章,纯粹是为了面试时被问到这种问题。

收获是 absolute 的居中要父子同心(父元素设置高度,子元素设置宽高),fixed 的居中只需要设置子元素的宽高。

线上 demo 查看

参考资料