Objective We want a column on the left (sidebar) with a width specified in 'em' units* and a column on the right with a width specified in 'px' units. The left sidebar will be suitable for holding a scalable flyout menu that expands/contracts as the browser's default font-size is increased/decreased.
We also want a page header at the top of the page and a footer at the bottom of the page. The footer should be positioned at the bottom of the viewport/screen when the page is shorter than the screen but it should remain tied to the bottom of the page when the page is longer than the screen.
Outline of Method We use left- and right-floated divs for the side colums and an absolutely positioned div for the left sidebar background color. The fixed-width right column gets its background color from a background image. This 3-column layout is derived from the previous 2-column layout in which the 'content' div is first replaced by an 'innerWrapper' div. Then we insert a 'content' and a 'right' div floated to its right inside the 'innerWrapper' div. * You can use 'px' units in this tutorial if you prefer. We are using 'em' units so the page can be used with menus that are dimensioned in 'em' units. Such menus expand/contract when the user changes the default fontsize in the browser.Note Menukit can build CSS Menus using 'em' units.
- Create a local site for this tutorial. Download the support files for this tutorial. Create an empty site and copy the support files into it. Start Dreamweaver and open the start page, 3colDivs_start.htm.
- Analysis of the 3colDivs_start.htm page.
The divs in 3colDivs_start.htm have temporary borders and some filler text just to make them easy to see in a browser. We will delete these borders and text in a moment.
We use a labelled <div> for each of the five main areas: 'header', 'footer', 'left', 'content' and 'right' as shown in the diagram below.
The 'content' and 'right' divs are enclosed in an 'innerWrapper' div. The 'right' div is defined before the 'content' div in the source code and is styled 'float: right'. This causes the 'content' div to appear on its left.
The 'left' div is styled 'float: left' and is defined before the 'innerWrapper' div in the HTML source file. This causes the 'innerWrapper' div to appear on its right.
All these divs are enclosed in a 'wrapper' div. The 'wrapper' div allows us to keep the 'footer' positioned at the bottom of the page when the page is longer than the screen.
View 3colDivs_start.htm in a browser if you want to see this effect. (Just ignore the behavior of the 'footer' for the moment.)As text is added to the 'content' div it flows downwards and around the floated 'right' div. Text in the 'innerWrapper' also flows downwards and around the floated 'left' div resulting in the following shape:
You will find this line already present and commented out in 3colDivs_start.htm. Just uncomment it.To prevent the text flowing under the 'right' div, add a margin to the right side of the 'content' div using a style like this:#content {
margin: 0 150px 0 0;
...You will also find this line commented out in 3colDivs_start.htm. Uncomment it.... and to prevent text in the innerWrapper div flowing under the 'left' div add a margin to the left side of the 'innerWrapper' div like this:#innerWrapper {...
margin: 0 0 0 7.5em;The flow constraints imposed by these 2 margins give us the 3 column shape that we want:
The 'footer' div is absolutely positioned at the bottom of its containing block, in this case the 'wrapper' div.
The 'min-height: 100%' attribute ensures that 'wrapper' is at least as high as the screen but it can grow to the height of the page when the page is longer than the screen.* Warning! IE6 does not understand 'min-height'.The 'wrapper' div, which contains the 'footer' div, establishes a positioning context for the 'footer' div in this case because it is positioned relatively and has a height (actually min-height*) assigned to it.As far as IE6 is concerned, since 'wrapper' has no height and no width specified, it does not establish a positioning context for 'footer'. Therefore, IE6 positions 'footer' absolutely in the next outer block, <body>, in this case.
Note that the <body> element has a height of 100%. It so happens that IE6 interprets 'height' as if it were actually 'min-height'. That suits our purposes very well since the footer still ends up positioned where we want it - at the bottom of the page.
Note This only works because IE6 does not see 'wrapper' as a positioning context. If we were to do something that converted 'wrapper' into a positioning context for IE6 (eg. if we gave it a 'width'), then 'footer' would be positioned at the bottom of 'wrapper' rather than the bottom of the page.
Refer to the supplement at the end of this tutorial or to tutorial Centered Scalable Layout using <div>'s to see how to handle that situation.
Annotated source code.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"3colDivs_start.htm
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>3 Column Scalable Layout (using 2 floated <div>'s)</title>
<style type="text/css">
html {
height: 100%;
}
Browsers make the body element only as tall as is needed for the actual page contents - they do not stretch the height to fill the browser window (or more correctly, the viewport). But we can force the <body> to stretch to 100% of the viewport by giving it a height of 100% with a style specification. In some browsers, the <body> element gets its height from the <html> element rather than directly from the viewport. To cater for these browsers we also give the <html> element a height of 100%.body {
margin: 0;
padding: 0;
background: #FBFBFF;
color: #000033;
height: 100%;
}
Note The '#wrapper' element includes the attribute 'position: relative'. That is required to make 'wrapper' the positioning context for 'footer'.#wrapper {Why is this important? If 'wrapper' were not the positioning context (eg. if its 'position' had been 'static' or omitted altogether) then the footer would be positioned at the bottom of the next outer containing block i.e. the <body> element in this case. Since <body> has been given a height of 100% (the height of the screen), the footer would always appear at a fixed position on the page equal to the height of the screen regardless of the actual size of the page - not what we want.
position: relative;
min-height: 100%;
}
#header {
height: 6em;
border: 1px solid blue;
}
#left {
float: left;
width: 7.5em;
border: 1px solid red;
}
#innerWrapper {
/* margin: 0 0 0 7.5em; */
}
#right {
float: right;
width: 150px;
border: 1px solid green;
}
#content {
/* margin: 0 150px 0 0; */
border: 1px solid orange;
}
#footer {
position: absolute;
bottom: 0;
height: 4em;
width: 100%;
border: 1px solid black;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="header">Header area in 3 column layout</div>
<div id="left"> Some text in the left column.
... </div>
<div id="innerWrapper">
<div id="right">Some text in the right column.
...</div>
<div id="content">Some text in the content area.
...</p>
</div>
</div>
<div id="footer">Footer area in 3 column layout</div>
</div>
</body>
</html>
- Add a shim to keep the footer below the flowed content.Since
the footer is absolutely positioned it is removed from the document flow
and does not affect where any of the flowed content is placed. In other
words its presence is ignored. To see the effect of this, view the page in a
browser and try dragging the bottom of the screen up and down.
Now delete the temporary borders (they are commented as 'temporary' in the source).
Make your browser window narrow enough so that the content in the main area is taller than the content in the left column.After you remove these borders you can make the browser's vertical scrollbar disappear completely by dragging the bottom of the screen downwards until it is below the flowed content. Now move the bottom of the screen upwards and notice that the vertical scrollbar becomes visible again as soon as the bottom of the screen (NOT THE FOOTER) touches the bottom of the flowed content in any column.This reveals a small problem that we must fix...
...the footer, being tied to the bottom of the screen, is also moved up and overlaps the lower part of the content. Actually, it overlaps the content to a height of 4em, the height of the 'footer' div.Note. 'static' is the default value of 'position' so we can just omit it from the stylesheet element.A simple way to fix this is to insert a spacer div (a 'shim' in mechanical engineering terms) into the flow at the bottom of the 'wrapper' div. This spacer div must have the same height as the footer AND IT MUST BE FLOWED content i.e. its position attribute must have the value 'static'. Add the shim just before the footer div like this:
Note. The 'footer' div is absolutely positioned and has no effect on flowed content inside the 'wrapper' div. Therefore it doesn't matter whether we place the shim (flowed) before or after 'footer' in the source code as long as we place it inside the 'wrapper' div.vel lorem.</div>
<div class="shim"></div>
<div id="footer">Footer area in 3 column layout</div>
</div>Make the shim the same height as the footer:
Here we are using a class selector rather an ID selector because we may need to use a similar shim elsewhere..shim {
height: 4em;
}
If you are not sure about how the shim works, just give it a temporary background color and you'll be able to see its effect as you drag the bottom of the screen up and down..shim {
height: 4em;
background: #CCCCCC; /* temporary background color */
}At this stage, everything looks fine provided the main content area is longer than each side column. But if a side column becomes longer than the content area then we will again see the footer overlapping the bottom of the longer side column. We can fix this by inserting the attribute 'clear: both' in the shim's style element.
'clear: both' means that the browser must place the shim below any previously floated element..shim {
height: 4em;
clear: both;
}
The effect of this is to make the browser move the shim down until it is underneath both floated div's. (Don't forget to delete the shim's temporary background color if you have not done so already.)Your page should now look like page 3colDivs_inter.htm which you can find in the support files.
- Set background colors for header, content and footer
areas. We have already set the text and
background colors for the main content area by setting them for the <body> element.
Now we can apply colors to the header and footer like this:
#header {
height: 6em;
background-color: #000059;
color: #F2F2FF;
}#footer {
position: absolute;
bottom: 0;
height: 4em;
width: 100%;
background: #80A7E0;
color: #000033;
} - Set background color for the left column.
The Challenge
The floated 'left' div that we use for the left column may or may not stretch right
down to the footer depending on the size of its contents. This means we cannot use
its background color to create a full length column as a sidebar. Instead we will create
another, absolutely positioned div whose height we can control.
The Solution Insert an absolutely positioned div, which we will identify as 'leftbar', inside the 'wrapper' div and place it in the source code before the 'header' and 'left' divs like this :
<div id="wrapper">Add a style element for it:
<div id="leftbar"></div>
<div id="header"> Header area in 2 column layout </div>
<div id="left"> Some text in the left column. Vivamus nisl.
#leftbar {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 7.5em;
background-color: #DDEEFF;
color: #000033;
border-right: 1px solid #000055;
}
If you preview the page now with a Mozilla-based browser (don't use IE6 just yet) you will see that the 'left' column and part of the 'header' have disappeared. In fact, they have been hidden by the new 'leftbar' div which is now covering the 'left' column and part of the 'header'.Why is the 'footer' not hidden as well?What has happened is that a new positioning context, with a higher stacking order on the Z-axis (coming out of or perpendicular to the screen), was created for the new div.
Since 'footer' is positioned absolutely, it too is a positioning context and since it is defined in the source file after 'leftbar', it is given a higher stacking order than 'leftbar'.We can, however, bring the contents of 'left' back into view by creating a positioning context for 'left' which is actually higher than 'leftbar' on the Z-axis.
Create a new positioning context for the 'left' div by inserting an attribute 'position: relative' into its style element. And since the 'left' div is located in the source code after the 'leftbar' div it receives a higher stacking value and is therefore placed on top of the background provided by 'leftbar' - exactly what we want.
#left {
position: relative;
float: left;
width: 7.5em;
}Similarly, create a new positioning context for the 'header' div to bring it too in front of the sidebar by inserting an attribute 'position: relative' into its style element.
#header {
position: relative;
height: 6em;
background-color: #000059;
color: #F2F2FF;
}Although the page renders well in standards-compliant browsers, it can cause a problem in Internet Explorer 6 in certain circumstances.
- IE6 Background coloring problem. In certain
circumstances when a vertical scrollbar is required to view the lower section
of a long page, the background color can disappear from the left column.
The problem manifests itself when the page is longer than the screen by
more than the height of the footer (in other words, if there is more
of the page than just the footer scrolled down). The solution is in 2 parts:
Part 1 The first part takes care of the situation when the left column is the longest. All we have to do here is add the background color and right border to the style for the 'left' div like this:
#left {
position: relative;
float: left;
width: 7.5em;
background-color: #DDEEFF;
color: #000033;
border-right: 1px solid #000055;
}The above amendment does not adversely affect any other browser so we do not need to isolate it for IE.
Part 2 The problem of the missing background-color can still occur, however, if the left column is shorter than either of the other two columns. The solution is similar to the one we use in the 2-column layout. The basic approach is to put a wide, colored border on the left side of the 'innerWrapper' div that provides the background color for the left column.However, the 3-column situation is slightly more complicated than the 2-column one because now there are 2 columns inside the 'innerWrapper' div and the border must be as tall as the taller of these.
As things stand, the 'innerWrapper' div does not actually enclose the 'right' div...
...even though the 'right' div is a child element of the 'innerWrapper' div, it is ignored when the height of 'innerWrapper' is calculated BECAUSE IT IS A FLOATED CHILD.
We can force 'innerWrapper' to enclose its floated child element by floating 'innerWrapper' itself. It doesn't matter in this case whether we use 'float: left' or 'float: right' because the flowed content that follows it, i.e. the shim, has already been styled 'clear: both'.
Now that we have made sure that it is as high as the taller of the 2 columns, we add a wide border (same width as the left column) to the left side of the 'innerWrapper' div and give it the same color as the column background.
Having added the wide border, we no longer want the wide margin on the left side of 'innerWrapper' that we added earlier. In fact, we must specify a negative value for margin in order to slide the border all the way under the left column and its 1px border. The required value (converting everything to ems) is therefore 7.5em + 0.0625em (using the formula 1em = 16px commonly used as the default font-size in browsers). In this case, we use a conditional comment to isolate this solution for IE6.
<!--[if lte IE 6]>
<style type="text/css">
#innerWrapper {
float: left;
border-left: 7.5em solid #DDEEFF;
margin-left: -7.5625em !important;
}
</style>
<![endif]-->Now when we view a long page in IE6 we see that the background-color does indeed extend down to the footer.
But the 1-pixel border is still missing on the bottom section of the page that scrolls up into view. To fix that we can include a 1-pixel background image in the 'innerWrapper' div at position 0 on the x-axis (to position it immediately right of the wide border) and repeat it down the y-axis.
Alas, there is still a font-size problem with IE6...
Whenever we change the default font-size, it appears that IE 6 forgets to recalculate the size of our 7.5em wide border. Fortunately, by including 'font-size: 100%;' (which should do nothing) in the #innerWrapper style it seems we can remind IE 6 to do the required recalculation whenever the font-size changes.
So the final version of the style element that is needed to help IE6 looks like this:
<!--[if lte IE 6]>
<style type="text/css">
#innerWrapper {
float: left;
font-size: 100%;
border-left: 7.5em solid #DDEEFF;
margin-left: -7.5625em !important;
background: url(borderpx.gif) 0 0 repeat-y;
}
</style>
<![endif]-->
- Set a background color for the right column. We
are making a column on the right with a fixed width of 150px. That
means we can use a background image to position
it on the right side of the page. The image is actually 151px wide because
it includes a 1px border on its left side. Insert the background
image into the page's style like this:
body {
margin: 0;
padding: 0;
background: #FBFBFF url(rtcol.gif) top right repeat-y;
color: #000033;
height: 100%;
}Your finished 3-column page should now look like page 3colDivs_final.htm which you can find in the support files.
Now you can delete the temporary filler text from the various divs to make an empty 3-column layout, 3colDivs.htm ready to receive content.
- Reduce width of wrapper
After we reduce the width of the wrapper and center it, the page body background
will become visible at the sides of the wrapper.
Currently the content area inherits its text and background colors from the
body element.
We need to transfer the current text and background colors of the body into the wrapper and change the color of the page background to the dark color that we want for the margins, like this:
body {
margin: 0;
padding: 0;
height: 100%;
background: #999999;
height: 100%;
}
#wrapper {
position: relative;
min-height: 100%;
background: #FBFBFF;
color: #000033;
}Now we set the width of the wrapper to 95% and set its left and right margins to 'auto' to center it within the body:
#wrapper {
position: relative;
min-height: 100%;
background: #FBFBFF;
color: #000033;
width: 95%;
margin: 0 auto 0 auto;
}It no longer looks right in IE6If you view the page now in IE6 you will see that the footer is no longer tied to the bottom of the window. - What has happened to IE6 and how can we fix it?
Since it now has a width specification, the 'wrapper' div has become a positioning context and the footer is positioned absolutely at bottom.
IE6 considers the 'wrapper' div to have no height specification (it does not understand min-height) and so it determines the height of 'wrapper' from its contents.
If we set the height of 'wrapper' to 100% then it ill inherit the height of its parent, the browser window. In fact, it happens that IE6 interprets 'height' as 'min-height' and as a result, the height of 'wrapper' will increase to the length of the page when the page is longer than the screen.
But there is a potential problem.
If we set the height of 'wrapper' to 100% then it will break the solution we already arrived at above for browsers that implement the standard interpretion of 'height'.
So, we enclose the 'height' style in a conditional comment so it can be seen only by IE6:
<!--[if IE 6]>
<style type="text/css">
#wrapper {
height: 100%;
}
</style>
<![endif]-->
Your page should now look like page 3colDivs_centered.htm which you can find in the support files.

Print