249

I'm trying to understand what appears to be unexpected behaviour to me:

I have an element with a max-height of 100% inside a container that also uses a max-height but, unexpectedly, the child overflows the parent:

.container {
  background: blue;
  padding: 10px;
  max-height: 200px;
  max-width: 200px;
}

img {
  display: block;
  max-height: 100%;
  max-width: 100%;
}
<div class="container">
  <img src="http://placekitten.com/400/500" />
</div>

This is fixed, however, if the parent is given an explicit height:

.container {
  background: blue;
  padding: 10px;
  max-height: 200px;
  max-width: 200px;
  height: 200px;
}

img {
  display: block;
  max-height: 100%;
  max-width: 100%;
}
<div class="container">
  <img src="http://placekitten.com/400/500" />
</div>

Does anyone know why the child would not honour the max-height of its parent in the first example? Why is an explicit height required?

johannchopin
  • 13,720
  • 10
  • 55
  • 101
iamkeir
  • 3,222
  • 2
  • 21
  • 22
  • 1
    Have browsers changed default behavior for this? Because on my current version of Google Chrome, both fiddles above actually do not show the child overflowing for me. Are newer browsers defaulting to box-sizing: border-box or something? – David Mays Jan 20 '21 at 16:46

11 Answers11

365

When you specify a percentage for max-height on a child, it is a percentage of the parent's actual height, not the parent's max-height, oddly enough. The same applies to max-width.

So, when you don't specify an explicit height on the parent, then there's no base height for the child's max-height to be calculated from, so max-height computes to none, allowing the child to be as tall as possible. The only other constraint acting on the child now is the max-width of its parent, and since the image itself is taller than it is wide, it overflows the container's height downwards, in order to maintain its aspect ratio while still being as large as possible overall.

When you do specify an explicit height for the parent, then the child knows it has to be at most 100% of that explicit height. That allows it to be constrained to the parent's height (while still maintaining its aspect ratio).

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 6
    This is a useful explanation, especially mixed with my friend's input: "max-height: 100% (child) of max-height: 100% (parent) = 0 - this is why your child isn't honouring its parent value". – iamkeir Jan 11 '13 at 10:09
  • 31
    I'm not sure which answer to accept - this one answers the 'why'. – iamkeir Jan 11 '13 at 10:12
  • 2
    I only answered the 'why' because I'm not sure what your goal is as it's not explicitly mentioned. Maybe if you elaborate on that, I can answer it. – BoltClock Jan 11 '13 at 10:14
  • 2
    Accepted this as it answers my actual question, thank you. Please note @Daniel's answer below offers a fix. – iamkeir Jan 11 '13 at 10:15
  • 5
    In my case doing `display: flex; flex-direction: column` solved the problem and added the scrollbar that I wanted. – xdevs23 Apr 23 '19 at 07:21
  • To save anyone some trouble, using an explicit `max-height` on the parent alone does _not_ mitigate this issue. Seen in @Daniel's answer, inheriting an explicit height is the only thing that has the actual semantics of "100% of parent height." With `calc()` `var()` and SCSS variables, it can even be palatable in a real app! – John Neuhaus Dec 11 '20 at 22:47
  • NO, the computed value says the parent has a height of 200px. – WeiChing 林煒清 Jul 07 '22 at 15:21
  • @WeiChing 林煒清: Whoa, relax. I'm not sure who you're responding to as none of us here said the computed value of height was anything other than 200px. I only talked about the computed value of max-height. – BoltClock Jul 07 '22 at 15:24
111

.container {
  background: blue;
  padding: 10px;
  max-height: 200px;
  max-width: 200px;
  float: left;
  margin-right: 20px;
}

.img1 {
  display: block;
  max-height: 100%;
  max-width: 100%;
}

.img2 {
  display: block;
  max-height: inherit;
  max-width: inherit;
}
<!-- example 1  -->
<div class="container">
  <img class='img1' src="http://via.placeholder.com/350x450" />
</div>

<!-- example 2  -->

<div class="container">
  <img class='img2' src="http://via.placeholder.com/350x450" />
</div>

I played around a little. On a larger image in firefox, I got a good result with using the inherit property value. Will this help you?

.container {
    background: blue;
    padding: 10px;
    max-height: 100px;
    max-width: 100px;
    text-align:center;
}

img {
    max-height: inherit;
    max-width: inherit;
}
Daniel
  • 4,816
  • 3
  • 27
  • 31
  • 4
    I'm not sure which answer to accept - this one answers the 'fix'. – iamkeir Jan 11 '13 at 10:14
  • Accepted @BoltClock for answering the why, but thank you for offering a fix. – iamkeir Jan 11 '13 at 10:15
  • 1
    In addition to @BoltClock, my answer is exactly doing what he says. CSS is just telling the computer how to handle an element, but after computations it will have delimited all values down to the basic needed values for rendering, like height, width, color, coordinates, ... The inherit property is telling the img to take the "computed" value of the parent. So you get the computed values for height and width in you max-height and max-width css properties. – Daniel Jan 12 '13 at 20:07
  • 3
    This does not work when `max:height: 100%` instead of a fixed height: http://jsfiddle.net/umbreak/n6czk9xt/1/ – Didac Montero Apr 17 '15 at 15:01
  • @DidacMontero - It does work, but does not render the result you want. The "inherit" setting for the image refers to the parent. Only a static setting will be copied exactly, where your percentage setting is not defined. – Daniel Apr 18 '15 at 16:15
  • @Daniel, So there is a way that my jsfiddle example work as expected (the IMG resizes but do not overflow over the first div)? Probably is better that I open another complete issue with that, since is not totally the same question – Didac Montero Apr 18 '15 at 23:25
  • @DidacMontero - It is not clear on what you want to achieve. I was merely stating that the above solution does work, but that it may not be suitable for what you want. The "inherit" value will take over the set value of the parent once it gets "computed". You just break the value of the parent by applying a percentage, which the compiler interprets different. Maybe this will help you some, but otherwise I would advise to open a new thread. http://jsfiddle.net/djwave28/n6czk9xt/11/ – Daniel Apr 19 '15 at 19:42
  • 2
    I cannot declare width and height as fixed cause my layout is responsive `dispaly: flex;`. I opened a new thread: I opened I new thread: http://stackoverflow.com/questions/29732391/make-img-responsive-and-fill-into-a-div However, in the worse case I could use Jquery to fetch the img real width and height and set them in the parent image's div. – Didac Montero Apr 20 '15 at 07:44
  • Thank you, that is great anwser! At least what I was looking for `inherit` is something i wasnt thinking of – Szymon Oct 29 '21 at 12:14
29

Instead of going with max-height: 100%/100%, an alternative approach of filling up all the space would be using position: absolute with top/bottom/left/right set to 0.

In other words, the HTML would look like the following:

<div class="flex-content">
  <div class="scrollable-content-wrapper">
    <div class="scrollable-content">
      1, 2, 3
    </div>
   </div>
</div>

.flex-content {
  flex-grow: 1;
  position: relative;
  width: 100%;
  height: 100%;
}

.scrollable-content-wrapper {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  overflow: auto;
}

.scrollable-content {
    /* Add styling here */
}

Try it below:

.flex-content {
  flex-grow: 1;
  position: relative;
  width: 100%;
  height: 100%;
}
.scrollable-content-wrapper {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  overflow: auto;
}
html {
  height: 50%;
  width: 50%;
}
body {
  height: 100%;
  width: 100%;
}
.parent {
  height: 100%;
  outline: 1px solid red;
}
<html>
<body>
  <div class="parent">
    <div class="flex-content">
      <div class="scrollable-content-wrapper">
        <div class="scrollable-content" id="scrollable">
          1, 2, 3
        </div>
      </div>
    </div>
  </div>
  
  <button onClick="scrollable.innerText += '\nSome more text'" style="margin-top: 1rem;">Add Line</button>
  <p>
    The red outline represents the parent. Click above to add a line until overflow occurs to see that the size of the parent is not increased.
  </p>
</body>
</html>
Nate Levin
  • 918
  • 9
  • 22
Tianzhen Lin
  • 2,404
  • 1
  • 19
  • 19
  • 1
    This is the only solution that works if you need the container to be able to scroll on overflow, and be the size of its parent without increasing the parent's height. Amazing. – Nate Levin Jul 13 '22 at 01:40
8

I found a solution here: http://www.sitepoint.com/maintain-image-aspect-ratios-responsive-web-design/

The trick is possible because it exists a relation between WIDTH and PADDING-BOTTOM of an element. So:

parent:

container {
  height: 0;
  padding-bottom: 66%; /* for a 4:3 container size */
  }

child (remove all css related to width, i.e. width:100%):

img {
  max-height: 100%;
  max-width: 100%;
  position: absolute;     
  display:block;
  margin:0 auto; /* center */
  left:0;        /* center */
  right:0;       /* center */
  }
Niente1
  • 89
  • 1
  • 1
5

You can use the property object-fit

.cover {
    object-fit: cover;
    width: 150px;
    height: 100px;
}

Like suggested here

A full explanation of this property by Chris Mills in Dev.Opera

And an even better one in CSS-Tricks

It's supported in

  • Chrome 31+
  • Safari 7.1+
  • Firefox 36+
  • Opera 26+
  • Android 4.4.4+
  • iOS 8+

I just checked that vivaldi and chromium support it as well (no surprise here)

It's currently not supported on IE, but... who cares ? Also, iOS supports object-fit, but not object-position, but it will soon.

Community
  • 1
  • 1
Luis Sieira
  • 29,926
  • 3
  • 31
  • 53
  • Nice alternative! I imagine flex-box could potentially assist too, if graceful degradation in IE is acceptable. – iamkeir Jul 06 '15 at 13:32
  • This is unrelated to the actual question, and the provided example is unrelated to the problem described – vsync Jan 23 '23 at 20:39
3

Here is a solution for a recently opened question marked as a duplicate of this question. The <img> tag was exceeding the max-height of the parent <div>.

Broken: Fiddle

Working: Fiddle

In this case, adding display:flex to the 2 parent <div> tags was the answer

KidBilly
  • 3,408
  • 1
  • 26
  • 40
2

Maybe someone else can explain the reasons behind your problem but you can solve it by specifying the height of the container and then setting the height of the image to be 100%. It is important that the width of the image appears before the height.

<html>
    <head>
        <style>
            .container {  
                background: blue; 
                padding: 10px;
                height: 100%;
                max-height: 200px; 
                max-width: 300px; 
            }

            .container img {
                width: 100%;
                height: 100%
            }
        </style>
    </head>
    <body>
        <div class="container">
            <img src="http://placekitten.com/400/500" />
        </div>
    </body>
</html>
Kevin Brydon
  • 12,524
  • 8
  • 46
  • 76
  • If they want the container to become smaller if the image is smaller than 200x200, then yes, it has to be done with min-height/max-height. – cimmanon Jan 10 '13 at 16:56
2

The closest I can get to this is this example:

http://jsfiddle.net/YRFJQ/1/

or

.container {  
  background: blue; 
  border: 10px solid blue; 
  max-height: 200px; 
  max-width: 200px; 
  overflow:hidden;
  box-sizing:border-box;
}

img { 
  display: block;
  max-height: 100%; 
  max-width: 100%; 
}

The main problem is that the height takes the percentage of the containers height, so it is looking for an explicitly set height in the parent container, not it's max-height.

The only way round this to some extent I can see is the fiddle above where you can hide the overflow, but then the padding still acts as visible space for the image to flow into, and so replacing with a solid border works instead (and then adding border-box to make it 200px if that's the width you need)

Not sure if this would fit with what you need it for, but the best I can seem to get to.

2

A good solution is to not use height on the parent and use it just on the child with View Port :

Fiddle Example: https://jsfiddle.net/voan3v13/1/

body, html {
  width: 100%;
  height: 100%;
}
.parent {
  width: 400px;
  background: green;
}

.child {
  max-height: 40vh;
  background: blue;
  overflow-y: scroll;
}
Tiberiu Popescu
  • 4,486
  • 2
  • 26
  • 38
1

Containers will already generally wrap their content nicely. It often doesn't work as well the other way around: children don't fill their ancestors nicely. So, set your width/height values on the inner-most element rather than the outer-most element, and let the outer elements wrap their contents.

.container {
    background: blue;
    padding: 10px;
}

img {
    display: block;
    max-height: 200px;
    max-width: 200px;
}
Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80
1

http://jsfiddle.net/mpalpha/71Lhcb5q/

.container {  
display: flex;
  background: blue; 
  padding: 10px; 
  max-height: 200px; 
  max-width: 200px; 
}

img { 
 object-fit: contain;
  max-height: 100%; 
  max-width: 100%; 
}
<div class="container">
  <img src="http://placekitten.com/400/500" />
</div>
etb
  • 21
  • 2