CSS佈局之-水平垂直居中

對一個元素水平垂直居中,在咱們的工做中是會常常遇到的,也是CSS佈局中很重要的一部分,本文就來說講CSS水平垂直居中的一些方法。
另外,文中的css都是用less書寫的,若是看不懂less,能夠把我給的demo連接打開,而後在控制檯中查看最終的css,或者是點擊codepen上的「View Compiled」按鈕,能夠查看編譯後的csscss

先看一張圖,這是去年cssConf大會時阿里的 @寒冬winter 老師放出來的:html

alt

如圖所示,CSS佈局是能夠分爲幾大塊的:css3

  • 盒子內部的佈局
    • 文本的佈局
    • 盒模型自己的佈局
  • 盒子之間的佈局visual formatting
    • 脫離正常流normal flow的盒子的佈局
      • absolute佈局上下文下的佈局
      • float佈局上下文下的佈局
    • 正常流normal flow下的盒子的佈局
      • BFC佈局上下文下的佈局
      • IFC佈局上下文下的佈局
      • FFC佈局上下文下的佈局
      • table佈局上下文下的佈局
      • css grid佈局上下文下的佈局

全部的CSS佈局其實都是圍繞着這些佈局模塊來的,水平垂直居中也同樣。web

一. 文本的水平垂直居中

line-height + text-align:centerless

DEMO連接ide

代碼:wordpress

<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組成的,以下圖:佈局

Alt text

也就說我任一個子盒子的水平和垂直方向的邊與最外面盒子的間距都是能夠控制的,所以也就有以下居中方法:flex

padding填充

DEMO連接

代碼:

<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()動態計算:

DEMO連接

<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填充

DEMO連接

代碼:

<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的屬性值,能夠動態計算元素的寬度,DEMO連接

使用盒模型進行佈局不會產生reflow,兼容也好,使用盒模型佈局是一種佈局思想,其實僅僅靠它就能實現不少visual formatting才能實現的佈局,這是另外一個話題,這裏不展開。

三. absolute佈局上下文下的水平垂直居中

50% + -50%

原理很簡單,就是利用left:50%將盒子的左邊先置於父容器的中點,而後再將盒子往左偏移盒子自身寬度的50%,這裏有三種具體實現:

DEMO連接

<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元素是會受文本的影響的,能夠參考張老師的這篇文章

DEMO連接

代碼:

<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

DEMO連接

代碼:

<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的一個方法

DEMO連接

代碼:

<div class="wrap">
  <p>
    <img src="http://nec.netease.com/img/s/1.jpg" alt="" />
    <img src="http://nec.netease.com/img/s/1.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%

DEMO連接

代碼:

<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連接在這裏,後來我找到了一個辦法:DEMO連接 ,基本思想就是利用元素超出父元素的左邊不會產生滾動條的特性,有點奇淫技巧,可是能解決問題,有興趣的能夠看看

margin-bottom : -50%

DEMO連接

代碼:

<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佈局上下文下的水平垂直居中

IFC又是個什麼概念呢,你能夠看看官方文檔,也能夠簡單的理解爲 display爲inline性質的行級元素的佈局。

text-align:center + vertical-align:middle

DEMO連接

代碼:

<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,不太好理解,我多貼幾篇文章:@靈感idea 的張鑫旭的MDN上的css-trick上的,以及官方文檔,這裏首先是用text-center讓inline-block水平居中,而後給一個vertical-align:middle,可是僅僅給vertical-align:middle是不夠的,由於此時它尚未vertical-align對齊的參照物,因此就給了一個佔位的inline-block,它的高度是100%。

這個方法對於居中元素不須要定寬高,並且元素根據vertical-align值的不一樣不只僅能夠實現居中,還能夠將其放在上面下面等。缺點是父元素需定高

text-align:center + line-height

DEMO連接

代碼:

<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

DEMO連接

代碼:

<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的做用,我把居中元素換成文本

DEMO連接

代碼:

<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;
}

效果:

alt

能夠看到字母a垂直居中了,這個字母a就對應那個隱匿文本節點

七.FFC佈局上下文下的水平垂直居中

父元素、子元素都定義flex:

DEMO連接

代碼:

<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:

DEMO連接

代碼:

<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;
  }
}

flex box的標準中有這句話(參考連接:http://www.w3.org/TR/css-flexbox-1/#item-margins)::)

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是個很強大的佈局模塊,也就三個屬性就搞定居中了,並且不論父容器仍是居中元素均可以不定寬高。參考連接:圖解CSS3 Flexbox屬性

八.table佈局上下文下的水平垂直居中

DEMO連接

代碼:

<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標籤

DEMO連接

代碼:

<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://div.io/topic/1155

相關文章
相關標籤/搜索