CSS布局之-水平垂直居中
對一個元素水平垂直居中,在我們的工作中是會經常遇到的,也是CSS布局中很重要的一部分,本文就來講講CSS水平垂直居中的一些方法。
另外,文中的css都是用less書寫的,如果看不懂less,可以把我給的demo鏈接打開,然后在控制臺中查看最終的css,或者是點擊codepen上的“View Compiled”按鈕,可以查看編譯后的css
先看一張圖,這是去年cssConf大會時阿里的 @寒冬winter 老師放出來的:
如圖所示,CSS布局是可以分為幾大塊的:
- 盒子內部的布局
- 文本的布局
- 盒模型本身的布局
- 盒子之間的布局visual formatting
- 脫離正常流normal flow的盒子的布局
- absolute布局上下文下的布局
- float布局上下文下的布局
- 正常流normal flow下的盒子的布局
- BFC布局上下文下的布局
- IFC布局上下文下的布局
- FFC布局上下文下的布局
- table布局上下文下的布局
- css grid布局上下文下的布局
- 脫離正常流normal flow的盒子的布局
所有的CSS布局其實都是圍繞著這些布局模塊來的,水平垂直居中也一樣。
一. 文本的水平垂直居中
line-height + text-align:center
代碼:
<div class='wrap'>
水平垂直居中水平垂直居中
</div>
html,body{
margin: 0;
}
.wrap{
line-height: 400px;
text-align:center;
height: 400px;
font-size: 36px;
background-color: #ccc;
}
這種方法只適合單行文字的水平垂直居中
二. 利用盒模型的水平垂直居中
我們一般講的盒模型都是說的塊級盒的盒模型,也只有塊級盒的盒模型用得多一點,塊級盒block-level box又是分別由content-box、padding-box、border-box、margin-box組成的,如下圖:
也就說我任一個子盒子的水平和垂直方向的邊與最外面盒子的間距都是可以控制的,因此也就有如下居中方法:
padding填充
代碼:
<div class="wrap">
<div class="content"></div>
</div>
@wrapWidth : 400px;
.wrap{
margin-left: auto;
margin-right: auto;
margin-top: 20px;
width: @wrapWidth;
height: @wrapWidth;
background-color: #ccc;
}
.content{
@contentWidth : 100px;
width: @contentWidth;
height: @contentWidth;
padding: (@wrapWidth - @contentWidth) / 2;
background-color: #333;
background-clip:content-box;
}
也可以用css3的calc()動態計算:
<div class="wrap">
<div class="content"></div>
</div>
.wrap{
margin-top: 20px;
margin-left: auto;
margin-right: auto;
width: 400px;
height: 400px;
background-color: #ccc;
.content{
padding: -webkit-calc(~"(100% - 100px) / 2");
padding: calc(~"(100% - 100px) / 2");
width: 100px;
height: 100px;
background-color: #333;
background-clip: content-box;
}
}
注意這里我在calc中使用了一個~””的寫法,這是less中的一個語法,告訴less這里不被less所編譯,要是被less編譯了的話,css的calc函數的參數就不是100% - 100px,而是0%了。
margin填充
代碼:
<div class="wrap">
<div class="ele"></div>
</div>
.wrap{
@wrapHeight : 400px;
@contenHeight : 100px;
overflow: hidden;
width: 100%;
height: @wrapHeight;
background-color: #ccc;
.ele{
margin-left: auto;
margin-right: auto;
margin-top: (@wrapHeight - @contenHeight) / 2;
width: 100px;
height: @contenHeight;
background-color: #333;
color: #fff;
}
}
使用margin填充我們需要知道元素的寬度,這點不太靈活,不過CSS3搞出了一個加fit-content的屬性值,可以動態計算元素的寬度
使用盒模型進行布局不會產生reflow,兼容也好,使用盒模型布局是一種布局思想,其實僅僅靠它就能實現很多visual formatting才能實現的布局,這是另一個話題,這里不展開。
三. absolute布局上下文下的水平垂直居中
50% + -50%
原理很簡單,就是利用left:50%將盒子的左邊先置于父容器的中點,然后再將盒子往左偏移盒子自身寬度的50%,這里有三種具體實現:
<div class="wrap">
<div class="ele margin">水平垂直居中水平垂直<br>居中水平垂直居中水平<br>垂直居中水平垂直居<br>中水平垂直居中</div>
</div>
<div class="wrap">
<div class="ele translate">水平垂直居中水平垂直<br>居中水平垂直居中水平<br>垂直居中水平垂直居<br>中水平垂直居中</div>
</div>
<div class="wrap">
<div class="ele relative">
<div class="ele-inner">水平垂直居中水平垂直<br>居中水平垂直居中水平<br>垂直居中水平垂直居<br>中水平垂直居中</div>
</div>
</div>
.wrap{
position: relative;
width: 100%;
height: 200px;
border:1px solid;
background-color: #ccc;
.ele{
position: absolute;
left: 50%;
top: 50%;
background-color: #333;
&.margin{
width: 160px;
height: 100px;
margin-left: -80px;
margin-top: -50px;
}
&.translate{
-webkit-transform:translate3d(-50%, -50%, 0);
transform:translate3d(-50%, -50%, 0);
}
.ele-inner{
position: relative;
left: -50%;
top: -50%;
width: 100%;
height: 100%;
background-color: #333;
}
&.relative{
width: 150px;
height: 100px;
background-color: transparent;
}
}
}
上面三個方法中,margin方法和relative方法都需要知道元素的寬高才行(relative方法只知道高也行),適用于固定式布局,而transform方法則可以不知道元素寬高
text-align:center + absolute
text-aign:center本來是不能直接作用于absolute元素的,但是沒有給其left等值的行級absolute元素是會受文本的影響的,可以參考張老師的 這篇文章
代碼:
<div class="wrap">
<div class="ele"></div>
</div>
.wrap{
text-align: center;
width: 100%;
height: 400px;
background-color: #ccc;
font-size: 0;
}
.ele{
position: absolute;
margin-left: -(100px / 2);
margin-top: (400px - 100px) / 2;
width: 100px;
height: 100px;
display: inline-block;
background-color: #333;
}
簡單解釋下,首先,text-align:center作用的是文本而不是absolute的元素,但是,當absolute元素為inline-block的時候,它會受到文本的影響,然后你可能會問這里沒文本啊,我只能告訴你說這下面是有的,是個匿名的文本節點。
If the inline box contains no glyphs at all, it is considered to contain a strut (an invisible glyph of zero width) with the A and D of the element’s first available font
然后這個匿名文本由于受到text-align:center影響居中了,這個時候absolute盒子的左邊跟父容器的中點對齊了,所以就還需要往回拉50%,這里用的是margin-left,你也可以用其它方式拉。然后就是垂直方向的對齊,垂直方向是不能被操作文本的屬性影響的,所以我這里用的是margin-top來讓它偏移下去。
absolute + margin : auto
代碼:
<div class="wrap">
<div class="ele"></div>
</div>
html,body{
width: 100%;
height: 100%;
margin: 0;
}
.wrap{
position: relative;
width: 100%;
height: 100%;
background-color: #ccc;
.ele{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: 100px;
height: 100px;
background-color: #333;
}
}
關于這種布局的原理,在標準中能找到如下解釋:
w3c.org 中有這樣一句話:
The constraint that determines the used values for these elements is:
‘left’ + ‘margin-left’ + ‘border-left-width’ + ‘padding-left’ + ‘width’ + ‘padding-right’ + ‘border-right-width’ + ‘margin-right’ + ‘right’ = width of containing block
這句話說的是absolute性質的盒子,它的包含塊的寬度等于它的盒模型的寬度 + 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所占寬度外的剩下寬度,擁有剩下寬度后,就是平分其寬度,以讓左右兩邊相等,達到居中,標準中給出了答案:
If none of the three is ‘auto’: If both ‘margin-left’ and ‘margin-right’ are ‘auto’, solve the equation under the extra constraint that the two margins get equal values, unless this would make them negative, in which case when direction of the containing block is ‘ltr’ (‘rtl’), set ‘margin-left’ (‘margin-right’) to zero and solve for ‘margin-right’ (‘margin-left’)
這里的”three”指的是left, width, right。如果left、right和width都不為auto,同時margin-left和margin-right都是auto,除非特別情況,它們倆就是相等的,而這個例子中不在特殊情況之列,因此兩者平分,此時達到了水平居中。而對于垂直方向的margin的auto值的計算,標準中也有如下兩句話,跟水平方向的同理(這里的“three”指的是“top, height, bottom”):
the used values of the vertical dimensions must satisfy this constraint:
‘top’ + ‘margin-top’ + ‘border-top-width’ + ‘padding-top’ + ‘height’ + ‘padding-bottom’ + ‘border-bottom-width’ + ‘margin-bottom’ + ‘bottom’ = height of containing block
if none of the three are ‘auto’: If both ‘margin-top’ and ‘margin-bottom’ are ‘auto’, solve the equation under the extra constraint that the two margins get equal values.
垂直方向也就因此也居中了。
這種方法能簡單的做到居中,但是必須有width和height值
適用于圖片居中的網易nec的一個方法
代碼:
<div class="wrap">
<p>
<img src="https://simg.open-open.com/show/687602e311e0b26f512c1abab38514f0.jpg" alt="" />
<img src="https://simg.open-open.com/show/687602e311e0b26f512c1abab38514f0.jpg" alt="" />
</p>
</div>
html,body{
width: 100%;
height: 100%;
margin: 0;
}
.wrap{
position:relative;
width: 100%;
height: 100%;
p{
position:absolute;
left:50%;
top:50%;
}
img{
&:nth-child(1){
position:static;
visibility:hidden;
}
&:nth-child(2){
position:absolute;
right:50%;
bottom:50%;
}
}
}
這種方法主要是利用了一個圖片進行占位,以讓父容器獲得高寬,從而讓進行-50%偏移的圖片能有一個參照容器作百分比計算。優點是可以不知道圖片的大小,隨便放張尺寸不超過父容器的圖片上去都能做到居中。另外,兼容性好,如果是不使用nth-child選擇器的花,IE6都是能順利兼容的
四. float布局上下文下的水平垂直居中
float + -50%
代碼:
<div class="wrap">
<div class="ele">
<div class="ele-inner">居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中居<br>中居中居中居中居中居中居中居中居中居<br>中居中居中居中居中居中居中</div>
</div>
</div>
.wrap{
float: left;
width: 100%;
height: 400px;
background-color: #ccc;
.ele{
float: left;
position: relative;
left: 50%;
top: 50%;
}
.ele-inner{
position: relative;
left: -50%;
-webkit-transform : translate3d(0, -50%, 0);
transform : translate3d(0, -50%, 0);
background-color: #333;
color: #fff;
}
}
這種方法的原理,首先是利用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的話,那就會在外面產生滾動條,問題DEMO鏈接 在這里 ,后來我找到了一個辦法: 基本思想就是利用元素超出父元素的左邊不會產生滾動條的特性,有點奇淫技巧,但是能解決問題,有興趣的可以看看
margin-bottom : -50%
代碼:
<div class="wrap">
<div class="placeholder"></div>
<div class='content'></div>
</div>
.wrap{
float: left;
width: 100%;
height: 400px;
background-color: #ccc;
@contentHeight : 100px;
.placeholder{
float: left;
width: 100%;
height: 50%;
/*居中元素.content高度一半*/
margin-bottom: -(@contentHeight / 2);
}
.content{
position: relative;
left: 50%;
transform:translate3d(-50%, 0, 0);
clear: both;
/*演示用,實際不需要定寬*/
max-width: 100px;
height: @contentHeight;
background-color: #333;
}
}
這種方法是先讓占位元素.placeholder占據50%高度,然后給一個居中元素高度一半的負的margin-bottom,然后下面的元素只要跟著擺放就能垂直居中了。水平方向就是利用translate做偏移,這個沒什么好說的,你也可以換成其他辦法。
這種方法就是各種固定死,首先最外層的父容器需要一個固定高度,以讓.placeholder的height:50%有效,然后,margin-bottom也需要固定死,而且得需要知道居中元素高度。單純就水平方向來說,這個方法比較適合需要兼容低版本IE的固定式布局的項目,因為兼容性好。
五.BFC布局上下文下的水平垂直居中
BFC的全稱是塊級排版上下文,這里有篇 文章 對齊進行了簡單的介紹,BFC布局上下文下的布局其實就是利用盒模型本身進行的布局,前面在利用盒模型布局的那一節中已經講過了,這里就不重復了
六.IFC布局上下文下的水平垂直居中
text-align:center + vertical-align:middle
代碼:
<div class="wrap">
<div class='placeholder'><!--占位元素,用來作為居中元素的參照物--></div>
<div class="ele"></div>
</div>
.wrap{
width: 100%;
height: 400px;
/* min-height: 400px; */
text-align:center;
font-size: 0;
background-color: #ccc;
.placeholder,
.ele{
vertical-align: middle;
display: inline-block;
}
.placeholder{
overflow: hidden;
width: 0;
min-height: inherit;
height: inherit;
}
.ele{
width: 100px;
height: 100px;
background-color: #333;
}
}
行級元素會受到text-align和vertical-align的影響,關于vertical-align,這里首先是用text-center讓inline-block水平居中,然后給一個vertical-align:middle,但是僅僅給vertical-align:middle是不夠的,因為此時它還沒有vertical-align對齊的參照物,所以就給了一個占位的inline-block,它的高度是100%。
這個方法對于居中元素不需要定寬高,而且元素根據vertical-align值的不同不僅僅可以實現居中,還可以將其放在上面下面等。缺點是父元素需定高
text-align:center + line-height
代碼:
<div class="wrap">
<div class="ele">居中居中居中居中居中居中<br>居中居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中</div>
</div>
.wrap{
text-align: center;
line-height: 400px;
width: 100%;
height: 400px;
background-color: #ccc;
font-size: 0;
.ele{
line-height: normal;
vertical-align: middle;
display: inline-block;
background-color: #333;
font-size: 18px;
color: #fff;
}
}
這個方法,首先是水平方向,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
代碼:
<div class="wrap">
<div class="ele"></div>
</div>
.wrap{
text-align: center;
font-size: 400px * 0.873;/*約為高度的0.873*/
margin-left: auto;
margin-right: auto;
width: 400px;
height: 400px;
background-color: #ccc;
.ele{
vertical-align: middle;
width: 100px;
height: 100px;
display: inline-block;
background-color: #333;
}
}
這個方法來自淘寶,基本原理還是讓隱匿文本節點所占據的行高等于父容器的高度,然后給居中元素一個vertical-align:middle對齊的一個參照。只是這里把定義line-height值換成了定義font-size值,讓font-siz足夠大從而讓其行高等于父容器高度。為了證明這個font-size的作用,我把居中元素換成文本
代碼:
<div class="wrap">
a
</div>
.wrap{
text-align: center;
font-size: 400px * 0.873;/*約為高度的0.873*/
margin-left: auto;
margin-right: auto;
width: 400px;
height: 400px;
background-color: #ccc;
}
效果:
可以看到字母a垂直居中了,這個字母a就對應那個隱匿文本節點
七.FFC布局上下文下的水平垂直居中
父元素、子元素都定義flex:
代碼:
<div class="wrap">
<div class="ele">
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中
</div>
</div>
html,body{
width: 100%;
height: 100%;
}
.wrap{
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: #ccc;
.ele{
background-color: #333;
}
}
只有父元素定義flex,子元素定義margin:auto:
代碼:
<div class="wrap">
<div class="ele">
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中<br>
居中居中居中居中居中居中居中
</div>
</div>
html,body{
width: 100%;
height: 100%;
}
.wrap{
display: flex;
width: 100%;
height: 100%;
background-color: #ccc;
.ele{
margin:auto;
background-color: #333;
}
}
The margins of adjacent flex items do not collapse. Auto margins absorb extra space in the corresponding dimension and can be used for alignment and to push adjacent flex items apart; see Aligning with auto margins.
意思就是說flex item的margin不會折疊,在flex-item有明確大小并且margin:auto時外邊距吸收了伸縮包含塊下的額外的空間,并且能被用于居中以及會讓其相鄰的flex item盡可能的往這個flex item所在的那一個方向靠。
flexbox是個很強大的布局模塊,也就三個屬性就搞定居中了,而且不論父容器還是居中元素都可以不定寬高。
八.table布局上下文下的水平垂直居中
代碼:
<div class='wrap'>
<div class='ele'>
<div class="ele-inner">居中居中居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中<br>居中居中居中居中居中居中居中居中居中居中</div>
</div>
</div>
.wrap{
width: 100%;
height: 300px;
display: table;
background-color: #ccc;
}
.ele{
text-align:center;
vertical-align: middle;
display:table-cell;
}
.ele-inner{
display: inline-block;
background-color: #333;
}
原理就是把div模擬成表格(換成真正的表格標簽也是可以的),然后給那幾個屬性就成了,這個沒什么好講的,不懂的去翻翻手冊就明白了,然后@于江水寫的一篇 table那些事 還不錯
九.CSS grid布局上下文下的水平垂直居中
CSS3 grid layout是IE搞出來的一個布局模塊,目前貌似還只有IE0和IE11支持
十.其它未知歸屬的水平垂直居中方法
使用button標簽
代碼:
<button>
<div>
居中居中居中居中居中居中<br>
居中居中居中居中居中居中<br>
居中居中居中居中居中居中<br>
居中居中居中居中居中居中<br>
</div>
</button>
button{
width: 100%;
height: 400px;
background-color: #cccccc;
border-width:0;
&:focus{
outline:none;
}
div{
display: inline-block;
font-size: 18px;
background-color: #333;
color: #fff;
}
}
這種方法屬于奇淫技巧,利用button標簽天生外掛的這一技能對其里面的元素進行居中。
來自:http://lise.io/2017/04/css-vertical-align/