一直都搞不定的“垂直居中”

“阿尔法狗”的下棋功力都已经能轻松完爆人类了,无人驾驶汽车都已经能上路了,无人机都开始送快递了,人类的星宇宙探测器都已经飞出太阳系了,人类自身的基因测序工作也完成了相当的程度了……而在CSS中我们竟然还不能用简单的方法去实现垂直居中!?——吐槽完毕!

炮姐-御坂美琴

多年来垂直居中已经成为了CSS领域的“圣杯”,同时也是一个超级大笑话。它具备很显著的特征:

  1. 垂直居中的需求,很常见!非常常见!!
  2. 从理论上来分析,似乎实现起来非常的简单。
  3. 在实践中,难如登天,尤其是涉及不固定尺寸元素的时候。

为了解决这一绝世难题,前端开发者们殚精竭虑琢磨出来了各种方法,有些不实用有些则很巧妙。不知道死了多少脑细胞才能得到一个似是而非的方案。干嘛搞出这么大动静呢?

这个问题,一直在困扰着我,我相信也在困扰着每一个前端。这是一个很普遍却也很棘手的问题,至今没有一个非常有效的解决方案。每次午夜梦回睡梦中惊起都是一身冷汗。后来也就是释然了,为什么一直没有一个能解决垂直居中的“一揽子”的解决方案——就像水平居中text-align:center;margin:auto;一样简单的方法呢?

这其实源于我们的阅读习惯。我们的阅读习惯是从左到右自上到下的阅读顺序。这样的阅读顺序也就决定了,我们阅读“空间”在X轴方向也就是水平方向是可固定的,而在Y轴方向也就是垂直方向是可无限延伸的。这样就决定了在垂直方向去决定元素是否居中也就丧失了意义。

而CSS最初的设计目的是排版,而现在的前端设计者都是在用CSS进行布局。阅读上的习惯加上使用和设计的偏差,使得垂直居中的问题一直不能很好的去实现。我想这个说明大概能解释一部分问题吧

罗列下常用的元素垂直居中方案,以供参考!

本文中的示例结构代码如下所示,不再做特别的说明!

<div class="warp">
  <div class="box"></div>
</div>

首先是这个是最容易的——已知容器尺寸且已知目标元素尺寸。容器尺寸已知且需垂直居中元素的尺寸已知,可以使用position的方法来解决。具体的操作就是:容器设置相对定位position:relative;目标元素设置据对定位position:absolute;然后又根据尺寸计算出需要定位的top&left or other

已知容器尺寸且已知目标元素尺寸
/*css*/
.warp{
  position:relative;
  width:500px;
  height:250px;
  border:thin solid #eee;
  background-color:#efefef;
}

.box{
  position:absolute;
  left:150px;
  top:75px;
  width:200px;
  height:100px;
  background-color:#ccc;
}
代码结果

我们再向前走一步。如果容器尺寸未知,目标元素尺寸已知的情况下,该怎么办呢?我们可以利用定位margin的方案来解决。具体实现思路就是,目标元素通过50%定位至容器中,然后通过margin设置负值使目标元素“移动”至垂直中间的位置。

未知容器尺寸和已知目标元素尺寸
.warp{
  position:relative;
  width:500px;
  height:250px;
  border:thin solid #eee;
  background-color:#efefef;
}

.box{
  position:absolute;
  left:50%;
  top:50%;
  margin-left:-100px;
  margin-top:-50px;
  width:200px;
  height:100px;
  background-color:#ccc;
}

以上是“早期”的或者说是“最常见”的垂直居中的方案。这些代码在本质上做到了几件事情,先把目标元素的左上角放到视口(或具有相对定位属性的容器元素)的正中心,然后在利用负外边距把它向上、向左移动(移动距离相当于自身宽高的一半),从而实现把元素放到视口(容器)的正中心,从而实现垂直居中。和实现思路是一致的,难道这样就行了么?

NO!NO!最为一名强迫症患者这样肯定是不行的,略微观察之后就会发现如果我们利用CSS3中强大的calc()函数,还能让我们的代码还可以再精简一些。

.box{
  position:absolute;
  left:calc(50% - 100px);
  top:calc(50% - 50px);
  width:200px;
  height:100px;
  background-color:#ccc;
}

代码的实现结果是不变的。很好,我们的方案可以经得起一定的考验了。不过上述两种方法的局限在于至少目标元素的尺寸是固定的,可是在通常的情况下,对于那些需要垂直居中的目标元素,其尺寸往往是由自身的内容来决定的。而且calc()函数在浏览器支持还是十分有限。如果找到一个平衡点。让目标元素可以基于自身百分比计算的属性为解析基准,这个问题就能迎刃而解。

在CSS中允许设置百分比值的属性大都是基于父级元素的尺寸为基准来进行计算的,关于百分比值属性的知识点归纳可以移步至我的另一篇文章CSS关于属性值是百分比的知识盘点。嘿嘿,有些问题的答案其实就来自我们很常见的地方。

translate()变形函数在使用百分比值的时候,就是基于元素自身的宽高为基准进行换算和移动的,这正是我们需要的。下面需要改造我们的代码,放弃calc()函数使用translate()变形函数,让我们彻底解除对固定尺寸的依赖

我们的结构需要稍微修改下,如果没有内容,translate()是不会有效果的。

<div class="warp">
  <div class="box">
    <h3>translate</h3>
    <p>百分比值基于自身计算的属性</p>
  </div>
</div>

而核心的CSS代码就是:

.box{
  position:absolute;
  top:50%;
  left:50%;
  transform :translate(-50%, -50%);
  background-color:#ccc;
}

当然了,我们要考虑兼容性。需要加一点点私有前缀:

.box{
  position:absolute;
  top:50%;
  left:50%;
  -webkit-transform :translate(-50%, -50%);
  -moz-transform :translate(-50%, -50%);
  -ms-transform :translate(-50%, -50%);
  -o-transform :translate(-50%, -50%);
  transform :translate(-50%, -50%);
  background-color:#ccc;
}

得到的结果


translate来实现垂直居中

现在,目标元素已经完美居中了。完全满足我们的需求。同时也让我们不在依赖宽高尺寸的限制。


任何技巧性的东西都不会十全十美,上述方法看似完美其实也存在一些“致命的瑕疵”:

  1. 据对定位对于布局的影响太强烈,脱离文档流的元素不好控制。
  2. 目标元素内容过多的时候,超过了容器的部分会被裁掉。
  3. 有些浏览器的渲染算法会使元素可能会被放置在半个像素上,导致显示效果模糊。

关于上述问题,有一些解决方法让我们绕过或是修复问题。这些很难保证它在未来不会出问题。对于强迫症的我来说万万不能忍受。看来还要继续探索一些方案,来让自己在不同的场景需求中有更多的应对能力。

装逼特效全开

好吧,为了避免文章过长就简单说一下:display: table-cell;方法利用表格的特性来使元素垂直居中。display:inline-block是元素获得文本的特性,然后通过伪元素来占位使元素垂直居中。

最风骚的莫过于使用CSS3中的display:-webkit-box。这种方法的硬伤是浏览器支持范围相对较窄,自身适应性较差。下面是CSS代码。

 display: -webkit-box;
 -webkit-box-pack:center;
 -webkit-box-align:center;
 -webkit-box-orient: vertical;
 text-align: center

对比下各种方案,个人觉得还是弹性布局——display: flex;能适应较多数的情况。在使用Flexbox的时候,margin:auto不仅在水平方向上将元素居中,垂直方向上也会居中。这种方法我们可以不指定任何宽度(需要的时候,还是可以设置的),相对更灵活一些。

.warp{
  display:flex;
  min-height:100vh;
  margin:0;
  background:#eee;
}

.box{
  margin:auto;
}

而且Flexbox的另一好处是可以将匿名容器(没有标签包裹的文本节点)垂直居中。假设我们有一个容器,内部只有文本没可以操控的目标元素;

<h3>这里面只有文本!</h3>

我们可以给这个元素指定一个尺寸,然后借助Flexbox中的align-items:center;justify-content:center;属性来实现内容的文本居中。

h3{
  display:flex;
  align-items:center;
  justify-content:center;
  width:20em;
  height:10em;
  background:#eee;
}

关于Flexbox的相关知识可以移步Flex布局语法

垂直居中的方案大致上就这么,每一个方案都不是尽善尽美。前面写的还可以,后面有点不像样子了。主要是有点累了。没吃饭,很饿!!!来让自己在不同的场景需求中有更多的应对能力才是核心对吧。多掌握了解点解决技巧还是有一定帮助的。

好了到这里吧,报告完毕!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容