學會 CSS Flexbox 時超開心,以為人生從此不必再為佔滿剩餘網頁寬/高度煩惱,豈知還是會踩到小石頭。

有個需求是希望 IFrame/DIV 佔滿 TD 全部高度,IFrame/DIV 有設 height: 100%,但因父容器未指定高度時,不會發生效果。

用範例解說。table 透過 flex-grow: 1; 佔滿剩餘高度,第一個 tr 指定高度 30px ,第二個 tr 則佔滿 table 剩餘高度,td 中的 div 雖然設了 height: 100%,但因為父層 td 未設高度,故未伸展維持預設高度,約一行文字高。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>100% height test</title>
  <style>
    table { width: 300px; flex-grow: 1; }
    td { border: 1px solid gray; }
    html,body {
      height: 100%; width: 300px;
      display: flex;
      flex-direction: column;
    }
    header { height: 20px; background-color: bisque; }
    td.hdr {
        width: 50px; background-color: #eee
    }
    div {
        height:100%; background-color:cornflowerblue
    }
  </style>
</head>
<body>
  <header>Banner</header>
  <table id="tbl">
    <tr style="height:30px;">
        <td class="hdr"></td>
        <td></td>
    </tr>
    <tr>
      <td class="hdr"></td>
      <td>
        <div>&#160;</div>
      </td>
    </tr>
  </table>
</html>

爬文得知屬常問題,父元素未指定高度時,子元素無法 height: 100% 為已知限制,一般建議回歸 Flexbox 解法。參考:How to force child div to be 100% of parent div's height without specifying parent's height?

但這代表要把原本 table 結構拆掉,改用 Flexbox 模擬相同排版,工程不小,未必會被接受。

最後,招喚老朋友 jQuery,用土氣但有效的方法搞定。註冊 window 的 Resize 事件,遇到視窗縮放時先將 div 高度設為 0,讓 td 恢復自己應有高度,再讓 div 與 td 同高。為避免改變 window 尺寸時重複觸發 Resize 事件,我還加上 setTimeout 延遲 10ms 執行簡單實現 Debounce。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>100% height test</title>
  <style>
    table { width: 300px; flex-grow: 1; }
    td { border: 1px solid gray; }
    html,body {
      height: 100%; width: 300px;
      display: flex;
      flex-direction: column;
    }
    header { height: 20px; background-color: bisque; }
    td.hdr {
        width: 50px; background-color: #eee
    }
    div {
        height:100%; background-color:cornflowerblue
    }
  </style>
</head>
<body>
  <header>Banner</header>
  <table id="tbl">
    <tr style="height:30px;">
        <td class="hdr"></td>
        <td></td>
    </tr>
    <tr>
      <td class="hdr"></td>
      <td>
        <div id="h100">&#160;</div>
      </td>
    </tr>
  </table>
  <script src="https://code.jquery.com/jquery-3.1.0.js"></script>  
  <script>
    var debounce;
    $(window).resize(function() {
      const el = $('#h100');
      clearTimeout(debounce);
      setTimeout(function() {
        el.height(0)
          .height(el.parent().height());
      }, 10);      
    }).resize();
  </script>
</html>

就醬,在不更動排版樣式的前題下,簡單實現自動佔滿 TD 效果。

【2022-11-15 更新】

讀者程凱大分享一版寫法,html,body 的 height: 100% 改為 100vh (View Height),table 設 height: 100%,如此 td 視同被明確指定高度,div 可展高度。

table { width: 300px; flex-grow: 1; height: 100%}
html,body {
  height: 100vh; width: 300px;
  display: flex;
  flex-direction: column;
}

線上展示

感謝讀者 icecain 整理完整結論:

  • table height: 100% 是關鍵,等同具體指定了祖父容器 table 高度
    table { width: 300px; height: 100%; flex-grow: 1; }
    
  • 本例中html,body 設 height: 100% 或 100vh 均可讓 div 佔滿,但 100vh 在行動版會因網址列高產生垂直捲軸。
  • flex-grow: 1 的 1 為縮放比,在本例自動擴展的元素只有一個,填 0 亦可

線上展示

Example of using jQuery resize event to force div fill td's 100% height.


Comments

# by icecain

最初的作法還是可以加上額外的css達成需求 目標div的外層td用相對定位 div用絕對定位 https://stackblitz.com/edit/js-78mxls?file=index.html 寬高用calc(100% - 2px); 是為了視覺上與原範例的border-spacing一致

# by icecain

補充,再細看了一下 最初的範例,只要table補上height: 100%即可 其餘都不用 https://stackblitz.com/edit/js-jrm1dq?file=index.html

# by Jeffrey

to icecain, 感謝分享,已加入本文。所以 flex-grow: 1 不算指定高度,但 height: 100% 算;但如果 body 指定了 height: 100vh,則 flex-grow: 1 也算指定高度。(CSS 眉角真多)

# by icecain

to 黑大: CSS真的眉角超多,借這個例子我也發現了許多以前沒注意到的事 根據你先前貼的指定body高100vh那個網址來看 https://stackblitz.com/edit/js-evrppy?file=index.html 其實關鍵反而是table有設了height: 100% 若是將table的height: 100%移除,則一樣無法獲得高度 https://stackblitz.com/edit/js-kc77oz?file=index.html 另外,我們一直以為flex-grow: 1是關鍵,但驚奇發現,根本不需要 甚至設flex-grow: 0,也能佔據高度 https://stackblitz.com/edit/js-kc77oz?file=index.html 字面上我們以為flex-grow: 1是強制伸展(0 = false, 1 = true,程式寫慣了,這樣很合理) 但查了一下規格,他其實是定義元件在flex 容器中分配剩餘空間的相對比例 https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow 至於body設height: 100%或100vh,在pc版應用沒差 但100vh在實體手機上會有一個bug 因為手機瀏覽器,網址列多是浮動的(往下滑動閱讀時隱藏,往上滑動則顯示) 100vh會超出含網址列時的視界高度(產生捲軸,下方內容會被遮蔽) 此時就不合需求規格了

# by icecain

前述 >其實關鍵反而是table有設了height: 100% >若是將table的height: 100%移除,則一樣無法獲得高度 示意網址要改成這個 https://stackblitz.com/edit/js-3atv5e?file=index.html

# by Jeffrey

to icecain,謝謝詳細研究,已修改本文。CSS 真的難 (尤其要跨裝置跨瀏覽器...)

Post a comment