问题
I'm trying to create a layout that uses a grid that takes up the entire browser viewport (height: 100vh
) and then allows a div within one of the grid items to scroll in the Y dimension.
Here's some example code. There are three grid areas: a header across the top, a left nav, and a content area in the lower right.
I'm trying to make it so div inside the content area (the big-list
), is the only thing with a vertical scrollbar, and the rest of the UI stays on the screen.
body {
margin: 0;
}
.outer-grid {
display: grid;
height: 100vh;
grid-template-columns: 180px 1fr;
grid-template-rows: min-content 1fr;
}
.top-bar {
grid-column-start: 1;
grid-column-end: 3;
border-bottom: 1px solid rgb(200, 200,200);
}
.header {
font-weight: bold;
margin: 8px;
}
.left-nav {
overflow-y: scroll;
padding: 8px;
border-right: 1px solid rgb(200, 200, 200);
}
#content {
padding-left: 8px;
padding-right: 8px;
}
#search {
width:100%;
}
.big-list {
overflow-y: scroll;
}
.big-list div {
padding: 8px 0;
border-top: 1px solid rgb(230,230,230);
}
<body>
<div class="outer-grid">
<div class="top-bar">
<div class="header">Header</div>
</div>
<div class="left-nav">
<div>Nav 1</div>
<div>Nav 2</div>
<div>Nav 3</div>
</div>
<div id="content">
<input id='search' type="text"/>
<div class='big-list'>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Last Row </div>
</div>
</div>
</div>
</body>
回答1:
Solution
Add this to your code:
#content {
display: flex; /* new */
flex-direction: column; /* new */
}
.big-list {
flex: 1 0 1px; /* new */
}
.outer-grid {
display: grid;
height: 100vh;
grid-template-columns: 180px 1fr;
grid-template-rows: min-content 1fr;
}
.top-bar {
grid-column-start: 1;
grid-column-end: 3;
border-bottom: 1px solid rgb(200, 200, 200);
}
.header {
font-weight: bold;
margin: 8px;
}
.left-nav {
/* overflow-y: scroll; */ /* removed */
padding: 8px;
border-right: 1px solid rgb(200, 200, 200);
}
#content {
display: flex; /* new */
flex-direction: column; /* new */
padding-left: 8px;
padding-right: 8px;
}
#search {
width: 100%;
}
.big-list {
flex: 1 0 1px; /* new */
overflow-y: auto; /* adjusted */
}
.big-list div {
padding: 8px 0;
border-top: 1px solid rgb(230, 230, 230);
}
body {
margin: 0;
}
<div class="outer-grid">
<div class="top-bar">
<div class="header">Header</div>
</div>
<div class="left-nav">
<div>Nav 1</div>
<div>Nav 2</div>
<div>Nav 3</div>
</div>
<div id="content">
<input id='search' type="text" />
<div class='big-list'>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Last Row </div>
</div>
</div>
</div>
Explanation
For an overflow to occur, content must overflow the container.
For content to overflow the container, the container must have a size limitation. Usually it's a fixed or maximum length, such as width: 150px
or max-height: 50vh
.
From MDN:
In order for
overflow
to have an effect, the block-level container must have either a set height (height
ormax-height
) orwhite-space
set tonowrap
.
You're trying to render a vertical scrollbar on your list element (.big-list
), but it doesn't have a height limitation.
The heights you have in place for it are:
1fr
(for the row, set by.outer-container
). This isn't even a length. It's a distribution of free space. This cannot trigger an overflow.height: auto
, which is the default height on elements. This doesn't provide any limitation, as content will simply keep expanding the container. So this cannot trigger an overflow, either.
Since you don't want to use fixed heights, but want the list element to render a scrollbar when necessary, you've got to be a bit sneaky. Still, it's a win-win. The browser gets what it wants. You get what you want.
Add this to your code:
#content {
display: flex;
flex-direction: column;
}
.big-list {
height: 1px;
flex-grow: 1;
}
So what you're doing is turning the list element's parent (#content
) into a flex container for the sole purpose of enabling flex-grow
on the child (.big-list
).
You set a tiny fixed height on the list element (height: 1px
). This satisfies the condition needed for an overflow. The browser gets what it wants.
Then, for the list element to fill remaining height, you add flex-grow: 1
. You get what you want.
Since height
is equivalent to flex-basis
in a column-direction flex container, you can simplify to:
flex: 1 0 1px /* flex-grow, flex-shrink, flex-basis */
回答2:
You can apply height to .big-list
and .left-nav
to height: calc(100vh - 51px); overflow-y: auto;
of subtract height of header
and search
.big-list {
overflow-y: scroll;
height:calc(100vh - 56px);
}
body {
margin: 0;
}
.outer-grid {
display: grid;
height: 100vh;
grid-template-columns: 180px 1fr;
grid-template-rows: min-content 1fr;
}
.top-bar {
grid-column-start: 1;
grid-column-end: 3;
border-bottom: 1px solid rgb(200, 200,200);
}
.header {
font-weight: bold;
margin: 8px;
}
.left-nav {
overflow-y: auto;
padding: 8px;
border-right: 1px solid rgb(200, 200, 200);
height: calc(100vh - 51px);
}
#content {
padding-left: 8px;
padding-right: 8px;
}
#search {
width:100%;
}
.big-list {
overflow-y: scroll;
height:calc(100vh - 56px);
}
.big-list div {
padding: 8px 0;
border-top: 1px solid rgb(230,230,230);
}
<body>
<div class="outer-grid">
<div class="top-bar">
<div class="header">Header</div>
</div>
<div class="left-nav">
<div>Nav 1</div>
<div>Nav 2</div>
<div>Nav 3</div>
</div>
<div id="content">
<input id='search' type="text"/>
<div class='big-list'>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Row ... </div>
<div> Last Row </div>
</div>
</div>
</div>
</body>
来源:https://stackoverflow.com/questions/54564976/grid-with-viewport-height-and-inner-scrolling-div