#masthead{
border:1px dashed #333;
width:0;
position:absolute;
height:100px;
top:0;
}
#left{
border:1px dashed #000;
width:252px;
} #center{
border:1px dashed #666;
margin:.5em 0 0 0;
padding:1em;
position:absolute;
top:100px;
}
Overlapping 'div' objects (footer) due to unknown 'div'
object (column) heights as a result of dynamically generated content.
This problem also assumes the usage of positioned objects and not the usage of floats. There are arguments for using floats over positioning and vice versa. A strong argument against floats is how the page will display chronologically in a non-standards compliant browser or a text-only browser. My home page, which uses positioned elements and this dynamic height solution, displays in a logical order in these types of browsers. The order is, from top to bottom, the masthead object, left object, center object, right object, links object and footer object. Also, using floats with a 3 or more column layout can be difficult when designing for just one browser, not to mention 2 or more browsers.
To see the full effect when viewing the following examples, please resize your browser window and notice the footer's behavior.
Our first example, test page A, shows the problem of trying to use an absolute positioned footer with other positioned columns. Note the style for the left column: it doesn't use positioning as by default it will anchor to the top left corner of the page and it's height is set to auto.
Test page B shows the same problem using a relative positioned footer with other postponed columns. The left column this time, however, is positioned relative and has a height 90%. Assigning the footer a height of 10% and a relative position will now allow these 2 objects to resize dynamically* with each other.
In both examples, the footer, when forced into the space of the 2 right-hand columns, will overlap these columns. Throw into the equation dynamically generated data, trying to place these elements using positioning to avoid this problem would be impossible with CSS alone.
*Netscape 7/Mozilla 1 render incorrectly the 90% and 10% height values for the left column and footer, respectively.
Using client-side JavaScript, determine the height of the columns and set the top of the footer object to the bottom of the tallest column object.
Test page C shows how the page now responds with the fix in place. This time, be sure to use the "show text" and "hide text" links.
All right, enough playing around with the cool test pages. Let's get started!
We will use test page C as our reference page as it has a footer, 3 columns and uses JavaScript to adjust the footer's vertical position. For extra fun, the page also has a masthead and an additional row object called 'links' bringing this layout to 3 columns and four rows. As you will see, using JavaScript to adjust object heights and vertical position will open up a new level of design.
Netscape 6 in Windows fails to set the top style value of some objects - specifically the links object in this example - upon load of the page. However, as soon as the user resizes the window, the object lines up correctly.
To start, simply create your page by using absolute positioning to place your columns and rows via your CSS. You are welcome to copy the CSS found at the top of each object on this page. It doesn't matter if the layout of the page is perfect or not because we will be using JavaScript to ultimately position all objects for us. However, it is important to keep in mind that in non-standards compliant and text only browsers that your content should flow logically from top to bottom. For example, my home page has the same basic layout as the test page C and flows from masthead object, left object, center object, right object, links object, footer object from top to bottom. Next, use the following JavaScript code as the ground work of your page, altering the codes naming conventions to those used in your CSS.
window.onresize = findWindowDim;
function findWindowDim(){
var objLinks = document.getElementById('links');
var leftHeight = document.getElementById('left').offsetHeight;
var centerHeight = document.getElementById('center').offsetHeight;
var rightHeight = document.getElementById('right').offsetHeight;
var linksHeight = document.getElementById('links').offsetHeight;
var mastheadHeight = document.getElementById('masthead').offsetHeight;
var objFooter = document.getElementById('footer')
if (document.body.clientHeight > 200){
function hideError() {
return true;
}
window.onerror = hideError;
linksY = rightHeight + mastheadHeight;
linksYPosPx = linksY+"px";
objLinks.style.top = linksYPosPx;
}
if (window.setCursor && (window.innerHeight > 200)){
function hideError() {
return true;
}
window.onerror = hideError;
linksY = rightHeight + mastheadHeight;
linksYPosPx = linksY+"px";
objLinks.style.top = linksYPosPx;
}
if ((centerHeight + mastheadHeight > leftHeight) && (centerHeight
> rightHeight + linksHeight)){
footerTop = mastheadHeight + centerHeight;
footerTopPx = footerTop+"px"
objFooter.style.top = footerTopPx;
}
else if ((leftHeight > centerHeight + mastheadHeight) && (leftHeight
> rightHeight + mastheadHeight + linksHeight)){
footerTop = leftHeight;
footerTopPx = footerTop+"px"
objFooter.style.top = footerTopPx;
}
else {
footerTop = mastheadHeight + rightHeight + linksHeight;
footerTopPx = footerTop+"px"
objFooter.style.top = footerTopPx;
}
}
We need to run the function findWindowDim() anytime the user resizes their browser window. The onresize event handler will handle this for us.
window.onresize = findWindowDim;
Declare the function.
function findWindowDim(){
Creating some shorthand variables for the DOM. Additionally, we capture the offsetHeight of each object on the page and assign it to a variable. Any element on a page has an offsetHeight which can be read or written to.
var objLeft = document.getElementById('left');
var leftHeight = document.getElementById('left').offsetHeight;
var centerHeight = document.getElementById('center').offsetHeight;
var rightHeight = document.getElementById('right').offsetHeight;
var linksHeight = document.getElementById('links').offsetHeight;
var mastheadHeight = document.getElementById('masthead').offsetHeight;
var objFooter = document.getElementById('footer')
The 'if statement'. The DOM document.body.clientHeight finds the viewable area of the users browser window, give or take a couple of pixels. This statement is valid for IE5+ and NS7/Mozilla 1 as Netscape 6 uses window.innerHeight. The value of 200 is the sum of ALL the height's of the objects on the page that have a fixed height. In this example this includes the masthead and footer of 100px each. Neglecting to add this value or setting it less than the sum of the heights will generate an error when the browser's window height falls below this value.
if (document.body.clientHeight > 200){
But, what if we can't be for certain that our masthead and footer will remain at a total combined height of 200px or less? Not to worry - enter the hideError() function.
The hideError() function will be used to stop the script from displaying an error message when the browser's window height is less than the sum of all the fixed height objects on a page. With out the hideError() function, the script would generate an error when the height of the browser window is less than the above sum. For example, if a 150px tall image is placed in the footer the total height of the fixed height objects on the page would now equal 250px. However, the 'if statement' above is set to 200px. If the browser's height were now to fall under 250, the script would try to pass a negative value as the height of one of the dynamic columns. Though in theory, an objects style can have a negative height, JavaScript will not pass a negative height to a style, and, thus, will generate an error.
Not often, I would think, that the height of the masthead and footer would change. Therefore, this function may be left out if you are 100% sure of the sum of the fixed height objects AND set that height as the minimum height value in the 'if statement' above.
Window.onerror checks to see if a non-syntatical error has been generated in the script. When the error is generated, call the function, and return a value of true. This will suppress the error, hide any visual notification to the user, and allow the script to continue. NOTE: Do not add this function until you are completely finished with the script and have determined it doesn't not cause any other errors. We don't want to use this function to hide sloppy mistakes within our code.
function hideError() {
return true;
}
window.onerror = hideError;
We need to place in position the links object. We add together the heights of our masthead object and right object which gives us in pixels the distance from the top. We then concatenate this with "px" and pass the value to the links object style. Now as the page dimensions change, the script continually calculates the new height of the right object, adds this to the masthead object and passes that value to the links object. Child's play, right? Excellent! Onward to the footer object!
linksY = rightHeight + mastheadHeight;
linksYPosPx = linksY+"px";
objLinks.style.top = linksYPosPx;
}
Oops, can't forget about Netscape 6. The property window.setCursor is exclusive to Netscape 6 and including it within the 'if statement' will filter our Netscape 7/Mozilla 1 which also recognize window.innerHeight. Repeat the above 4 lines of code (plus hideError(), if required) after the following 'if statement'.
if (window.setCursor && (window.innerHeight > 200){
The footer! Just as we found the top position of the links object by adding the offsetHeights of 2 objects, we will do the same for the footer. But, we also need to compare the heights of the 3 columns against each other. It is then when we pass to the footer the style value for top. Let's do it.
I start by finding the height of the center object and comparing it to it's neighboring object heights. This seems most logical as in a 3 column layout, the center column would normally contain the most content. If the center column is tallest, set the footer's position and exit the script. If not, move onto the next scenario.
Let's breakdown the first condition together. You can dismantle the other 2 on your own. First, let's find the total height of the center column by adding the center object to the masthead object and see if it is greater than the height of the left column. Next let's take the height of the center object and see if it is greater than the combined heights of the right and links objects. If both conditions return true, then the center column must be the tallest. We now pass that value to the footer object's style as it's new top position. If the center object's height is less than either of it's neighbors, try the next condition.
if ((centerHeight + mastheadHeight > leftHeight) && (centerHeight
> rightHeight + linksHeight)){
footerTop = mastheadHeight + centerHeight;
footerTopPx = footerTop+"px"
objFooter.style.top = footerTopPx;
}
else if ((leftHeight > centerHeight + mastheadHeight) && (leftHeight
> rightHeight + mastheadHeight + linksHeight)){
footerTop = leftHeight;
footerTopPx = footerTop+"px"
objFooter.style.top = footerTopPx;
}
else {
footerTop = mastheadHeight + rightHeight + linksHeight;
footerTopPx = footerTop+"px"
objFooter.style.top = footerTopPx;
}
Simple enough. With the use of 2 JavaScript properties, 2 functions, the DOM, a couple of 'if statements' and a little math you are on your way to masterful page designs. Combine this knowledge with the dynamic width script and the possibilities are endless. Place and size objects at will with no need to worry about the content within them. Lastly, if you are worried about the use of JavaScript and those users that have it disabled AND you are still with me reading this, well...there is always CSS3.
[an error occurred while processing this directive]#right{
border:1px dashed #999;
margin:.5em 0 0;
padding:1em 0 1em 1em;
position:absolute;
top:100px;
} #links{
border:1px dashed #CCC;
margin:.5em 0 0;
padding:1em 0 1em 1em;
position:absolute;
}