问题描述
下面这段代码中, 按照原来设想应该是 sub-container 铺满 container 的剩余高度, 也就是 100vh, content 也是铺满 sub-container 的剩余高度, 也就是 100vh - 100px, 然后超出的部分通过滚动查看
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
margin: 0;
}
#container {
height: 100vh;
display: flex;
flex-direction: column;
}
#sub-container {
flex: 1;
display: flex;
flex-direction: column;
}
#content {
flex: 1;
background-color: red;
overflow: auto;
}
#footer {
height: 100px;
background-color: green;
}
</style>
</head>
<body>
<div id="container">
<div id="sub-container">
<div id="content"></div>
<div id="footer"></div>
</div>
</div>
<script>
const content = document.getElementById("content");
for (let i = 0; i < 100; i++) {
const div = document.createElement("div");
div.innerHTML = i;
content.appendChild(div);
}
</script>
</body>
</html>]但实际情况是 <div id="content"></div> 中的内容会撑开 <div id="sub-container"> 导出溢出 <div id="container">, overflow: auto 没有生效
原因
其原因是垂直排列的 flex 容器, 其子元素的 flex-shrink 默认为 1, 也就是说子元素在容器剩余空间不足时会自动收缩, 但同时子元素的 min-height 为 auto, 也就是说只能收缩到子元素的最小高度, 即子元素内容本身的高度
代入到上面示例, 虽然 sub-container 的 flex 为 1, 但最终高度还是由最小高度决定, sub-container 的 min-height 为 auto, 计算顺序是 sub-container 先看 content 本身的高度, 假设 content 最终高度为 1000px, 那么 sub-container 的最小高度就是 content 的高度加上 footer 的高度, 即 1100px, 虽然 content 有 overflow: auto, 但在 content 看来, 其本身的高度并没有超出 sub-container 的 1100px, 所以不会出现滚动, 最终 content 撑开 sub-container, 进而溢出 container
解决方案
min-height: 0
既然子容器的默认 min-height 为 auto, 那可以强制设置 min-height 为 0, 这样子容器就可以自由收缩不受内容高度限制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
margin: 0;
}
#container {
height: 100vh;
display: flex;
flex-direction: column;
}
#sub-container {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
#content {
flex: 1;
background-color: red;
overflow: auto;
}
#footer {
height: 100px;
background-color: green;
}
</style>
</head>
<body>
<div id="container">
<div id="sub-container">
<div id="content"></div>
<div id="footer"></div>
</div>
</div>
<script>
const content = document.getElementById("content");
for (let i = 0; i < 100; i++) {
const div = document.createElement("div");
div.innerHTML = i;
content.appendChild(div);
}
</script>
</body>
</html>overflow: hidden
给子容器设置 overflow: hidden 的原理和 min-height: 0 相同, 设置后子容器的 min-height 变为 0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
margin: 0;
}
#container {
height: 100vh;
display: flex;
flex-direction: column;
}
#sub-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
#content {
flex: 1;
background-color: red;
overflow: auto;
}
#footer {
height: 100px;
background-color: green;
}
</style>
</head>
<body>
<div id="container">
<div id="sub-container">
<div id="content"></div>
<div id="footer"></div>
</div>
</div>
<script>
const content = document.getElementById("content");
for (let i = 0; i < 100; i++) {
const div = document.createElement("div");
div.innerHTML = i;
content.appendChild(div);
}
</script>
</body>
</html>flex-basic: 0
设置内容的基础高度为 0, 这样子容器在计算最小高度时会认为 content 本身高度为 0, 所有子容器也能自由收缩
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
body {
margin: 0;
}
#container {
height: 100vh;
display: flex;
flex-direction: column;
}
#sub-container {
flex: 1;
display: flex;
flex-direction: column;
}
#content {
flex: 1;
background-color: red;
overflow: auto;
flex-basis: 0;
}
#footer {
height: 100px;
background-color: green;
}
</style>
</head>
<body>
<div id="container">
<div id="sub-container">
<div id="content"></div>
<div id="footer"></div>
</div>
</div>
<script>
const content = document.getElementById("content");
for (let i = 0; i < 100; i++) {
const div = document.createElement("div");
div.innerHTML = i;
content.appendChild(div);
}
</script>
</body>
</html>