Monday, July 20, 2009

Making position: fixed; work in IE 6

What is position: fixed;?
position: fixed; is an alternative to position: absolute; position: relative; and position: static;.
position: fixed; is basically the same as position: absolute; except that when the user scrolls the page, the element does not scroll with it, it just says exactly where it was. There are many pages that want to use this in order to position logos or menus.

What is wrong with position: fixed;?
Well, ... nothing. The problem is that the most popular browser - Internet Explorer for Windows - does not understand it, and instead of reverting to position: absolute; which would be better than nothing, it reverts to position: static; as specified by the CSS standard. This has the same effect as having no positioning at all. Note that IE 7 from beta 2 upwards does support position: fixed; (if you use a document type declaration that triggers strict mode) so I will exclude IE 7 from this fix.

As a result, serveral people write scripts that use setInterval to reposition an absolutely positioned element every few miliseconds, or (ignoring Netscape 4) when the onscroll event is detected. This produces a slightly jerky effect. It would be better if the position: fixed; style could be applied in browsers that supported it, and browsers that didn't could use position: absolute; and JavaScript. Some authors use the > CSS selector to isolate Internet Explorer and leave the element positioned absolutely in that browser, without the scrolling effect.

div#fixme { position: absolute; left: 0px; top: 0px; }
body > div#fixme { position: fixed; }

So what is the Solution?
Well, possibly. It would be neater if it could be done with just CSS, and I have found a way to take things one step closer to that.
When Microsoft added their proprietary 'expression's to CSS in Internet Explorer 5, I jumped at the chance to use these to position the absolute element. Expressions should automatically update according to changes in the browser, such as scrolling the page, or resising the window.

div#fixme {
left: expression( document.body.scrollLeft + 'px' );
top: expression( document.body.scrollTop + 'px' );
}
body > div#fixme { position: fixed; left: 0px; top: 0px; }

But major disappointment. Due to a bug in Internet Explorer's interpretation of expressions, it does not update this, so it just stayed at 0,0.
However, while playing around with some CSS, I noticed something slightly odd. If I assigned the value to a variable, then used that to assign it to the expression inline, it did update in Internet Explorer 5.5 and 6. It is slightly jerky, but not as bad as many script driven positioning techniques.

top: expression( ( ignoreMe = document.body.scrollTop ) + 'px' );

Of course, in Internet Explorer 6, the variable is assigned to document.documentElement.scrollTop when in standards compliant mode, so I needed to check for this as well. IE 7 must also be excluded from the fix.

omitMe= document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop

And we may want to position the element somewhere other than 0,0:

top: expression( ( 10 + ( omitMe= document.body.scrollTop ) ) + 'px' );

Of course, this also won't validate as CSS, so I prefer to put the extra CSS inside a conditional comment. This also allows lesser browsers like Netscape 4 to use the normal left and top positions.

<style type="text/css">
#fixme {
/* IE lesser browsers will use this */
position: absolute; left: 20px; top: 10px;
}
body > div#fixme {
/* used by Mozilla,Safari */
position: fixed;
}
</style>
<!--[if gte IE 5.5]>
<![if lt IE 7]>
<style type="text/css">
div#fixme {
/* IE5.5+/Win - this is more specific than the IE 5.0 version */
left: expression( ( 20 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( 10 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
</style>
<![endif]>
<![endif]-->

What about other palcements?
We can work out window size using the clientHeight property of the documentElement object in 'standards compliant mode' (What a load of rubbish! If IE was standards compliant, we wouldn't have to go through all of this stupidity in the first place - but I digress ...), and the body object in quirks mode (see the window size part of my JavaScript tutorial for more details). We can then get the size of the element itself using fixme.offsetHeight. This is IE's shorthand for the more normal expression:

 document.getElementById('fixme').offsetHeight 

Thankfully, the window and element size do not need any tricks to force them to update, but we will still need to do the check for standards/quirks mode.
The default setup uses the right and bottom properties, so browsers that support the standards correctly will work using them. Then in IE 5.5+, these will be reset to auto, and the left and top positions will be used instead. Remember that for right or bottom emulation like this, the offsets used in the expression must become negative.

<style type="text/css">
#fixme {
/* Netscape 4, IE 4.x-5.0/Win and other lesser browsers will use this */
position: absolute; right: 20px; bottom: 10px;
}
body > div#fixme {
/* used by Opera 5+, Netscape6+/Mozilla, Konqueror, Safari, OmniWeb 4.5+, iCab, ICEbrowser */
position: fixed;
}
</style>
<!--[if gte IE 5.5]>
<![if lt IE 7]>
<style type="text/css">
div#fixme {
/* IE5.5+/Win - this is more specific than the IE 5.0 version */
right: auto; bottom: auto;
left: expression( ( -20 - fixme.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' );
top: expression( ( -10 - fixme.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' );
}
</style>
<![endif]>
<![endif]-->


So here the final tags you need to Have to Fix the Footer at bottom of the page
/* Netscape 4, IE 4.x-5.0/Win and other lesser browsers will use this */
#footer { 
 position: absolute;
 bottom: 0;
}
div#footer {
 position: absolute;
}
body > div#footer {
 position: fixed;
}
/* This selector is need to be used in conditional comment so it will apply to the specific browser only. Netscape 4, IE 4.x-5.0/Win and other lesser browsers will use this */
*html div#footer {
 bottom: expression( ( - 0 - footer.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( omitMe = document.documentElement.scrollBottom ? document.documentElement.scrollBottom : document.body.scrollBottom ) ) + 'px' );
}

Lets Try yourself !!!