3

I have a <div> that takes up 60% of the window space, and it contains two things:

  • a narrow header line
  • an image that I want to take up the remainder of the div.

How can I do this with pure CSS (no Javascript)? I've been trying a bunch of things, no luck.

This is the closest I can get; the image sneaks outside of the green border of the div.container

html, body {
  height: 100%;
  overflow: hidden;
  margin: 0px;
}
div.container {
  height: 60%;
  border: 2px solid green;
  box-sizing: border-box;
}
div.rest {
  height: 40%;
  border: 2px solid red;
  box-sizing: border-box;
}
div.img-container {
  height: 100%; /* this is wrong, but what do I do? */
}
div.img-container img {
  max-height: 100%;
  max-width: 100%;
  height: auto;
  width: auto;
  opacity: 0.5;
}
<html>
<body>
<div class="container">
<div class="header">hieronymus bosch last judgement</div>
<div class="img-container"><img src="https://i.imgur.com/TT6drhn.jpg"></div>
</div>
<div class="rest">
<h1>blah blah blah</h1>
</div>
</body>
</html>

Here's my attempt at using flex but that fails.

html, body {
  height: 100%;
  overflow: hidden;
  margin: 0px;
}
div.container {
  height: 60%;
  border: 2px solid green;
  box-sizing: border-box;
  display: flex;
  flex-direction: column
}
div.rest {
  height: 40%;
  border: 2px solid red;
  box-sizing: border-box;
}
div.img-container {
  flex: 1;
}
div.header {
  flex: 0;
}
div.img-container img {
  max-height: 100%;
  max-width: 100%;
  height: auto;
  width: auto;
  opacity: 0.5;
}
<html>
<body>
<div class="container">
<div class="header">hieronymus bosch last judgement</div>
<div class="img-container"><img src="https://i.imgur.com/TT6drhn.jpg"></div>
</div>
<div class="rest">
<h1>blah blah blah</h1>
</div>
</body>
</html>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Jason S
  • 184,598
  • 164
  • 608
  • 970

3 Answers3

4

@Christian's approach works if you know the height of the header element, however alternatively you could use flex.

This allows the element to grow to fill the remaining space dynamically, so your header can be any height.

html,
body {
  height: 100%;
  overflow: hidden;
  margin: 0px;
}

div.container {
  height: 60%;
  border: 2px solid green;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

div.rest {
  height: 40%;
  border: 2px solid red;
  box-sizing: border-box;
}

div.img-container {
  flex: 1;
  position: relative;
}

div.img-container img {
  opacity: 0.5;
  height: 100%;
  position: absolute;
}
<html>

<body>
  <div class="container">
    <div class="header">hieronymus bosch last judgement</div>
    <div class="img-container"><img src="https://i.imgur.com/TT6drhn.jpg"></div>
  </div>
  <div class="rest">
    <h1>blah blah blah</h1>
  </div>
</body>

</html>
j-petty
  • 2,668
  • 1
  • 11
  • 20
  • 1
    Thank you for taking the time to write out a flex example! =) – Lewis Feb 14 '20 at 03:59
  • If I add back the max-width/max-height=100% width/height=auto for the img, then it does exactly what I want! https://codepen.io/jason-s/full/RwPrjGJ but could you explain the need for relative/absolute on the img-container and img? I've only seen `absolute` used when you position something at a fixed spot relative to its container and have something like `left: 10px` to anchor it near the left side of its container. – Jason S Feb 14 '20 at 04:34
  • ...and what if I want to center the image horizontally? – Jason S Feb 14 '20 at 04:37
  • 1
    You want the image to fill the container's height. A good way of doing this is setting the container to `position: relative;` and the child image to `position: absolute;` with `height: 100%;`. That is one way for forcing the child element to fill it's parent's height. As for centering the image you could set `display: flex;` and `justify-content: center;` on the img-container. – j-petty Feb 14 '20 at 05:00
2

If you look at div.img-container in Chrome Inspector, you can see what the issue is - the img element is doing its job and filling its container, but the container itself is overflowing.

This is happening because it is set to height: 100% - what this says is "make my height 100% of my parent's height", but this does not mean "fill the remaining space." The browser just reads the computed height of the element's parent, and then multiplies it by your % value - basically, it's all in absolute terms. You can see that the blue box is 100% as tall as the box outlined in green, but because it sits below a line of text, it overflows by the height of that text.

flex could be used to solve this problem, but you can patch this pretty quickly by using calc to subtract out the height of that text. In your example, it's 19px, and I would recommend manually setting the height of that text element container just to be sure nothing will break in edge cases. Then, the .img-container gets height: calc(100% - 19px) and it works as expected.

html, body {
  height: 100%;
  overflow: hidden;
  margin: 0px;
}
div.container {
  height: 60%;
  border: 2px solid green;
  box-sizing: border-box;
}
div.rest {
  height: 40%;
  border: 2px solid red;
  box-sizing: border-box;
}
div.img-container {
  height: 100%; /* this is wrong, but what do I do? */
}
div.img-container img {
  max-height: 100%;
  max-width: 100%;
  height: auto;
  width: auto;
  opacity: 0.5;
}

/* 
  ADDED CODE BELOW
*/

/* optional, just to be safe */
.header {  
  height: 19px;
}

/* overrides .img-container from above */
.img-container {
  height: calc(100% - 19px) !important;
}
<html>
<body>
<div class="container">
<div class="header">hieronymus bosch last judgement</div>
<div class="img-container"><img src="https://i.imgur.com/TT6drhn.jpg"></div>
</div>
<div class="rest">
<h1>blah blah blah</h1>
</div>
</body>
</html>
Lewis
  • 4,285
  • 1
  • 23
  • 36
  • how can I use flex? I tried that, with the parent element having `display: flex` and the child elements having `flex: 1` but it didn't seem to work... I don't know why I can't seem to get the hang of flex. I don't know the height of the header row in advance, so `calc()` seems iffy to me. – Jason S Feb 14 '20 at 02:50
  • 1
    You'd need to set all of the elements to `display: flex`, use `flex-direction: column`, and set the sizes of the boxes in relative terms. [See this guide.](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) – Lewis Feb 14 '20 at 02:53
  • As soon as you're thinking in terms of "fill the _remaining_ space" in CSS, generally, you are talking about a _flexible_ box, and need to use `flex` unless you hack around it with `calc`. – Lewis Feb 14 '20 at 02:55
  • huh, ok. I can't grok some of these CSS concenps for some reason, which is weird because I get the corresponding concepts in TeX like boxes and glue and `hfil` and all that. – Jason S Feb 14 '20 at 03:58
  • @jpetty wrote an example out for you using `flex`! It's a newer standard, and it's just for situations like this where the fixed pixel size of something might change. Flex lets you do stuff like, "draw two boxes, one twice as big as the other" or "fill the remaining space." It just means "flexible." – Lewis Feb 14 '20 at 04:02
  • no, I get that -- like I said, TeX has `hfil` and `hfill` and stretchable glue and allows you to set weighting factors for how the blank space is allocated. What I can never seem to get is how the rules work like they do, with absolute/relative interacting with display:block/display:inline interacting with display:flex parents, it all seems to muddle together confusedly. – Jason S Feb 14 '20 at 04:06
  • Yeah, it's a little messy - `display: block` and `display: flex` are kind of opposites, they're sort of opposite "modes". I'm not trying to just bore you with entry level resources, but the w3schools article isn't bad for an overview: https://www.w3schools.com/css/css_positioning.asp – Lewis Feb 14 '20 at 04:11
0

With Flex, you may use the flex property and overflow (or min-height). Example:

html, body {
  height: 100vh;
  margin: 0px;
  display:flex;
  flex-direction:column;  
}
div.container {
  flex:6;/* instead height:xx% */
  border: 2px solid green;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow:hidden; /* or min-height:0 if scroll is needed */
}
div.rest {
  flex:4;/* instead height:xx% */
  border: 2px solid red;
  box-sizing: border-box;
}
div.img-container {
  flex: 1;
  min-height:0; /* or overflow:hidden; */
}
div.header {
  min-height:1.6em; /* if you need something alike ?? */
}
div.img-container img {
  max-height: 100%; 
  opacity:0.5;
}
<html>
<body>
<div class="container">
<div class="header">hieronymus bosch last judgement</div>
<div class="img-container"><img src="https://i.imgur.com/TT6drhn.jpg"></div>
</div>
<div class="rest">
<h1>blah blah blah</h1>
</div>
</body>
</html>
Lewis
  • 4,285
  • 1
  • 23
  • 36
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129