LodeRunner
LodeRunner

Reputation: 8393

Make a div scrollable inside flexbox

I have a sidebar that contains 2 panes. When I resize the window vertically, I want the top pane to dynamically resize, and the bottom pane to remain to a fixed size. When the total height of the sidebar is smaller than both panes, I want the top pane to shrink smaller than it's content, and become scrollable.

The expected behavior is the same as VS Code's sidebar.

Currently, when resizing, the top pane "pushes" the bottom pane down.

document.addEventListener("DOMContentLoaded", () => {
  const listItem = document.querySelector(".top-panel .list-item");
  for (let i = 0; i < 25; i++) {
    listItem.parentNode.appendChild(listItem.cloneNode(true));
  }
});
html,
body {
  margin: 0;
}

.main {
  margin-left: 12rem;
}

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 12rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-right: 1px solid #454545;
}

.top-panel {
  flex-shrink: 1;
  display: flex;
  flex-direction: column;
}

.bottom-panel {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
}

.header {
  background-color: #ababbc;
  font-weight: 700;
}

.panel-top .list {
  flex-shrink: 1;
  overflow-y: auto;
}

.panel-bottom .list {
  flex-shrink: 0;
}

.list-item {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
<aside class="sidebar">
  <div class="top-panel">
    <div class="header">Top panel</div>
    <div class="list">
      <div class="list-item"><span>https://example.com/users/helloworld</span></div>
      <!-- copied 25 times in the JS -->
    </div>
  </div>
  <div class="bottom-panel">
    <div class="header">Bottom panel</div>
    <div class="list">
      <div class="list-item"><span>Lorem</span></div>
      <div class="list-item"><span>Ipsum</span></div>
    </div>
  </div>
</aside>
<main class="main">Main content</main>

Use "Full page" in snippet to see resizing behavior.

Upvotes: 5

Views: 134

Answers (3)

Brett East
Brett East

Reputation: 4322

On your top panel add this:

.top-panel {
  overflow-y: auto;
}

Basically you need to tell that div how to behave when it reaches the smallest it can be while showing all of its contents.

flex-shrink: 1 helps with letting it know that the top-panel should shrink and that the bottom-panel shouldn't, but once it shrinks to the height of it's children, you need to let it know that it can shrink further than that. Adding the overflow-y: auto will do that.

That should sort it out :)

Upvotes: 2

Cuong Vu
Cuong Vu

Reputation: 3723

You can add overflow-y: auto to the top panel, to only show the scrollbar when necessary.

You can also add a fixed size to the bottom panel by adding height: 100px for example. The top panel will automatically shrink to fit the remaining space.

document.addEventListener("DOMContentLoaded", () => {
  const listItem = document.querySelector(".top-panel .list-item");
  for (let i = 0; i < 25; i++) {
    listItem.parentNode.appendChild(listItem.cloneNode(true));
  }
});
html,
body {
  margin: 0;
}

.main {
  margin-left: 12rem;
}

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 12rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-right: 1px solid #454545;
}

.top-panel {
  overflow-y: auto;
  flex-shrink: 1;
  display: flex;
  flex-direction: column;
}

.bottom-panel {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
}

.header {
  background-color: #ababbc;
  font-weight: 700;
}

.panel-top .list {
  flex-shrink: 1;
  overflow-y: auto;
}

.panel-bottom .list {
  flex-shrink: 0;
}

.list-item {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
<aside class="sidebar">
  <div class="top-panel">
    <div class="header">Top panel</div>
    <div class="list">
      <div class="list-item"><span>https://example.com/users/helloworld</span></div>
      <!-- copied 25 times in the JS -->
    </div>
  </div>
  <div class="bottom-panel">
    <div class="header">Bottom panel</div>
    <div class="list">
      <div class="list-item"><span>Lorem</span></div>
      <div class="list-item"><span>Ipsum</span></div>
    </div>
  </div>
</aside>
<main class="main">Main content</main>

Upvotes: 2

Kasper
Kasper

Reputation: 114

Adding the overflow-y property to the top-panel class should work. Then give it the value of auto to make it scroll if the content is too large for the div height.

document.addEventListener("DOMContentLoaded", () => {
  const listItem = document.querySelector(".top-panel .list-item");
  for (let i = 0; i < 25; i++) {
    listItem.parentNode.appendChild(listItem.cloneNode(true));
  }
});
html,
body {
  margin: 0;
}

.main {
  margin-left: 12rem;
}

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 12rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border-right: 1px solid #454545;
}

.top-panel {
  flex-shrink: 1;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.bottom-panel {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
}

.header {
  background-color: #ababbc;
  font-weight: 700;
}

.panel-top .list {
  flex-shrink: 1;
  overflow-y: auto;
}

.panel-bottom .list {
  flex-shrink: 0;
}

.list-item {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
<aside class="sidebar">
  <div class="top-panel">
    <div class="header">Top panel</div>
    <div class="list">
      <div class="list-item"><span>https://example.com/users/helloworld</span></div>
      <!-- copied 25 times in the JS -->
    </div>
  </div>
  <div class="bottom-panel">
    <div class="header">Bottom panel</div>
    <div class="list">
      <div class="list-item"><span>Lorem</span></div>
      <div class="list-item"><span>Ipsum</span></div>
    </div>
  </div>
</aside>
<main class="main">Main content</main>

Upvotes: 1

Related Questions