【转载】解读CSS布局之-水平垂直居中
原文地址:http://f2e.souche.com/blog/jie-du-cssbu-ju-zhi-shui-ping-chui-zhi-ju-zhong/
CSS布局是可以分为几大块的:
- 盒子内部的布局
- 文本的布局
- 盒模型本身的布局
- 盒子之间的布局visual formatting
- 脱离正常流normal flow的盒子的布局
- absolute布局上下文下的布局
- float布局上下文下的布局
- 正常流normal flow下的盒子的布局
- BFC布局上下文下的布局
- IFC布局上下文下的布局
- FFC布局上下文下的布局
- table布局上下文下的布局
- css grid布局上下文下的布局
- 脱离正常流normal flow的盒子的布局
所有的CSS布局其实都是围绕着这些布局模块来的,水平垂直居中也一样。
一. 文本的水平垂直居中
line-height + text-align:center
.wrap{height: 400px;line-height: 400px;background: #ccc;text-align: center;}
<div class="wrap"> 水平垂直居中水平垂直居中 </div>
二. 利用盒模型的水平垂直居中
我们一般讲的盒模型都是说的块级盒的盒模型,也只有块级盒的盒模型用得多一点,块级盒block-level box又是分别由content-box、padding-box、border-box、margin-box组成的
也就说我任一个子盒子的水平和垂直方向的边与最外面盒子的间距都是可以控制的,因此也就有如下居中方法:
padding填充
.wrap{width: 400px;height: 400px;margin: 20px auto;background: #ccc;} .content{width: 100pz;height: 100px;padding: 150px;background: #333;background-clip: content-box;}
<div class="wrap"> <div class="content"></div> </div>
也可以用css3的calc()动态计算:
.wrap .content { padding: -webkit-calc((100% - 100px) / 2); padding: calc((100% - 100px) / 2); width: 100px; height: 100px; background-color: #333; background-clip: content-box; }
margin填充
.wrap{height: 400px;background: #ccc;overflow: hidden;} .ele{width: 100px;height: 100px;margin: 150px auto;background: #333;}
<div class="wrap"> <div class="ele"></div> </div>
三. absolute布局上下文下的水平垂直居中
50% + -50%
原理很简单,就是利用left:50%将盒子的左边先置于父容器的中点,然后再将盒子往左偏移盒子自身宽度的50%,这里有三种具体实现:
1.
<div class="wrap"> <div class="ele margin">水平垂直居中水平垂直</div> </div>
.wrap{height: 400px;background: #ccc;position: relative;} .ele{width: 100px;height: 100px;background: #333;position: absolute;top: 50%;left: 50%;} .margin{margin-left: -50px;margin-top: -50px;}
2.
.margin{transform: translate3d(-50%,-50%,0);}
3.
.wrap{height: 400px;background: #ccc;position: relative;} .ele{width: 100px;height: 100px;position: absolute;top: 50%;left: 50%;} .ele-inner{position: relative;top: -50%;left: -50%;background: #333;}
<div class="wrap"> <div class="ele"> <div class="ele-inner">水平垂直居中水平垂直<br>居中水平垂直居中水平<br>垂直居中水平垂直居<br>中水平垂直居中</div> </div> </div>
上面三个方法中,margin方法和relative方法都需要知道元素的宽高才行(relative方法只知道高也行),适用于固定式布局,而transform方法则可以不知道元素宽高
text-align:center + absolute
text-aign:center本来是不能直接作用于absolute元素的,但是没有给其left等值的行级absolute元素是会受文本的影响的,
.wrap{height: 400px;background: #ccc;text-align: center;} .ele{width: 100px;height: 100px;position: absolute;margin-top: 150px;margin-left: -50px;background: #333;display: inline-block;}
<div class="wrap"> <div class="ele"></div> </div>
简单解释下,首先,text-align:center作用的是文本而不是absolute的元素,但是,当absolute元素为inline-block的时候,它会受到文本的影响,然后你可能会问这里没文本啊,我只能告诉你说这下面是有的,是个匿名的文本节点。
然后这个匿名文本由于受到text-align:center影响居中了,这个时候absolute盒子的左边跟父容器的中点对齐了,所以就还需要往回拉50%,这里用的是margin-left,你也可以用其它方式拉。然后就是垂直方向的对齐,垂直方向是不能被操作文本的属性影响的,所以我这里用的是margin-top来让它偏移下去。
absolute + margin : auto
.wrap{height: 400px;background: #ccc;position: relative;} .ele{width: 100px;height: 100px;position: absolute;left: 0;right: 0;top: 0;bottom: 0;background: #333;margin: auto;}
<div class="wrap"> <div class="ele"></div> </div>
它的包含块的宽度等于它的盒模型的宽度 + left + right值,包含块的高度同理,盒模型包括margin-box、border-box、padding-box、content-box,而在这个居中方法中,.ele的left + right值是0,width是定值,width所在盒子包括了除了margin-box外的那三个box,margin都是auto值,按照上面那个公式,margin-left + margin-right的值应该等于包含块的宽度 - left的值 - right的值 - width的值,也就是说margin-left + margin-right的值等于除了width所占宽度外的剩下宽度,拥有剩下宽度后,就是平分其宽度,以让左右两边相等,达到居中,标准中给出了答案:
垂直方向也就因此也居中了。
这种方法能简单的做到居中,但是必须有width和height值
适用于图片居中的网易nec的一个方法
.wrap{height: 400px;background: #ccc;position: relative;} p{position: absolute;top: 50%;left: 50%;} .wrap img:nth-child(1){position: static;visibility: hidden;} .wrap img:nth-child(2){position: absolute;right: 50%;bottom: 50%;}
<div class="wrap"> <p> <img src="img/0.jpg" alt=""> <img src="img/0.jpg" alt=""> </p> </div>
这种方法主要是利用了一个图片进行占位,以让父容器获得高宽,从而让进行-50%偏移的图片能有一个参照容器作百分比计算。优点是可以不知道图片的大小,随便放张尺寸不超过父容器的图片上去都能做到居中。另外,兼容性好,如果是不使用nth-child选择器的花,IE6都是能顺利兼容的
四. float布局上下文下的水平垂直居中
float + -50%
.wrap{height: 400px;background: #ccc;float: left;width: 100%;} .ele{position: relative;top: 50%;left: 50%;float: left;} .ele-inner{position: relative;left: -50%;background: #333;transform: translate3d(0, -50%, 0);}
<div class="wrap"> <div class="ele"> <div class="ele-inner">居中居中居中居中居中居中</div> </div> </div>
这种方法的原理,首先是利用float属性将需要居中的元素的父元素.ele的宽度收缩,然后left:50%将.ele的左边和水平中线对齐,这个时候还没居中,还需要将其往回拉自身宽度的50%,于是.ele-inner便是真正需要水平居中的元素,我给它一个position:relative,将其往回拉自身宽度50%就行了。对于垂直方向,依然是先将.ele top:50%到垂直方向中点,但是这时给.ele-inner top:50%是不起作用的,因为如果没给父元素明确高度的话,这个50%是计算不出来的,因此,就有了transform : translate3d(0, -50%, 0)。
这种方法的好处是元素可以不定宽,任何时候都可以做到居中
我当时在w3cplus的站上发现这个方法后,当时觉得这个方法很好,兼容性好,又还可以不定宽,但当我用了一段时间后,发现了一个问题:
就是当居中元素的父元素left:50%时,如果元素宽度足够大,会超出外面的容器,而如果外面的容器又正好是overflow:auto的话,那就会在外面产生滚动条
五.BFC布局上下文下的水平垂直居中
BFC的全称是块级排版上下文,BFC布局上下文下的布局其实就是利用盒模型本身进行的布局,前面在利用盒模型布局的那一节中已经讲过了,这里就不重复了
六.IFC布局上下文下的水平垂直居中
text-align:center + vertical-align:middle
.wrap{height: 400px;background: #ccc;width: 100%;text-align: center;} .ele,.placeholder{vertical-align: middle;display: inline-block;} .placeholder{width: 0;overflow: hidden;height: inherit;} .ele{width: 100px;height: 100px;background: #333;}
<div class="wrap"> <div class='placeholder'><!--占位元素,用来作为居中元素的参照物--></div> <div class="ele"></div> </div>
text-align:center + line-height
<div class="wrap"> <div class="ele">居中居中居中居中居中居中</div> </div>
.wrap{height: 400px;background: #ccc;width: 100%;text-align: center;line-height: 400px;} .ele{width: 100px;height: 100px;background: #333;line-height: normal;vertical-align: middle;display: inline-block;}
这个方法,首先是水平方向,text-align:center就行了,至于垂直方向,起作用的就是父容器的一个line-height和居中元素的vertical-align:middle,为什么这两个属性可以让一个inline-block垂直居中呢,这里重点是父容器在其下面产生了一个隐匿的文本节点,这个我在上面 text-align:center + absolute 那个方法的讲解中说到过了,然后这个这个隐匿文本节点会因line-height属性的作用而拥有了一个父容器一样高的行高,此时元素有了一个vertical-align对齐的参照物,再给其vertical-align:middle值就能垂直对齐了。
使用这个方法,居中元素无需定宽高,但缺点是得给父容器一个固定的行高才行。
text-align:center + font-size
.wrap{height: 400px;background: #ccc;width: 400px;margin: 0 auto;font-size: 349.2px;text-align: center;} .ele{width: 100px;height: 100px;background: #333;vertical-align: middle;display: inline-block;}
<div class="wrap"> <div class="ele"></div> </div>
这个方法来自淘宝,基本原理还是让隐匿文本节点所占据的行高等于父容器的高度,然后给居中元素一个vertical-align:middle对齐的一个参照。只是这里把定义line-height值换成了定义font-size值,让font-siz足够大从而让其行高等于父容器高度。为了证明这个font-size的作用,我把居中元素换成文本
.wrap{height: 400px;background: #ccc;width: 400px;margin-left:auto;margin-right:auto;font-size: 349.2px;text-align: center;}
<div class="wrap"> a </div>
可以看到字母a垂直居中了,这个字母a就对应那个隐匿文本节点
七.FFC布局上下文下的水平垂直居中
父元素、子元素都定义flex:
.wrap{height: 400px;background: #ccc;width: 400px;display: flex;align-items: center;justify-content: center;} .ele{background: #333;}
<div class="wrap"> <div class="ele"> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中 </div> </div>
只有父元素定义flex,子元素定义margin:auto:
.wrap{height: 400px;background: #ccc;width: 400px;display: flex;} .ele{background: #333;margin: auto}
<div class="wrap"> <div class="ele"> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中<br> 居中居中居中居中居中居中居中 </div> </div>
意思就是说flex item的margin不会折叠,在flex-item有明确大小并且margin:auto时外边距吸收了伸缩包含块下的额外的空间,并且能被用于居中以及会让其相邻的flex item尽可能的往这个flex item所在的那一个方向靠。
flexbox是个很强大的布局模块,也就三个属性就搞定居中了,而且不论父容器还是居中元素都可以不定宽高
八.table布局上下文下的水平垂直居中
.wrap{height: 400px;background: #ccc;width: 400px;display: table;} .ele{text-align: center;vertical-align: middle;display: table-cell;} .ele-inner{display: inline-block;background: #333;}
<div class='wrap'> <div class='ele'> <div class="ele-inner">居中居中居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中居中居中</div> </div> </div>
九.CSS grid布局上下文下的水平垂直居中
CSS3 grid layout是IE搞出来的一个布局模块,目前貌似还只有IE0和IE11支持,我没有研究过其居中的方法,有兴趣的可以看看大漠老师的介绍文章
十.其它未知归属的水平垂直居中方法
使用button标签
button{height: 400px;background: #ccc;width: 400px;border-width: 0;} div{display: inline-block;font-size: 18px;background: #333;color: #fff;}
<button> <div> 居中居中居中居中居中居中<br> 居中居中居中居中居中居中<br> 居中居中居中居中居中居中<br> 居中居中居中居中居中居中<br> </div> </button>