《css揭秘》笔记:居中方法探讨

在css中对元素进行水平居中是非常简单的:如果它是一个行内元素,就对它的父元素应用text-align:center;如果是一个块级元素,就对自身应用margin:auto;
而如果要对一个元素垂直居中,可能光是想想就令人头皮发麻了。

在本篇中,我们将探索现代css的强大威力,以全新的思路去攻克各种场景下的垂直居中难题。

我们将使用如下所示的结构代码,并直接插入<body>元素中(实际上我们探讨的技巧与容器无关)。

1
2
3
4
<main>
<h1>Am I centered yet?</h1>
<p>Center me,please!</p>
</main>

我们将设置一些基本样式,如下所示:

1
2
3
4
5
6
7
8
9
10
11
html{font-size: 100%;}
body{background: #F0F0F0;font-size: 1em;}
main{
width: 30em;
height: 6em;
background: red;
color: #fff;
text-align: center;
margin: 0 auto;
line-height: 1.75;
}

得到这样的一个效果,[点此预览demo]


1.基于绝对定位的解决方案

我们先来看一个早期的垂直居中方法,它要求元素具有固定的宽度和高度。

1
2
3
4
5
6
7
8
9
main{
position: absolute;
top:50%;
left:50%;
margin-top:-3em; /* 6/2 = 3 */
margin-left:-15em /* 30/2=15 */
width: 30em;
height: 6em;
}

这段代码在本质上做了这样几件事:先把这个元素的左上角放置在视口(或最近的,具有定位属性的祖先元素)的正中心,
然后再利用负外边距把它向左、向上移动(移动的距离相当于它自身宽高的一半),从而把元素的正中心放置在视口的正中心。
显然,这个方法最大的局限在于它要求元素的宽高是固定的。


2.利用css变形技巧
当我们在translate()变形函数中使用百分比值时,是以这个元素自身的宽度和高度为基准进行换算和移动,运用这一点,我们可以彻底解除对固定尺寸的依赖。

1
2
3
4
5
6
main{
position: absolute;
top:50%;
left:50%;
transform: translate(-50%,-50%);
}

利用这个技巧,可以让宽高不固定的元素垂直居中。
当然,上面这个方法也有一些需要注意的地方。
-我们有时不能选用绝对定位,因为它对整个布局的影响太过强烈。
-如果需要居中的元素已经在高度超过视口,那它的顶部会被视口切掉,有一些办法可以绕过这个问题,但是hack味道过重。
-在某些浏览器中,显示有些模糊,因为可能被放置在半个像素上,可以用transform-style:preserve-3d来修复,不过这个修复手段也可以认为是一个hack,难保在未来不会出问题。


3.基于视口单位的解决方案
假设我们不想使用绝对定位,仍然可以采用translate()技巧来把这个元素以其自身宽高的一半为距离进行移动;但在缺少left和top的情况下,如何把这个元素的左上角放置在容器的正中心呢?
第一反应是用margin属性的百分比值来实现,就像这样:

1
2
3
4
main{
margin:50% auto 0;
transform: translateY(-50%);
}

不过这段代码会产生十分离谱的结果,点击demo
原因在于margin的百分比值是以父元素的宽度作为解析基准的。
我们期望margin的百分比值可以基于视口的尺寸来解析,但实际是行不通的。
不过幸运的是,如果只是想把元素相对于视口进行居中,仍然是有希望的。css值与单位定义了一套新的单位,称为视口相关的长度单位。

-vw是与视口宽度相关的,与常人的直觉不符的是,1vw实际上表示视口宽度的1%,
-与vw相似,1vh表示视口高度的1%
-当视口宽度小于高度时,1vmin等于1vw,否则等于1vh
-当视口宽度大于高度时,1vmax等于1vw,否则等于1vh

所以我们可以这么做:

1
2
3
4
main{
margin:50vh auto 0;
transform: translateY(-50%);
}

当然这个技巧的实用性是相当有限的,只适用于在视口中居中的场景。


4.基于flexbox的解决方案

这毋庸置疑是最佳解决方案,我们探讨其他方案,仅仅是因为那些方案在浏览器的支持程度上稍微好一些,其实目前现代浏览器对flexbox的支持度已经相当不错了。
我们只需写两行声明即可:
先给这个待居中的父元素设置display:flex;(这个例子中是元素)
再给这个元素自身设置margin:auto(这个例子中是

元素)

1
2
3
4
5
6
7
body{
display:flex;
min-height: 100vh;
}
main{
margin:auto;
}

当我们使用flexbox时,margin:auto不仅在水平方向上将元素居中,垂直方向上也是如此。
还有一点,我们不需要设置任何宽度,(当然,如果需要,也可以指定。):这个居中元素分配到的宽度等于max-content。
如果浏览器不支持flexbox,页面就和起点图一样,虽然没有垂直居中,但完全可以接受。

flexbox的另一个好处在于,它可以将匿名容器(即没有被标签包裹的文本节点)垂直居中。例如:
html代码

1
<main>Center me,please!</main>

我们可以借助flexbox规范引入的align-items和justify-content属性,让它内部的文本也实现居中。
1
2
3
4
5
main{
display:flex;
align-items: center;
justify-content: center;
}

点击查看demo


使用table
该方法我们不再探讨

使用table-cell居中
上面说得table在现在来说有点过时,但我们仍然可以把一个div元素通过display:table;把它模拟成一个表格。

html代码

1
2
3
<div class="table-box">
<div class="table-cell">Center me,please!</div>
</div>

css样式

1
2
3
4
5
6
7
8
9
.table-box{
display: table;
width: 100%;
}
.table-cell{
display: table-cell;
text-align: center;
vertical-align: middle;
}

点击查看demo

关于未来
根据盒对齐模型(第三版)的计划,在未来,对于简单的垂直居中需要,我们完全不需要动用特殊的布局模式了,因为只需要下面这行代码就可以搞定:

1
align-self:center;


参考链接:
原文来自《css揭秘》这本书。
table布局:Centering in the Unknown