I had to redo this again to support a site with relative links in it. I have a layout that handles the equal height columns and also relative links (and drop shadows to boot).
I'm using a method for sizing the height of elements that I've not seen previously and I think perhaps I can submit it to A List Apart…
I'm going to retract the submission because I was unaware that a xml
declaration turned on IE's quirks mode. This layout only works in quirks mode and not even in that in IE7. A better, though hackish solution, is absurdly tall div
s.
I've redone my sample using the overflow method.
Three column layouts are all the rage nowadays. It's a great way to keep your navigation bits accessible and keep the content from getting really wide and looking all cluttered. Most of time attention is paid only to the widths of the columns, but heights can be important as well. Say, for instance, that you want a navigation bar on the side to go all the way to the bottom of the page. There are seveal methods that are currently being employed to achieve this effect:
One of the most common methods is to put 100% height on the #navbar
, body
and html
.
html, body, #navbar { height: 100%; }
html, body { margin: 0; }
#navbar, #content { float: left; }
This works well enough so long as:
#navbar
stops at the edge of the initial view.#navbar
will force scrolling regardless of the content's length.Another method is to fake the background on the side column by either using a background image on the containing element. The problem with this is fixes the layout sizes and requires image editing to play with the appearance. This was the method used on this site when I got it and it kept irritating me.
Similarly, you could make the side borders of the containing element really big, but this prevents you from using images and specifying widths as percentages.
The best method I've seen is div
s with long padding and negative bottom margins. They essentially extend as far as the content possibly could and are contained by an element with overflow: hidden
to crop the bottom. Honestly, this works well and the only reason I don't especially like it is my layout is two nested three column layouts (the outer one is drop shadows) and the markup was getting convoluted.
What I really want is to be able to say is: "This element should be the same height as the containing element." I was searching the internet for a method to do this and ran across this statement at quirksmode:
"[I]f you want to make an element as high as the entire page (whatever this height may be) you're out of luck. Although it might seem simple the specs (and the browsers' unthinking conformance) make this completely impossible.
"The spec says: 'If the height of the containing block is not specified explicitly (i.e., it depends on content height), the value is interpreted like
auto
.'"
That's pretty daunting. I like quirksmode and there's lots of good information over there. In fact, when I read that, I gave up and started looking for workarounds. Fortunately however, I have a really bad memory. I ran across the problem a little while later and, before I remembered it is impossible, solved the problem.
As is often the case, the solutions are completely different for IE and CSS compliant browsers. First, to deal with IE:
The solution in IE arises from what happens when an element overflows its boundaries. According to the specification it should simply extend past the bottom unless the overflow property specifies otherwise. In IE the containing block is reflowed to be large enough to contain the content.
If you had the following HTML:
<div class="outer"><div class="inner"></div></div>
With the following style applied:
.inner, .outer { width: 20px; height: 20px; border: 2px solid; } .outer { border-color: #68A; background-color: #ACF; } .inner { border-color: #A86; background-color: #FCA; margin: 5px; }
In your browser, this is what it looks like:
In a CSS compliant browser, it would display like this:
Whereas in IE, the outer box is reflowed and it comes out like this:
If you've been following closely, you may be wondering why the #navbar
stopped short on example with long content. The body
had a height of 100% which was too short. Why didn't the box reflow to encompass the content? Apparently the reflowing doesn't work for the body
. If the page content is wrapped in a div
however and that div
's height is set to 100%, it will reflow as expected:
html, body, #body, #navbar { height: 100%; }
html, body { margin: 0; }
#navbar, #content { float: left; }
Sizing to the height of the container in a CSS compliant browser simply takes advantage of the fact that table cells are the height of the containing row. In some browsers (Firefox, Opera), children of elements with a display
of table-cell
will render according to the CSS specification. I won't go into the details, but if you are curious, you can see my invalidated bug report. The end effect is that you'll sometimes get strange top margins if you leave the vertical-align
property as baseline
.
html, body { height: 100%; margin: 0; }
body { display: table; }
#navbar { display: table-cell; vertical-align: top; }
These two methods are similar enough that they can almost be combined without any CSS hacks. The one issue is that in the IE layout the #navbar
is float
ed. If this is done in the CSS layout the element is pulled out of the normal flow and the height is only that of the contained elements. I personally like to use conditional comments rather than parsing bugs to handle IE specific stuff. Who knows, maybe IE 7 will actually work. Using these, it is painless to combine the two methods. Also, in case you were curious, this still fills the initial screen with less than a screenful of content. (This time I included the whole style tag to show the conditional comments.)
<style type="text/css">
html, body, #body, #navbar { height: 100%; margin: 0; }
#body { display: table; }
#navbar { display: table-cell; }
</style>
<!--[if lt IE 7]>
<style type="text/css">
#navbar { float: left; }
</style>
<![endif]-->
The layout that I have been working on converting to CSS needs this method in several places. The HTML looks like:
<div class="layout">
<!-- top shadow markup omitted -->
<div class="shadowed">
<div class="left shadow"></div>
<div class="body">
<div class="header"></div>
<div class="content">
<div class="leftcol"></div>
<div class="centercol"></div>
<div class="rightcol"></div>
</div>
<div class="footer"></div>
</div>
<div class="right shadow"></div>
</div>
<!-- bottom shadow markup omitted -->
</div>
And I want it to render like this:
There are several columns in this layout whose heights are important:
.left.shadow
and .right.shadow
) need to be the same height as the .header
, .content
, .footer
.content
(.leftcol
, .centercol
and .rightcol
) all need to be the same heightThough they look very similar, the layout for these two sets of columns is different. I will discuss each in turn.
They are often maligned by the web's technorati, but the client wanted drop shadows on this layout. This is a description of how the columns on the center section were done. (The contents of the .shadowed
div
.) There's really not all that much to them. The bits for specifying the sizes of the shadows are shared by both layouts:
.right { width: 15px; }
.left { width: 10px; }
The CSS version is pretty simple. Though it goes counter to the CSS adherents thinking, all you have to do is look at the layout as if it was in a table:
.layout { display: table; width: 80%; margin: auto; }
.shadow.top, .shadowed, .shadow.bottom
{ display: table-row; }
.left, .body, .right { display: table-cell; }
The IE version is a bit more complicated. You might have noticed that the width of .layout
is a percentage. This means it isn't possible to know the actual pixel width. The drop shadows however, do have fixed pixel widths. Most three column layouts float all three columns and either specify all three as percentages or all three as fixed widths. There are a couple different ways to handle this mixed scenario. One would be to float the columns to the left and right and allow the content the fill in the middle. The problem with that is it requires both .left.shadow
and .right.shadow
to come before .body
, and for the CSS layout .right.shadow
needs to come after .body
.
Instead of using floats, I took advantage of the fact that IE reflows affect absolutely positioned elements as well as floats. So the shadows are just absolutely positioned and there is an appropriate margin on the .body
.
#layoutsample { text-align: center; }
.shadowed { height: 0; position: relative; }
.left { position: absolute; left: 0; }
.right { position: absolute; top: 0; right: 0; }
.body { padding: 0 15px 0 10px; }
.shadow.left, .shadow.right { height: 100%; }
The content columns are more a more traditional three column layout (in IE at least). Again, the styles controlling the widths are shared:
.leftcol, .rightcol { width: 25%; }
.centercol { width: 50%; }
The CSS compliant markup is pretty much identical:
.content { display: table; width: 100%; }
.leftcol, .rightcol, .centercol { display: table-cell; }
And the version for IE is pretty simple too:
.content { height: 0; }
.leftcol, .rightcol, .centercol
{ float: left; height: 100%; }
I developed this layout working in Firefox 1.5.03 in XP, Fedora Care 4 and OSX. Then I ported it over to Internet Explorer 6.0, Safari 2.0.3 and Konqueror 3.5.2-0.1.fc4. During the porting process I ran across several bugs. Most of them don't warrant mentioning and are noted in comments in the stylesheet, but one affects the HTML. There is a one pixel space under the footer in Firefox XP that doesn't appear in any other browser including Firefox OSX. There's no way I know of to catch that without javascript, so I just whacked a pixel off of all the browsers except IE.
Doing this goes back to the conditional comments which can be used to exclude content from IE as well as include it:
<!--[if !IE]>--> <style type="text/css"> .footer { margin-bottom: -1px; } </style> <!--<![endif]-->
The sample above is laid out with this layout. The operative styles are:
drop_shadow.css
: the basic layout styledrop_shadow_ie.css
: the IE specific elements of the layoutdrop_shadow_non_ie.css
: the elements of the layout hidden from IE. (This is only the footer bug.)