CSS 雜記:父元素未指定高度時 height 100% 無效
6 |
學會 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> </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"> </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 真的難 (尤其要跨裝置跨瀏覽器...)