面試題:水平垂直居中的17種方法

山頭人漢波發表於2022-04-02
面試的時候,絕不能只說一種,絕不能說一種解決方案,絕不能停下你吞吞吐吐的嘴

CSS 方面問的最多的問題之一,我想分三種情況,水平居中、垂直居中和水平垂直居中來分析

單單就水平垂直居中而言,大概有以下幾種方案:

居中元素不定寬高

  • absolute + transform
  • flex 屬性居中
  • flex + 子項 margin auto
  • grid 屬性居中
  • grid + 子項 margin auto
  • grid + 子項屬性居中
  • -webkit-box 屬性居中
  • table-cell + text-align
  • line-height + text-align
  • writing-mode
  • table

僅居中元素定寬高適用:

  • 須知寬高 + absolute + 負 margin
  • 須知寬高 + absolute + calc
  • 須知寬高 + absolute + margin auto

侷限性較大的全域性居中

  • 須知寬高 + fixed + transform
  • 須知寬高 + fixed + 負 margin
  • 須知寬高 + fixed + calc
  • 須知寬高 + fixed + margin auto

水平居中

text-align: center

text-align: center;

需設定 display: inline-block 行內塊元素

絕對定位 + transform 位移

position: absolute;
left: 50%;
transform: translateX(-50%);

脫離文件流

寬度+ margin: 0 auto

width: 100px;
margin: 0 auto;

這裡說明下,width:100px 必須是具體的數字,且這個居中是外層居中,寬度中的內容沒有居中

垂直居中

絕對定位 + transform 位移

position: absolute;
top: 50%;
transform: translateY(-50%);

與水平方向的居中一樣,都是脫離文件流的做法

table-cell + vertical-align

display: table-cell;
vertical-align: middle;

display: table-cell ,讓其標籤元素以表格單元格的形式呈現,類似於 td 標籤,

vertical-align: middle,用來指定行內元素(inline)或表格單元格(table-cell)元素的垂直居中

水平垂直居中

絕對居中 + transform 位移

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  position: relative;
}
.son {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

flex 屬性居中

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: flex;
  justify-content: center;
  align-items: center;
}

flex + margin auto

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: flex;
}
.son {
  margin: auto;
}

grid 屬性居中

<div class="father">123123</div>
// 或者
<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: grid;
  justify-content: center;
  align-items: center;
}

grid 子項屬性居中

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: grid;
}
.son {
  align-self: center;
  justify-self: center;
}

grid + margin auto

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: grid;
}
.son {
  margin: auto;
}

grid 和 flex 很像,是 flex 的升級版,所以 grid 能做的事情更多

以上絕對定位、flex、grid 關於水平垂直居中的做法,剩下再說居中比較老的佈局方法

-webkit-box 屬性居中

這是一個已經過時的佈局,可以看看這篇文章 CSS3 display: flex 和 display: box 有什麼區別?

網友一絲說:

flex 是 2012 年的語法,是以後的標準

box 是 2009 年的語法,已經過時,需要加上對應字首

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: -webkit-box;
  -webkit-box-pack: center;
  -webkit-box-align: center;
}

table-cell + text-align

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}
.son {
  display: inline-block;
}

line-height + text-align

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  height: 200px;
  line-height: 200px;
  text-align: center;
}

line-heightheight ,行高和高度一樣高,自然就垂直方向居中了

writing-mode

<div class="father">
  <div class="“son”">
    <div class="“sonson”">
      123123
    </div>
  </div>
</div>
.father {
  writing-mode: tb-lr;
  writing-mode: vertical-lr;
  text-align: center;
}

.father .son {
  writing-mode: lr-tb;
  writing-mode: horizontal-tb;
  text-align: center;
  width: 100%;
  display: inline-block;
}
.father9 .son .sonson {
  display: inline-block;
  text-align: initial;
}

這個很冷悶,有人介紹過這種情況

table

<table>
  <tbody>
    <tr>
      <td class="father">
        <div class="son">
          123123
        </div>
      </td>
    </tr>
  </tbody>
</table>
.father {
  text-align: center;
}

table 標籤自己將它垂直居中了,text-align:center 後就是水平居中了

可以看 demo

元素有寬高的情況,又多了三種方案

須知寬高 + 絕對居中 + margin 負邊距

<div class="father">
    <div class="son">
        123123
    </div>
</div>
.father {
  position: relative;
  height: 200px;
}
.son {
  width: 100px;
  height: 100px;
  position: absolute;
  top: 50%;
  left: 50%;
  margin: -50px 0 0 -50px;
}

父元素必須要有個高度,這樣才能撐開容器。子元素必須要有個寬高,才能計算出 margin 值

須知寬高 + 絕對定位 + calc

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  position: relative;
  height: 200px;
}

.son {
  width: 100px;
  height: 100px;
  position: absolute;
  top: calc(50% - 50px);
  left: calc(50% - 50px);
}

與 margin 負邊距一個道理,父元素需要設定一個高度。子元素必須要有高度,不用 margin,而用 CSS3 中的 calc,計算出要居中位移,相容性需要支援 CSS3 屬性

須知寬高 + 絕對居中 + margin: auto

<div class="father">
  <div class="son">
    123123
  </div>
</div>
.father {
  position: relative;
  height: 300px;
}

.son {
  width: 100px;
  height: 100px;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
}

同以上兩種情況。

這三種需要定位方式來實現水平垂直居中的方法,需要設定父元素的高度(一定要有,撐開畫面),子元素需要設定寬高,前兩種方法是為了算出它在父元素中的相對位置,後一種方法是為了說明子元素是個容器(如果不設定寬高,就是無)

其他方法

其實,水平垂直居中方面,如果面試官硬要問還有嗎?還真的有,用 fixed 定位。但這個方法有缺陷,雖然能實現水平垂直居中,但它是相對於視口(viewport),而非父元素

方法就是以上用 absolute 實現的改成 fixed 即可

  • 須知寬高 + fixed + transform
  • 須知寬高 + fixed + 負 margin
  • 須知寬高 + fixed + calc
  • 須知寬高 + fixed + margin auto

這四種方法,都需要設定子元素的寬高

這裡貼一下程式碼

<div class="father">
  <div class="son">
    123123
  </div>
</div>
/* transform */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: blue;
}

/* 負 margin */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-left: -50px;
  margin-top: -50px;
  background: blue;
}

/* calc */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: calc(50% - 50px);
  left: calc(50% - 50px);
  background: blue;
}

/* margin: auto */
.son {
  width: 100px;
  height: 100px;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  background: blue;
}

總結

隨著微軟宣佈放棄 IE11,現實專案中完全可以使用 flex 佈局,grid 部分還不適配,但是以後肯定會取代 flex。

雖然寫了很多,但是自己工作中也不會使用 table 、writing-mode、-webkit-box 等過時的佈局方式,寫這篇文章,純粹是為了面試時被問到這種問題。

收穫是 absolute 的居中要父子同心(父元素設定高度,子元素設定寬高),fixed 的居中只需要設定子元素的寬高。

線上 demo 檢視

參考資料

本文參與了 SegmentFault 思否徵文「如何“反殺”面試官?」,歡迎正在閱讀的你也加入。

相關文章