半熟前端

軟體工程師 / 台灣人 / 在前端的路上一邊探索其他領域的可能性

前端

高度相同的排版解決方案

在前端的頁面中,我們經常會碰到需要相同高度的排版。最直覺的方法就是將容器裡的所有元素設為 float 或是 inline-block。

float 及 inline-block

如果是使用 float 排版,不但要先撐開父元素容器(clearfix),還要針對子元素設定 margin。 所以一旦內容太多,或是高度不足就會跑版。

而且,這樣的排版最大的缺陷就是,必須設定高度

那,如果不設置高度呢? 就算設定了 min-height 也一樣,當內容超出高度時,就必定會面臨 overflow 的危機。

後來決定直接用 css media query 在不同的螢幕寬度下分別給予不同的高度。 雖然解法比較麻煩,也比較醜一點,但的確解決了寬度過窄時會跑版的問題。

這個問題後來一直深埋在心中,直到最近發現了 flex 的奧秘。

排版遇到困難,先想想 flex

人生遇到挫折的時候,想想 flex,這個彈性盒子常常會救你一命。 flex 已經支援大部分的主流瀏覽器,而且真的很好用!

將 display 設置為 flex 的時候,如果子元素沒有設定高度,則子元素的高度會是其中最高的那個。

一行屬性就解決了我朝思暮想的問題,真是優雅的 flex

但除此之外,我們還需要對排版做一些調整。 flex 預設如果沒有設置 flex-wrap 屬性的話,就會以單行顯示的方式來撐開父容器。 因此我們可以再加上一行。

.wrap {
  flex-wrap: wrap;
}

好了,高度相同的 responsive 排版,不宣告 height 就此完成。大概的 css 會長得像這樣:

.container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
}

.card {
  height: auto;
  width: 30%;
  border: 1px solid #aaa;
}

不支援 flex 的瀏覽器怎麼辦?

首先 flex 已經支援大部分的主流瀏覽器了,不要用支援度這種藉口來忽視這麼好用的 flex。 但如果瀏覽器真的不支援,可以用 js 的方式來做排版。主要的原理是不設定高度,偵測 container 裡面的所有元素,並且找出高度最高的,並將此高度套用到每個元素中。

寫了一個很基本的範例代碼:

var cards = document.querySelectorAll(".card")

function getMaxinumHeight(elements) {
  var nums = []

  elements.forEach(function(value) {
    nums.push(value.offsetHeight)
  })

  return nums.sort(function(a, b) {
    return a < b
  })[0]
}

var maxHeight = getMaxinumHeight(cards)
cards.forEach(value => {
  value.style.height = maxHeight + "px"
})

Table is new sexy

雖然古老的 table 排版已經被唾棄,不過像是等高這種場景,如果不幸無法使用 flex 時,可以利用 table 的特性來達成等高排版。

要完成 table 排版,可以使用 display: table, display:table-row, display: table-cell,來完成。

display: table 等同於 <table>display: table-row 等同於 <tr>display: table-cell 等同於 <td>

不過,雖然能夠達到等高的效果,但 HTML 的 markup 變得更複雜了。而且 table 在使用上仍然有一些限制,像是 margin 沒辦法在 table 裡頭生效等等,這些在實作 mockup 時都是很大的阻礙。所以,如果能夠用 flex 來做的話,就盡量使用 flex 吧!

延伸閱讀

Flexbox responsive equal height