位小伙伴在写前端代码的时候,经常会遇到元素垂直居中的情况,虽然css垂直居中是一个很简单的问题,但细细研究下来却发现原来一个垂直居中并不简单。
先从一个最简单的场景说一下吧。
如上图所示,在一个固定的div框内有一行文字,我们需要把文字水平垂直居中,我相信各位小伙伴都可以解决这个问题,就是给文字一个行高,以及text-align。
但有一天我们发现红框的文字变成多行的时候,问题就来了
这时候我们就发现我们就发现文字竟然跑到外面去了,这时候我们就发现刚才方法的弊端就是只适用于单行文字垂直居中。那我们怎么解决多行文字垂直居中的问题呢?我们先来一个笨办法试试效果。现在文字有两行,我们要求文字的行高是30px。那两行的文字的高度就是60px。外面红框的高度是100px。如果让文字垂直居中的话需要给span一个( 100 - 60 ) / 2=30px的padding-top。好废话不多说,上代码
可运行结果确没有按照我们想象中的进行,文字并没有垂直居中,这是为什么呢?这是因为span默认是行内元素,行内元素给padding-top是不起作用的,这时候我们需要做的就是改变span的display,让其变成块元素。
这时候我们发现文字已经垂直居中啦。但这个padding-top需要手动计算出结果,可不可以让css自动计算出padding-top的值呢(这里问什么用padding-top而不用margin-top呢?其实这里有个很尖锐的问题,先挖个坑,留着以后说明)?答案是可以的,css3中新增的计算属性calc()就可以帮我们解决这个问题。
需要注意的一点就是calc中的运算符号前后需要各加一个空格
这个时候我们发现即使用calc计算也存在很多问题,这种方法“太笨”了,比如现在文字变成了三行,行距变成了20px。这时候我们需要重新计算padding-top的值。由此我们可以得出这种方法的适用于文字基本固定不变的场景。如何解决动态内容的垂直居中呢?我们先把span的display:block去掉,然后加另外一个属性vertical-align。这个属性是用来处理元素垂直对齐方式的
运行结果确跟我们想象的并不一样
文字没有垂直居中。vertical-align这个属性脾气很怪异,时而有用,时而无用,有很多时候着实让人摸不着头脑,关于此属性的解析我会在后续的文章中做详细的讲解,在这就不过多的阐述。我们都知道html中的单元格是有水平居中与垂直居中的属性的,那我们可不可以把span变成具有单元格属性的元素呢?答案当然是肯定的。
文字并没有垂直居中,其实这是因为需要单元格无法根据父元素的高度在确定自己的高度决定的,决绝方法就是给一个高度即可,
完美。不管文字有多少个,多少行,文字始终会垂直居中了。这种方法是很值得推荐的,因为兼容性好,甚至可以兼容ie6。前提是文字总内容不要超出外边框
文字可以通过js或者后端程序进行截取。
用vertical-align: middle配合display: table-cell;让元素居中的在很多时候是很脆弱的,比如一旦使用的float或者绝对定位固定定位等就会失效。
上面讨论的方法都是限制知道外部元素具体的高度情况下的垂直居中,一旦外部高度改变,那我们就需要修改span相对应的参数,很不方便,也不灵活。那外部容器高度不确定的情况下如何做到垂直居中呢?
我们可以利用最经典的方式positon+margin
运行结果:
完美,绿色的方块已垂直居中。这时候我们修改外部元素的高度绿色的块也会居中。
内部元素用到top与margin-top两个属性来控制元素垂直居中,虽然margin-top可以用calc()来计算,但还是需要在写一次内部元素的高度,那有什么办法解决这一问题呢?其实很简单,
给绝地定位的元素一个初始left,top,bottom, right。那元素的margin:auto就会起作用。
如果这时候让文字也在绿色框中垂直居中呢?我们想到了很经典的vertical-align: middle配合display: table-cell。但这里用了绝对定位,用vertical-align: middle不会起作用。那我们解决这问题呢?
很简单我们在span里面在嵌套一层。
运行结果:
完美。垂直中的垂直。
那问题又来了,如果外部元素的高度位置,内部元素的高度未知,如何做到内部元素相对于外部元素垂直居中呢?
解决这个问题的方法有很多种,大部分是用css3去处理的。当然css2也是可以完成的。我再这里写几种比较常见的
这里用文字垂直居中举例说明,为什么用文字呢?因为文字的高度是不固定的,文字的多少会影响元素的高度,所有用文字垂直居中来演示再好不过了。
第一种是用::before伪元素配合vertical-align
这里需要注意的就是display: inline-block元素之间的间距问题。
由于display: inline-block间距的问题导致文字达到一定宽度的时候会整体移出外部元素,解决办法是吧外部元素的字体大小设置为0,然后再在内部元素内加一个字体大小即可。
是不是很完美。但这时候对有强迫症的我来说发现一个问题,就是文字左右边距竟然不一样。
这个问题的解决方法也很简单,来一个text-align: justify;文字两端对齐
第二种方法就是定位配合位移。
前文提到用posrtion配合top以及margin-top实现垂直居中,前提是需要知道居中元素的高度,如果用css3的transform: translate()属性的话就无需知道内部元素的高度啦
是不是感觉css3真的很神奇。
第三种方法就是利用flex
是不是很简单粗暴,内容部元素都不需要写任何样式。果然还是css3好用啊
第四种方法就是利用Grid + template
这个用的css3的列,使用不是很多,有兴趣的小伙伴可以自行了解一下。
关于flex跟Grid让元素垂直居中可以裂变出很多种方法,在这里我就不一一列举了,以后介绍flex或者grid的时候会详细介绍,再次不在赘述。
者|颜海镜
编辑|覃云
出处丨前端之巅
本文已获作者授权,转载来源:
https://segmentfault.com/a/1190000016389031
划重点,这是一道面试必考题,很多面试官都喜欢问这个问题,我就被问过好几次了。
要实现上图的效果看似很简单,实则暗藏玄机,本文总结了一下 CSS 实现水平垂直居中的方式大概有下面这些,本文将逐一介绍一下,我将本文整理成了一个 github 仓库在:https://github.com/yanhaijing/vertical-center
欢迎大家 star。
仅居中元素定宽高适用:
居中元素不定宽高:
为了实现上面的效果先来做些准备工作,假设 HTML 代码如下,总共两个元素,父元素和子元素:
<div class="wp"> <div class="box size">123123</div> </div>
wp 是父元素的类名,box 是子元素的类名,因为有定宽和不定宽的区别,size 用来表示指定宽度,下面是所有效果都要用到的公共代码,主要是设置颜色和宽高。
注意:后面不在重复这段公共代码,只会给出相应提示。
/* 公共代码 */ .wp { border: 1px solid red; width: 300px; height: 300px; } .box { background: green; } .box.size{ width: 100px; height: 100px; } /* 公共代码 */
绝对定位的百分比是相对于父元素的宽高,通过这个特性可以让子元素的居中显示,但绝对定位是基于子元素的左上角,期望的效果是子元素的中心居中显示。
为了修正这个问题,可以借助外边距的负值,负的外边距可以让元素向相反方向定位,通过指定子元素的外边距为子元素宽度一半的负值,就可以让子元素居中了,css 代码如下。
/* 此处引用上面的公共代码 */ /* 此处引用上面的公共代码 */ /* 定位代码 */ .wp { position: relative; } .box { position: absolute;; top: 50%; left: 50%; margin-left: -50px; margin-top: -50px; }
这是我比较常用的方式,这种方式比较好理解,兼容性也很好,缺点是需要知道子元素的宽高。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute1.html
这种方式也要求居中元素的宽高必须固定,HTML 代码如下:
<div class="wp"> <div class="box size">123123</div> </div>
这种方式通过设置各个方向的距离都是 0,此时再讲 margin 设为 auto,就可以在各个方向上居中了。
/* 此处引用上面的公共代码 */ /* 此处引用上面的公共代码 */ /* 定位代码 */ .wp { position: relative; } .box { position: absolute;; top: 0; left: 0; right: 0; bottom: 0; margin: auto; }
这种方法兼容性也很好,缺点是需要知道子元素的宽高。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute2.html
这种方式也要求居中元素的宽高必须固定,所以我们为 box 增加 size 类,HTML 代码如下:
<div class="wp"> <div class="box size">123123</div> </div>
感谢 css3 带来了计算属性,既然 top 的百分比是基于元素的左上角,那么在减去宽度的一半就好了,代码如下
/* 此处引用上面的公共代码 */ /* 此处引用上面的公共代码 */ /* 定位代码 */ .wp { position: relative; } .box { position: absolute;; top: calc(50% - 50px); left: calc(50% - 50px); }
这种方法兼容性依赖 calc 的兼容性,缺点是需要知道子元素的宽高。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute3.html
还是绝对定位,但这个方法不需要子元素固定宽高,所以不再需要 size 类了,HTML 代码如下:
<div class="wp"> <div class="box">123123</div> </div>
修复绝对定位的问题,还可以使用 css3 新增的 transform,transform 的 translate 属性也可以设置百分比,其是相对于自身的宽和高,所以可以讲 translate 设置为 -50%,就可以做到居中了,代码如下:
/* 此处引用上面的公共代码 */ /* 此处引用上面的公共代码 */ /* 定位代码 */ .wp { position: relative; } .box { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
这种方法兼容性依赖 translate2d 的兼容性。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/absolute4.html
利用行内元素居中属性也可以做到水平垂直居中,HTML 代码如下:
<div class="wp"> <div class="box">123123</div> </div>
把 box 设置为行内元素,通过 text-align 就可以做到水平居中,但很多同学可能不知道通过通过 vertical-align 也可以在垂直方向做到居中,代码如下:
/* 此处引用上面的公共代码 */ /* 此处引用上面的公共代码 */ /* 定位代码 */ .wp { line-height: 300px; text-align: center; font-size: 0px; } .box { font-size: 16px; display: inline-block; vertical-align: middle; line-height: initial; text-align: left; /* 修正文字 */ }
这种方法需要在子元素中将文字显示重置为想要的效果。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/lineheight.html
很多同学一定和我一样不知道 writing-mode 属性,感谢 @张鑫旭老师的反馈,简单来说 writing-mode 可以改变文字的显示方向,比如可以通过 writing-mode 让文字的显示变为垂直方向。
<div class="div1">水平方向</div> <div class="div2">垂直方向</div> .div2 { writing-mode: vertical-lr; }
显示效果如下:
水平方向 垂 直 方 向
更神奇的是所有水平方向上的 css 属性,都会变为垂直方向上的属性,比如 text-align,通过 writing-mode 和 text-align 就可以做到水平和垂直方向的居中了,只不过要稍微麻烦一点:
<div class="wp"> <div class="wp-inner"> <div class="box">123123</div> </div> </div> /* 此处引用上面的公共代码 */ /* 此处引用上面的公共代码 */ /* 定位代码 */ .wp { writing-mode: vertical-lr; text-align: center; } .wp-inner { writing-mode: horizontal-tb; display: inline-block; text-align: center; width: 100%; } .box { display: inline-block; margin: auto; text-align: left; }
这种方法实现起来和理解起来都稍微有些复杂。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/writing-mode.html
曾经 table 被用来做页面布局,现在没人这么做了,但 table 也能够实现水平垂直居中,但是会增加很多冗余代码:
<table> <tbody> <tr> <td class="wp"> <div class="box">123123</div> </td> </tr> </tbody> </table>
tabel 单元格中的内容天然就是垂直居中的,只要添加一个水平居中属性就好了。
.wp { text-align: center; } .box { display: inline-block; }
这种方法就是代码太冗余,而且也不是 table 的正确用法。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/table.html
css 新增的 table 属性,可以让我们把普通元素,变为 table 元素的现实效果,通过这个特性也可以实现水平垂直居中。
<div class="wp"> <div class="box">123123</div> </div>
下面通过 css 属性,可以让 div 显示的和 table 一样:
.wp { display: table-cell; text-align: center; vertical-align: middle; } .box { display: inline-block; }
这种方法和 table 一样的原理,但却没有那么多冗余代码,兼容性也还不错。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/css-table.html
flex 作为现代的布局方案,颠覆了过去的经验,只需几行代码就可以优雅的做到水平垂直居中。
<div class="wp"> <div class="box">123123</div> </div> .wp { display: flex; justify-content: center; align-items: center; }
目前在移动端已经完全可以使用 flex 了,PC 端需要看自己业务的兼容性情况。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/flex.html
感谢 @一丝姐 反馈的这个方案,css 新出的网格布局,由于兼容性不太好,一直没太关注,通过 grid 也可以实现水平垂直居中。
<div class="wp"> <div class="box">123123</div> </div> .wp { display: grid; } .box { align-self: center; justify-self: center; }
代码量也很少,但兼容性不如 flex,不推荐使用。
点击查看完整 DEMO:
http://yanhaijing.com/vertical-center/grid.html
下面对比下各个方式的优缺点,肯定又双叒叕该有同学说回字的写法了,简单总结下:
小贴士:关于 flex 的兼容性决方案,请看这里:
https://yanhaijing.com/css/2016/08/21/flex-practice-on-mobile/
最近发现很多同学都对 css 不够重视,这其实是不正确的,比如下面的这么简单的问题都有那么多同学不会,我也是很无语:
<div class="red blue">123</div> <div class="blue red">123</div> .red { color: red } .blue { color: blue }
问两个 div 的颜色分别是什么,竟然只有 40% 的同学能够答对,这 40% 中还有很多同学不知道为什么,希望这些同学好好补习下 CSS 基础。
于前端程序员,你掌握了几种垂直水平居中的方法,现在带你看一看,喜欢的朋友记得点赞加关注,我会定时更新程序员的世界。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<style>
#container {
margin: 10px;
border: 1px solid red;
height: 100vh;
display: -webkit-flex;
display: flex;
/* 关键属性 */
align-items: center;
/* 垂直居中 */
justify-content: center
/* 水平居中 */
}
.item {
width: 200px;
height: 200px;
border: 1px solid lightpink;
margin: 0 auto;
}
img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="container">
<div class="item">
<img src="img/avter.jpg" />
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<style>
#container {
border: 1px solid red;
height: 100vh;
position: relative;
}
.item {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 200px;
height: 200px;
margin: auto;
border: 1px solid lightpink;
}
img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="container">
<div class="item">
<img src="img/avter.jpg" />
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<style>
#container {
border: 1px solid red;
height: 100vh;
position: relative;
}
.item {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border: 1px solid lightpink;
}
img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="container">
<div class="item">
<img src="img/avter.jpg" />
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<style>
#container {
border: 1px solid red;
height: 100vh;
position: relative;
}
.item {
position: absolute;
left: 50%;
top: 50%;
width: 200px;
height: 200px;
margin-top: -100px;
margin-left: -100px;
border: 1px solid lightpink;
}
img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="container">
<div class="item">
<img src="img/avter.jpg" />
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<style>
#container {
border: 1px solid red;
height: 100vh;
position: relative;
text-align: center;
}
.item {
position: absolute;
border: 1px solid green;
width: 200px;
height: 200px;
left: calc(50% - 100px);
top: calc(50% - 100px);
}
img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="container">
<div class="item">
<img src="img/avter.jpg" />
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
</head>
<style>
#container {
border: 1px solid red;
height: 100vh;
text-align: center;
line-height: 100vh;
}
.item {
display: inline-block;
line-height: 1.5;
border: 1px solid green;
vertical-align: middle;
}
img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="container">
<div class="item">
<img src="img/avter.jpg" />
</div>
</div>
</body>
</html>
*请认真填写需求信息,我们会在24小时内与您取得联系。