The reason why things do not work the way you expect is simply because max-height
does not set the height of the containing div. All it does is, as its name implies, set a maximum limit to the height of the div.
Here's a quote from the W3 CSS2 specification on how the percentage heights are calculated on block elements. This might help to shed some light on the matter:
The percentage is calculated with respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'.
In your case the height of the containing div is not set explicitly and depends on content height, because when you set the max-height
of the containing div to 90%
, and there is not enough content to stretch the containing div to 90%
of the height of its own containing element, the height of the containing div will be less than 90% of the height of its own containing element.
An attempt to explain what I believe is happening
The browser renders the containing div with an initial height of auto
, which computes to 0px
as there is no content yet. Along comes the contained div which wants to be rendered with a height of 100%
of the height of its containing block, the browser realizes that this is ridiculous, as 100%
of 0px
is exactly 0px
again. So it decides to set the height of the contained div to auto
instead. If it didn't do so, then the contained div would never be visible, because no matter what happens next, 100%
of the containing block's height of 0px
is always going to be 0px
. Remember that the browser is trying to stick to this part of the rule quoted above:
The percentage is calculated with respect to the height of the generated box's containing block
ONLY NOW along come some more div's which would like to be rendered inside the contained div. At the moment when the previous decisions were made, the browser didn't yet know about these div's, they're a bit late to the party. If the browser was then to backtrack and fix itself up after it had rendered those div's, it would effectively be breaking the part of the rule quoted above. As it would indirectly* be setting the percentage height of the contained div based on the height of its contents.
Because of this the W3 specification people have come up with the second part of the rule. Which lets the browser decide to set the height of the contained div to auto
if the height of its containing div is not set (and therefore defaults to auto
).
So you could say that those late div's are lucky that the browser has taken some precautions and is still able to render those div's, as it has been preemptive and has set the height of the contained div to auto
to accommodate for latecomers.
*by calculating the height of the containing div based on the height of the contents of the contained div, and then basing the percentage height of the contained div on this value.
In conclusion
Browsers are just sticking to the W3 specification, which is a good thing. Your first fiddle works because the browser makers are adhering to the specification, and your second fiddle doesn't work for the exact same reason.
The solution
You can only fix your issue by making sure that the div which you want to have a height of 90%
of the browser window is a direct descendant of a div which has its height set to 100%
of the browser window. If the ancestor div is not absolutely placed, every ancestor of the div, all the way up to the html
document element, would also have to have a height of 100%
set on itself.
The line above is true, except if an ancestor is encountered which is absolutely placed (which would take it out of the regular document flow), without this ancestor itself having an ancestor with position: relative
set (which would force its absolute positioning to be based on the position of its relatively positioned parent instead of on the height of the browser window), and this ancestor is set to be the height of the browser window (using top: 0px; bottom: 0px;
). In that case the running up the DOM tree will stop at the absolutely positioned ancestor.