Putting CSS Footers in Their Place

Error message

  • Deprecated function: The each() function is deprecated. This message will be suppressed on further calls in menu_set_active_trail() (line 2405 of /home1/markspap/public_html/kathy/includes/menu.inc).
  • Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of /home1/markspap/public_html/kathy/includes/common.inc).
September 2, 2005

One of the most difficult things I've encountered in CSS layouts is footers. They're hard to understand, and even when you do figure them out, that doesn't guarantee you're always going to know how to get them to behave.

When you float or position columns, footers are particularly unruly. They don't always end up where you want them—neatly at the end of the page, as well as at the end of all columns. Fortunately, some pretty bright minds have already tackled the problem and come up with some good solutions for handling footers.

The Almost Sure-Fire Fix

A common problem is a two-column layout with one or both columns "floated" into position. Usually you can stick a clear: both; onto the #footer rule, and everything's fine...until that page where there's not much going on. When both columns are shorter than the browser window, the footer suddenly rides up into the middle of your page and you're pulling out your hair.

Luckily, there's a pretty easy fix for this. To get the footer to behave, you need to position it at the bottom of both the browser window and the document. To position the footer at the bottom of the document, wrap the columns and the footer in an all-encompassing div and use absolute positioning to stick the footer at the bottom:

#wrapper {
    position: absolute;
    top: 0;
    left: 0;
}

#footer {
    position: absolute;
    bottom: 0;
}

Now you need to ensure that the document (i.e., your new div) is always at least as long as the browser window. To do this, set the min-height of the document:

html, body, #wrapper {
    min-height: 100%;
    width: 100%;
    height: 100%;
}

html>body, html>body #wrapper {
    height: auto;
}

The Exception

This works well in most cases, I've found...except for one. When one of your columns is positioned absolutely and is a good deal longer than the other column(s), all bets are off. It just so happens that I was working on a layout like this last night. Here's the simplified markup:

<div id="header">
  <p>Header.</p>
</div>

<div id="content">
  <p>Content.</p>
</div>

<div id="sidenav">
  <p>Sidenav links.</p>
</div>

<div id="footer">
  <p>Footer.</p>
</div>

That's simple enough. Now say that, for one reason or another, you have to set the #sidenav with absolute positioning, like so:

#header {
    width: 100%;
    height: 100px;
}

#content {
    margin-right: 240px;
}

#sidenav {
    position: absolute;
    top: 100px;
    right: 0;
    width: 230px;
}

#footer {
    width: 100%;
}

So far, so good. As long as the #content is longer than the #sidenav on every page, you can use the "almost sure-fire" fix above to position the #footer precisely. However, if the #sidenav is very long (or the #content very short), you're going to run into problems. And I do mean problems.

Because the #sidenav is positioned absolutely, it's been removed from the flow of the document. In effect, the #footer acts like #sidenav isn't there—as though it doesn't even exist. It only pays attention to the #content, and will always position itself after that column.

 

Yikes is right, because the handy sure-fire fix from above doesn't work in this case, either. Why? Because setting the document's min-height at 100% sets it to the bottom of the #content only—remember, #sidenav's been removed from the flow of the document and, for all intents and purposes, doesn't exist (except to screw up our layout). Min-height doesn't pay attention to it, either.

 

Now, truth be told, there are several more or less complex fixes for this situation. But I had one additional problem: I couldn't change the markup, either by adding or removing markup or by moving content (no, I'm not working on a CSS Zen Garden design, but that's all I'm saying for now). In any case I really dislike adding extra markup to get CSS to work. I prefer to find a way to fix things using the existing semantic markup, if at all possible.

But I'll admit I was beginning to doubt it was possible. I even considered a bunch of ways to float #sidenav. Nothing worked.

Dave Shea's Miracle Cure

Then, lo and behold! [sun rising pinkish in the horizon and a swell of violins], I stumbled across Dave Shea's miraculous Min-Height Fix. Yes, miraculous, I say—or it sure seemed so at 11:30 last night.

Shea found a way to get all browsers to recognize a defined min-height and play nice. It's a beautiful thing. He explains it so well, I won't try to summarize but will just direct you to the article.

How does this fix our #footer problem? If you know the approximate length of your absolutely positioned column (#sidenav in this case), you can set the #content to that min-height using Shea's fix and, wa-la!, your #footer behaves like a #footer ought to. As luck would have it, I did happen to know my #sidenav was a whooping 1900px. I gave it a little room to grow and ended up with 150ems. It does require a few bits of extra markup, but I was able to minimize even that by using an existing selector.

The only change I made to Shea's code was to change pxs to ems. That way if a user enlarges the text, the min-height of #content will enlarge along with it.

Thank you, Mr. Shea, from my kids, who would like you to know that I am no longer "grumpy, grumbly Mommy."