This script is not yet ready for prime time. It works fine on this page, except in Explorer 5.0 Windows and Safari 1.0, but on more complicated pages it may misfire in a number of subtle ways which I haven't investigated yet.
The tiny script presented on this page begins to solve a common CSS problem: stretching three floated columns to the height of the highest column. The script isn't yet ready for real use, but it nicely illustrates the principle of minimal CSS enhancement.
CSS has a few very annoying blanks that may frustrate your attempts to create a nicely designed pure XHTML/CSS page. I'm thinking of the lack of vertical alignment, or the stretching up of several floated columns to the height of the highest one.
In my opinion JavaScript can lend a helping hand. However, we should avoid the interminable and useless DHTML libraries that scourged web development in earlier, less enlightened eras. Instead, we should establish the principle of minimal CSS enhancement.
The purpose of such a script is to help smooth out rough edges on CSS by applying the minimal necessary change to achieve an effect. Ideally, this means tweaking only one single style value, as this example script does, and leaving the rest to normal, simple CSS.
Once we've written such a simple script, accessibility issues become manageable. If the script doesn't work the CSS problem will not be solved, but since these solutions usually fall in the nice-to-have category and are in no way necessary for the basic functioning of the page, I don't see any fundamental reason not to use JavaScript to help CSS along.
For a more fundamental and extensive discussion, see Bobby van der Sluis's Presentational JavaScript article.
This example doesn't work under all circumstances, and therefore it is not ready for prime time. Please read all browser notes below on this page before mailing me about it.
This is a classic example of a three column layout. The left and right columns have only a little content (in a real website they'd have more than just one line of text, but still less than the main column).
Ideally, all these three columns should have the same height. Of themselves, the left and
right columns have "as much height as they need" (ie. height: auto
) for reasons
I'll explain later on.
Nonetheless graphic designers are less than thrilled by this lack of stretching, and rightly so. Pure CSS just doesn't give us the possibility to solve this problem. Do we embrace the purity argument and leave the CSS as it is, or do we use JavaScript to solve the problem?
The three column layout above is very simple:
<div id="testmain"> <div class="left"> This is the left column </div> <div class="mid"> [text] </div> <div class="right"> This is the right column </div> <div class="clearer"> </div> </div>
The HTML is styled as follows:
.left { float: left; background-color: #732264; width: 15%; color: #ffffff; height: 100%; } .mid { float: left; width: 50%; height: 100%; margin: 0 15px; } .right { float: right; background-color: #B0BDEC; width: 15%; height: 100%; } .clearer { clear: both; font-size: 1px; }
As you see I floated the three columns and gave each of them a width. I also gave
each of them a height: 100%
, and that's the key to the effect.
height: 100%
means: make the height 100% of that of the parent element.
In this case the parent element is <div id="testmain">
, and that's
where the problems start. Since I do not know in advance how much text the middle column
will contain, nor how wide the user's browser window will be, I cannot give this parent
element a height
(well, I could, but it'd be ugly in 99.9% of the cases).
Therefore the parent element has height: auto
, ie. "as much as you need".
The problem is that the CSS specification says that any block with a percentual height that
is contained by a block that has height: auto
should also get height: auto
.
In other words, the height: 100%
does not apply to naturally stretched blocks.
Superficially this is a very serious problem. To enable the height: 100%
we'd
have to give the container block a specific height
, but how do we know what
value it should have, when we don't know how much text the middle column contains and how
wide the user's window is?
The answer, of course, is reading out the real, rendered height by JavaScript
and setting the style.height
to this value:
function threeColumn() { var elm = document.getElementById('testmain'); elm.style.height = 'auto'; var x = elm.offsetHeight; elm.style.height = x + "px"; }
We read out the offsetHeight
of the containing block, which contains the
actual height the block has in the rendered page. We then add "px"
to this
value and paste it in style.height
.
Now, all of a sudden the container block has a specified height
, and therefore
the height: 100%
of the three columns kicks in. The three columns
stretch themselves to completely fill their container and the effect has been established.
As to the elm.style.height = 'auto';
, this line is necessary if the function
is called more than once. If we have to recalculate the height of the container, we should
first reset it to auto
, to allow it to stretch naturally, and read
out the new offsetHeight
only afterwards.
This script solves an annoying problem by tweaking one single style value and leaving the rest to regular CSS definitions. Obviously, if JavaScript is disabled the CSS value is not tweaked, but in that case we're not worse off than we were before. The page is not inaccessible if the three columns don't have equal height, it's just less beautiful.
Unfortunately the tiny script above is only a first approximation of a solution. Cross-browser reality is more obnoxious, to say the least.
Most of these problems can be solved by fundamental research into offsetHeight
and its changing, but at the moment I don't have time for it.
First of all we need a clearer element. As you see the last element inside the container is
<div class="clearer"> </div> .clearer { clear: both; font-size: 1px; }
All browsers except for Explorer require it. The reason is that without this element the container div contains only floated elements, which means that its normal height defaults back to 0. We don't want that, we want the container to stretch up as much as necessary so we can read out its actual height.
The clearer solves this problem for us. It is the last element in the container, it is not floated, hence it stretches up the container so that it actually contains the floater, and thus the three columns above it.
As to the single
: Mozilla requires it (don't ask me why). If
it's not there the trick doesn't work. I set the clearer's font size to 1px to hide the
as much as possible.
If you resize this page you'll see that the column height doesn't change. The obvious solution to this problem is
window.onresize = threeColumn;
However, this solution turns out to work only in Mozilla and Explorer Mac. Explorer Windows and Opera make the container larger and larger and larger with each resize, and I'm not yet sure why.
You'll notice that when the page becomes narrower, and the container block higher, Explorer Windows nicely changes the height of the columns, too. This does not happen when you make the page wider and the container block less high.
The cause is Explorer's peculiar interpretation of overflow: visible
. If
the content is too large for a block to contain, Explorer stretches up the block to contain
all content, even if the block has a set height
. Therefore it correctly
handles any increase (but not decrease) in container height without further
JavaScript aid.
Browser differences in box model will certainly cause serious problems. This example entirely avoids the issue by not applying any paddings or borders, but for real-world applications these problems must be solved.
If you call the script more than once, you'll notice that Opera doesn't behave right.
As far as I can see this is because it doesn't take the time to calculate the effect of
resetting the height to auto
before calculating the new offsetHeight
.
Therefore the offsetHeight remains what it was, and the columns do not react properly.
The same issue may bug Explorer on Windows if we call the script onresize
,
but I haven't yet investigated this.
You may note that in Opera the container element becomes slightly larger each time you run the script, even when you don't resize the browser window. I'm not yet completely sure of the cause, but in tightly designed pages this may become a problem.
When I tried this script in a page where one column contained a large image, Explorer added the height of this image to the height of the container element, and the columns became much too high. I don't yet know why, but it means that this technique is not yet usable in all pages.
The default styles of my example are very weird in Explorer 5.0 Windows. I assume this
is because of a problem in the handling of float
, but I haven't yet investigated
it. In any case the example doesn't work in this browser.