Search code examples
cssflexbox

Fill (stretch) or scroll based on element size


Is it possible to get the stretch behavior of display: flex when min-content would not fill the page, but still have good scrolling behavior if min-content is larger than the page size?

Essentially if the behavior second code snippet doesn't fill the page, I want the behavior of the first code snippet.

document.addEventListener("DOMContentLoaded", () => {
  document.querySelector(".footer > input").addEventListener("change", (event) => {
    for (d of document.querySelectorAll(".content > div")) {
      d.style.height = ((event.target.value < 0.01) ? "100%" : (event.target.value + 'rem'));
    }
  });
});
* {
  margin: 0.5rem;
}

html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  justify-items: stretch;
  align-items: stretch;
  background-color: grey;
}

.content {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-items: stretch;
  align-items: stretch;
  background-color: blue;
}

.content>div {
  height: 100%;
  min-height: 1rem;
  background-color: gold;
}

.footer {
  height: 2rem;
  min-height: 2rem;
  flex: 0 0 1;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: green;
}
<body>
  <div class="content">
    <div></div>
    <div></div>
    <div></div>
  </div>
  <div class="footer">
    <input type="number" value=0 min=0>
  </div>
</body>

document.addEventListener("DOMContentLoaded", () => {
  document.querySelector(".footer > input").addEventListener("change", (event) => {
    for (d of document.querySelectorAll(".content > div")) {
      d.style.height = event.target.value + 'rem';
    }
  });
});
* {
    margin: 0.5rem;
}

html, body {
    width: 100%;
    height: min-content;
    margin: 0;
    padding: 0;
}

body {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    justify-items: stretch;
    align-items: stretch;
    background-color: grey;
}

.content {
    height: min-content;
    display: flex;
    flex-direction: column;
    justify-items: stretch;
    align-items: stretch;
    background-color: blue;
}

.content > div {
    height: 6rem;
    min-height: 1rem;
    background-color: gold;
}

.footer {
    height: 2rem;
    min-height: 2rem;
    flex: 0 0 1;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: green;
}
<body>
  <div class="content">
    <div></div>
    <div></div>
    <div></div>
  </div>
  <div class="footer">
    <input type="number" value=1 min=0>
  </div>
</body>


Edit: Added input box to make the behavior easier to see. If the gold boxes are too large in the first snippet than you can't scroll to see all of the footer. In the second snippet, the blue and gold boxes will never stretch to fill the available space.


Solution

  • I was able to get it working. I think the key was to use min-height: 100vh instead of height: 100vh as well as using flex-grow: 1. I tried flex: 1 before making the OP, but that didn't work (I probably need to read more about flex since it's apparently preferred to use flex instead of flex-*).

    document.addEventListener("DOMContentLoaded", () => {
      document.querySelector("#footer > input").addEventListener("change", (event) => {
        for (d of document.querySelectorAll(".content > div")) {
          d.style.height = event.target.value + 'rem';
        }
      });
    });
    body {
      margin: 0;
      padding: 0;
    }
    
    .container {
      width: 100%;
      min-height: 100vh;
      padding: 0.5rem;
      box-sizing: border-box;
      display: flex;
      flex-direction: column;
      justify-items: stretch;
      background-color: gray;
    }
    
    .content {
      padding: 0.5rem;
      flex-grow: 1;
      display: flex;
      flex-direction: column;
      justify-items: stretch;
      background-color: blue;
    }
    
    .content div {
      height: 2rem;
      flex-grow: 1;
      border: solid;
      background-color: gold;
    }
    
    .footer {
      height: 2rem;
      background-color: green;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .footer>input {
      height: min-content;
    }
    <div class="container">
      <div class="content">
        <div></div>
        <div></div>
        <div></div>
      </div>
      <div id="footer" class="footer">
        <input type="number" value=1 min=0>
      </div>
    </div>