I'm attempting to build a layout that allows a flexible height header and footer, with a section in the middle consuming the remaining space. Any overflow in the middle should give a scroll bar just for this middle section.
The code I have that works fine for Safari and Chrome is:
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
}
.l-fit-height {
display: table;
height: 100%;
}
.l-fit-height > * {
display: table-row;
height: 1px;
background-color: red;
}
.l-fit-height-expanded {
height: auto;
background-color: blue;
display: table-row;
}
.l-scroll-content {
height: 100%;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="l-fit-height">
<section>
Header
</section>
<section class="l-fit-height-expanded">
<div class="l-scroll-content">
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
</div>
</section>
<section>
Footer
</section>
</div>
</body>
</html>
I cannot for the life of me figure out why the behaviour is different in Firefox. The content in the middle will expand height correctly, but will not shrink more than the height of the contents.
It's difficult to know what is the correct behaviour. Any ideas?
Edit
Similar example setup here: http://jsfiddle.net/t3mZF/
Interestingly if .l-fit-height-row-content
is changed to display: table-cell
then WebKit and Gecko exhibit the same behaviour or ignoring the overflow.
If display: block
is used then WebKit gives desired behaviour (a scroll bar and footer remains at bottom of view port), but Firefox refuses to add the scroll bars and instead pushes the footer off the bottom of the screen (scroll bar on view port - not the middle content).
I've also opened a bugzilla ticket
I've discovered with a couple of extra div's and a crucial display: inline-block
it is possible to use the absolute positioning trick Torben mentions, in Firefox as well. This means that fully flexible header and footer is possible as a pure CSS solution.
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
}
.l-fit-height {
display: table;
height: 100%;
}
.l-fit-height-row {
display: table-row;
height: 1px;
}
.l-fit-height-row-content {
/* Firefox requires this */
display: table-cell;
}
.l-fit-height-row-expanded {
height: 100%;
display: table-row;
}
.l-fit-height-row-expanded > .l-fit-height-row-content {
height: 100%;
width: 100%;
}
.l-scroll {
/* Firefox requires this to do the absolute positioning correctly */
display: inline-block;
overflow-y: auto;
position: relative;
}
.l-scroll-content {
position: absolute;
top: 0;
bottom: 0;
}
</style>
</head>
<body>
<div class="l-fit-height">
<section class="l-fit-height-row">
<div class="l-fit-height-row-content">
Header
</div>
</section>
<section class="l-fit-height-row-expanded">
<div class="l-fit-height-row-content l-scroll">
<div class="l-scroll-content">
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
</div>
</div>
</section>
<section class="l-fit-height-row">
<div class="l-fit-height-row-content">
Footer
</div>
</section>
</div>
</body>
</html>
Hope this helps
I think you ran into two combined problems here:
Table-dimensions aren't as fixed as you might expect it from a normal div. Tables usually grow with their content and don't care much about their height property if the content is too big.
Therefore you have to exclude the content of the scroll-box from the content-size calculation by position it absolutely. All that's now left to do is to set the position and size the scroll-box to fill the complete space of the expanded table-row. However, this raises another issue:
The straightforward solution would be to set position:relative;
for the table-row and width:100%;height:100%;
for the scroll-box, but this won't work because most browsers ignore the position-property of table-rows. You could insert another div with display:table-cell;position:relative;
between the table-row and the scroll-box, but this will only work for all browsers except Firefox, since Firefox doesn't allow any relative positioning within a table.
Firefox however allows relative positioning for the table itself, so the trick is to set position:relative;
for the table-div and set the size and the position of the scroll-box according to the dimensions of the whole table.
As you might expect this comes with some limitations if you want to use a pure CSS solution, but there are some anyway. Apart from that there's also an elegant way to solve the problem with JavaScript, which I've included in the code, too.
The code below includes three ways to solve your problem. Two are pure CSS solutions and one, which uses JavaScript. I've tried to change you code as less as possible and commented all changes, so I think you'll find you way through the lines. Leave a comment if you still have questions.
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
}
.l-fit-height {
display: table;
height: 100%;
/* NOTE Use the table as refernece for absolute positioning. This */
/* is the only place where Firefox accepts the position property */
/* within a table. */
position: relative;
}
.l-fit-height > * {
display: table-row;
height: 1px;
background-color: red;
}
.l-fit-height-expanded {
/* NOTE Set the important flag to override the generic setting */
height: auto !important;
background-color: blue;
display: table-row;
}
.l-scroll-content {
/* NOTE We will set the height attribute later */
/* height: 100%; */
overflow-y: auto;
/* INFO Prevent the table from growing in height */
position: absolute;
/* INFO The content should span the full width */
width: 100%;
}
/* INFO Since Firefox only allows absolute positioning relative to the */
/* whole table, setting the correct height is a bit tricky. I've */
/* found two pure CSS solutions which work, but both have their */
/* glitches. If JavaScript is ok for you, there's another solution */
/* which I would recommend. */
/* INFO Example for percentage height footer and header. This sometimes */
/* leaves one or two pixels between the scroll-box and the footer. */
/* REMOVE THIS LINE TO ACTIVATE
.l-fit-height > * {
height: 20%;
}
.l-scroll-content {
height: 60% !important;
}
/**/
/* INFO Example for fixed height footer and header. Unfortunately this */
/* makes the header and footer-content unclickable, but I'm quite */
/* sure this could be solved by setting a z-index. */
/* REMOVE THIS LINE TO ACTIVATE
.l-fit-height > * {
height: 40px;
}
.l-scroll-content {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
height: 100%;
border-top: 40px solid transparent;
margin-top: -40px;
border-bottom: 40px solid transparent;
margin-bottom: -40px;
}
/**/
</style>
<script type="text/javascript">
// INFO Example how to fix the problem with JavaScript. This works for
// all kinds of heights (flexible, percentage and fixed) and has no
// issues except for the need of JavaScript to see a correctly
// layouted page.
//* REMOVE THE FIRST SLASH TO DEACTIVATE
function fixScrollContentHeight() {
var nodes = document.getElementsByClassName('l-scroll-content');
for (var i = 0; i < nodes.length; i++)
nodes[i].style.height = nodes[i].parentNode.offsetHeight+'px';
}
window.onload = fixScrollContentHeight;
window.onresize = fixScrollContentHeight;
//*/
</script>
</head>
<body>
<div class="l-fit-height">
<section>
Header
</section>
<section class="l-fit-height-expanded">
<div class="l-scroll-content">
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
<p>Foo</p>
</div>
</section>
<section>
Footer
</section>
</div>
</body>
</html>
PS: I wasn't able to test my code with IE, but I think it should work there, too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With