问题描述
可滚动容器
现在需要实现这样一个布局:
上面若干个固定高度的内容,最下面是一个可滚动的列表,滚动列表占据剩下的所有空间。最外层容器高度占据整个浏览器窗口高度的 100%,且最外层容器不可滚动。
这个实现思路比较明显,只需要用 Flex 布局,只允许最下面一个容器增长即可。
<div id="full">
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div id="scrollable">
<div>BBBBB</div>
<div>BBBBB</div>
<div>BBBBB</div>
<div>BBBBB</div>
<div>BBBBB</div>
...
</div>
</div>
</div>
html, #full, body {
padding: 0;
margin: 0;
height: 100%;
}
#full {
display: flex;
flex-direction: column;
gap: 10px;
}
#full * {
flex: 0 1 auto;
}
#scrollable {
flex: 1 1 auto;
overflow-y: auto;
}
Demo。效果图:
再嵌套一层
现在需求变了,需要在这个列表上面加一个标题,然后再在外面套一个边框,以突出显示。
这个也简单,只需要再嵌套一个 div 即可。
<div id="full">
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div class="fixed-item">AAAA</div>
<div id="scrollable-item">
<p>[Scrollable Title]</p>
<div id="scrollable">
<div>BBBBB</div>
<div>BBBBB</div>
<div>BBBBB</div>
...
</div>
</div>
</div>
html, #full, body {
padding: 0;
margin: 0;
height: 100%;
}
#full {
display: flex;
flex-direction: column;
gap: 10px;
}
#full * {
flex: 0 1 auto;
}
#scrollable-item {
display: flex;
flex-direction: column;
flex: 1 1 auto;
padding: 10px;
margin: 10px;
border: 1px solid black;
}
#scrollable {
overflow-y: auto;
}
Demo。但是你很快就会发现新的问题:
列表居然不滚动了,反而是页面变成滚动的了!
问题原因&解决
如果你打开 DevTools 的 Flex 调试画面,如图:
可以看见 scrollable-item
的下半部分被阴影覆盖。阴影的意思是,在这个 Flex 布局中,这一部分是空隙,或者说空白部分。
也就是说我们的 Flex 布局并没有问题,Flex 布局有尝试收缩 scrollable-item
,但是失败了,scrollable-item
溢出了而已。
为什么会溢出?
Flex 对其子项的收缩要受到 min-height
和 min-width
的限制。根据 MDN 对 min-width
的解释,min-width
的默认值为 auto
。这个 auto
有很多含义。其中,如果当前元素是 Flex 子项:
- 如果当前元素有显式设置宽度(例如
width
的值),那么min-width
就是显示设置的宽度 - 否则,值和
min-content
相同,也就是元素内容的最小宽度.
我们这里是高度,min-height
和 min-width
一样,默认值是元素内容的最小高度。scrollable-item
元素的内容就是一个标题 + 一个列表,因此最小高度就是“一个标题 + 一个列表”的高度。
最小高度就是内容高度,那当然没法收缩了。解决方法也很简单,设置 min-height: 0
即可。
#scrollable-item {
/* ... */
min-height: 0;
}
Demo。效果图:
为什么第一个例子里没有这个问题呢?
因为如果当前元素是 Flex 子项的同时还可滚动的话,那么 min-width
默认为 0。例一中的列表恰好满足这个条件,而例二中由于在外面又套了一层 div,滚动的实际是 Flex 子项的子元素,不满足这个条件。因此例二要手动设置 min-height
而例一不需要。
参考
https://stackoverflow.com/questions/36230944/prevent-flex-items-from-overflowing-a-container
https://developer.mozilla.org/en-US/docs/Web/CSS/min-width#browser_compatibility