Making IE 5+ use background-attachment: fixed;

Navigation

Skip navigation.

Site search

Site navigation

Deprecated

This fix is deprecated and is no longer supported (meaning that I will not help you to get it working with your pages). It was only written to fill the gap while waiting for Internet Explorer to implement fixed backgrounds. That has now happened in IE 7. Although this fix can make it work in IE 6 as well, that is counter productive for the future of the Web. IE 6 is a major problem to Web developers (IE 7 is a problem as well, but for now, let's overlook that, since it does at least implement fixed backgrounds), and the sooner it stops being used, the better.

Instead of using this hack, users of IE 6 should be encouraged to upgrade to IE 7. Users who cannot upgrade to IE 7 (because IE 7 is not being released for most Windows operating systems) should use a better browser, such as Opera or Firefox. They have been abandoned by Microsoft.

What is background-attachment: fixed;

background-attachment: fixed; is where the background picture on a web page does not move as the page is scrolled. It just stays exactly where it is. It can even be applied to any element, not just the whole page. In this case, the background position is calculated relative to the whole page (not the element) and must only be shown where the element passes over it.

What is wrong with background-attachment: fixed;?

Well, ... nothing. The problem is that the most popular browser - Internet Explorer 6- for Windows - does not correctly apply it to elements other than the <body> and on those elements, it makes a complete mess, and assumes that you meant background-attachment: scroll; (default). Clearly you didn't or you would not have said 'fixed'. What this means is that Internet Explorer will position the image relative to the element instead of the page, and it will not stay in place when the page is scrolled, the opposite of the correct effect.

(More accurately, it supports it on any element that has scrollbars, normally the BODY, or the HTML element in CSS1Compat mode. Additionally, it supports it on elements with overflow:auto; or overflow:scroll; However, on these elements, it still positions the image with respect to the element itself instead of the viewport. It also fixes it only with respect to the element's own scrolling, so scrolling of the entire page is not taken into account as it should be.)

This can be corrected, but it requires us to calculate the position of the element, something that CSS expressions are capable of, but only if you know the exact document structure, and you write the expression for each element. I prefer a more generic approach, but this needs JavaScript. So, I bite my lip, and say to use it. Unfortunately, this means that if script is disabled, it will produce errors, so I will also write the expressions with JavaScript. If JavaScript is disabled, Internet Explorer will revert to its default (broken) behaviour, but will not produce any errors.

How it works

We cannot change the way IE works, so we need to change the CSS. This means that we need to offset the image with each element, so that it appears to be positioned relative to the page, then we need to offset it again to take the page scrolling into account. We would need to readjust this every time the page is scrolled, or the element moves because the window is resized. CSS expressions take care of these readjustments for us so all we need is the code to reposition the background image.

This is limited to being able to calculate only pixel values, not percentage, em, etc. It may also appear a little jumpy in IE, but you have a choice; slightly jumpy effect, or completely broken effect. Note that IE 7 from beta 2 upwards does support background-attachment: fixed; (if you use a document type declaration that triggers strict mode) so I will exclude IE 7 from this fix.

Position with respect to the viewport

'Viewport'? Yes, that is a fancy way to say 'the inside of the browser window' - so 'the bit page that you can see' to us mere mortals. First we must assume that the page has not been scrolled. In this case, the image should be positioned relative to the viewport, but is instead positioned relative to the element. So, in order to position the image correctly, we need to subtract the position of the element from the desired position of the image.

To achieve this, we need to use JavaScript to find the position of the element, reguardless of what elements it is contained within. See my find-position script for an explaination.

while( oElement.offsetParent ) {
  oPos -= oElement['offset'+oWhich];
  oElement = oElement.offsetParent;
}

When calling this script, we will pass it the element reference - oElement, the desired offset - oPos, and which coordinate we are working on ('Left' or 'Top') - oWhich.

Stay still as the page scrolls

Now we have the image correctly positioned when the page has not scrolled, we need to ensure it remains in place as the page scrolls as well. So we need to add on the amount of scrolling to the top and left positions. To achieve this, we need to use JavaScript to find the amount of scrolling in any mode. See my scrolling measurement script for an explaination.

oPos += document.documentElement['scroll'+oWhich] ? document.documentElement['scroll'+oWhich] : document.body['scroll'+oWhich];

Now to apply that in a function

function correctPosition(oElement,oPos,oWhich) {
  while( oElement.offsetParent ) {
    oPos -= oElement['offset'+oWhich];
    oElement = oElement.offsetParent;
  }
  oPos += document.documentElement['scroll'+oWhich] ? document.documentElement['scroll'+oWhich] : document.body['scroll'+oWhich];
  return oPos;
}

And the CSS

Firstly, you need some regular CSS to define the background image. This should be a part of your main stylesheet(s). For example:

element selectors { background: #fff url(blah.jpg) no-repeat fixed 10px 20px; }

Then, as part of the IE fix, (re-)define the background-position style to apply the expression. The elements selectors must be more specific than the default ones used in the regular CSS, as we need to make sure that IE uses this line instead of the normal one. This could also mean using the same selector as normal, then putting the special IE altered CSS after the regular CSS.

element selectors { background-position: expression( correctPosition(this,10,'Left')+'px '+correctPosition(this,20,'Top')+'px'); }

To change the position, change the numbers.

Making it valid

Once again, I use conditional comments to isolate just IE 5+ on windows, and then I apply all the necessary scripting. I also exclude IE 7 here:

<!--[if lt IE 7]><script type="text/javascript">
function correctPosition(oElement,oPos,oWhich) {
  while( oElement.offsetParent ) {
    oPos -= oElement['offset'+oWhich];
    oElement = oElement.offsetParent;
  }
  oPos += document.documentElement['scroll'+oWhich] ? document.documentElement['scroll'+oWhich] : document.body['scroll'+oWhich];
  return oPos;
}
document.writeln( '<style type="text\/css">' );

//use document.writeln to put all CSS declarations in here, surrounded by " quotes
document.writeln( "div.fixedBack, h1.fixedBack { background-position: expression( correctPosition(this,10,'Left')+'px '+correctPosition(this,20,'Top')+'px'); }" );

document.writeln( '<\/style>' );
</script><![endif]-->

The result? It works properly. Just look at the background of the headings on this page. Watch what happens when you scroll.

This site was created by Mark "Tarquin" Wilton-Jones.
Don't click this link unless you want to be banned from our site.