2

I have a simple HTML document. I have a header, a section and a div (that contains an unknown number of other divs).

The header and the section do not (and can not) have set heights. Their height comes from the content. Only their width is known (set to 100%).

Is it possible, with flexbox or other means, to get each of those child divs, in this case with class="fill" to be the height of the body - minus the header and section?

In other words, when someone goes to the page, I want them to see the header and the section and then have the first div.fill reach all the way to the bottom, forcing them to scroll to see the next div (but not scroll to see the bottom of the first child div).

I am using a templating system so unfortunately the structure of the HTML can not change and I would like to do this only in CSS.

<html>
  <body>
      <header> Header content, might contain an image</header>
      <section> This is the sub header, unknown height </section>
      <div class="container">
        <div class="fill">I Want</div>
        <div class="fill">Each of These</div>
        <div class="fill">To be </div>
        <div class="fill">The height of the body - the Header - the Section</div>
      </div>
  </body>
</html>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Startec
  • 12,496
  • 23
  • 93
  • 160

2 Answers2

2

body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  margin: 0;
}

.container {
  flex: 1;                   /* 1 */
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
}

.fill { flex: 0 0 100%; }    /* 2 */

header  { background-color: aqua; }
section { background-color: orange; }
.fill:nth-child(odd) { background-color: yellow; }
.fill:nth-child(even) { background-color: lightgreen; }
<body>
  <header> Header content, might contain an image</header>
  <section> This is the sub header, unknown height </section>
  <div class="container">
    <div class="fill">I Want</div>
    <div class="fill">Each of These</div>
    <div class="fill">To be </div>
    <div class="fill">The height of the body - the Header - the Section</div>
  </div>
</body>

jsFiddle

Notes:

  1. The flex-grow: 1 component of flex: 1 tells the .container element (a flex item child of body) to consume all remaining space. This will cause .container to use up any space not consumed by header and section.

  2. The flex-basis: 100% component of flex: 0 0 100% tells the .fill items (flex item children of .container) to consume 100% height of the parent. So these items will always take the full height of flex-grow: 1 on the parent.

    Because flex items are set, by default, to shrink in order to not overflow the container, an override is set with flex-shrink: 0 in the flex: 0 0 100% rule. This disables the shrinking feature and allows the items to stay fixed at 100% height. (Otherwise, regardless of the defined height / flex-basis, the items would shrink evenly to prevent an overflow. See demo.)

Community
  • 1
  • 1
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • I like this solution, but unfortunately this cuts off some of the first div. I am using an image so I would prefer to not have the bottom of the image cut off at first. – Startec Mar 02 '17 at 01:08
  • Not sure it's possible with pure CSS. I've got everything set up, but we need to tell each `.fill` item to occupy the full height of the container. CSS can only tell them to distribute the available height amongst themselves. You may need JS. https://jsfiddle.net/tajvm6kk/2/ – Michael Benjamin Mar 02 '17 at 01:21
  • Your answers are both very good and very close, it's much appreciated. It seems like there should be a way to do this. – Startec Mar 02 '17 at 01:56
  • 1
    Hi @Startec, I updated my answer. With newer browser versions, which allow percentage heights to use flex heights for reference ([traditionally they could only reference fixed heights](https://stackoverflow.com/a/31728799/3597276)), your layout is now possible in pure CSS. Tested in Chrome, Firefox and Edge. – Michael Benjamin Nov 05 '17 at 11:46
0

If you change the structure of the elements a bit you can get it with only css.
Basically add the first .fill element in a container with the header and the section (let's call it first). For the other divs use height: 100vh

body {
  margin: 0;
}

.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.fill {
  height: 100vh;
  overflow: auto;
}

.first {
  flex: 1;
}

header { background-color: aqua; }
section { background-color: orange; }
.first, .fill:nth-child(odd) { background-color: yellow; }
.fill:nth-child(even) { background-color: lightgreen; }
<html>
<body>
  <div class="container">
    <header> Header content, might contain an image</header>
    <section> This is the sub header, unknown height </section>
    <div class="first">I Want</div>
  </div>
  <div class="fill">Each of These</div>
  <div class="fill">To be </div>
  <div class="fill">The height of the body - the Header - the Section</div>

</body>
</html>
pol
  • 2,641
  • 10
  • 16