Welcome to WebmasterWorld Guest from 54.161.108.58
Forum Moderators: not2easy
Why and how...
We know that there is a solution for the lack of position:fixed in Internet Explorer 6. It consists in some javascript code placed in a style rule thanks to a IE-only feature: one can declare style using the expression() operator/function, and fill it with javascript code.
If you want a div to stay at the top of the window the style would look like this:
(The test on "document.documentElement.scrollTop" makes this code compatible in both standard and quirks modes)#divFixed {
position: absolute;
top: expression(0+((e=document.documentElement.scrollTop)?e:document.body.scrollTop)+'px');
left: expression(0+((e=document.documentElement.scrollLeft)?e:document.body.scrollLeft)+'px');}
}
Those who have tried this method know that, when you scroll the page, the fixed element will jitter and jump from a scrolled down (or up) position, to its correct (fixed) position. This is due to IE scrolling the page, updating the display, and only then, calculating the new values of all the expression().
Recently I've read a technical article dealing with the method (sorry, I don't remember the site's URL). The writer was lamenting on the fact that, unfortunately, we'll have to live with the jittering effect since it's the closer we can come to a working solution to position:fixed. In a follow-up comment he put forth a possible solution not working for him (and for me when I first tried) : someone told him that having a background image fixed on the body element was getting rid of the jittering.
In fact, this solution really works. When the background image is fixed, IE is forced to process the "layers" (bdcolor+bgimage+tags) before displaying the page, and the end result is that the page is refreshed with the expression() rules already updated.
Technical possibilities
For pages without background image, the fix is easy: add a transparent, fixed, background, and you're done. For pages that already have a background image: if it is fixed, no need for a fix ; if it's not fixed, you'll have to make it fixed. This is not always a good solution. No everyone wants the background image becoming fixed as a side effect of a "dirty" fix.
Since BODY has a parent element (HTML), I've tried applying a background to it. It has the same effects as a background on BODY, with some nuances:
Notes on quirks mode:IE6 handles HTML and BODY (almost) as if they were a single tag. setting a background image on HTML does not trigger the anti-jittering effect. No transparency is applied to BODY to allow the display of the HTML element underneath. consequence, if you apply a transparent image (spacer) to BODY, IE won't display the background of HTML. Knowing that, the only possible fix for documents in quirks mode is to apply "document-attachment: fixed" to BODY, and set a transparent background image if none is set.
Also, the content of expression() is parsed differently: () inside a 'string' are interpreted as real javascript (). Which cause a parsing error. This could be overcome if the entire script code is enclosed inside a (function(){})() .css: expression('someString with ()'...);// fails in quirks
css: expression((function(){'someString with ()'...})());// works
Back to standard mode
Possibilities:
We still have a problem if we need the background color and background image to be seen simultaneously (partially transparent image, or image not repeated in a direction and relying on the bg color to fill the gap). Since HTML can't have a background color, after the fix has been applied the background color of the page is white (or the default background color set in the display settings in Windows).
If we use margin:0px on the body element, we can put a background on the HTML element and keep the normal background on the BODY element without it being moved by the margin. In this case BODY background doesn't need to be modified ; adding a background image to HTML is enough to trigger the anti-jittering effect.
Example page available...
Houston, we have a problem...
However, applying this solution triggers a side-effect. If the document height is smaller than the available space in the browser, the body's height won't span the entire viewport's height, and its background will only be seen on the area that IE thinks is used. We can apply height:100%;, but, if the body has padding and/or border, 100% will be too much and scrollbars will appear.
Once again expression() comes to the rescue (in standard mode).
height:
expression((function(){with(document.body.currentStyle)document.documentElement.clientHeight -((parseInt(marginTop)¦¦0) +(parseInt(marginBottom)¦¦0)+(parseInt(borderTopWidth)¦¦0) +(parseInt(borderBottomWidth)¦¦0) +(parseInt(paddingTop)¦¦0) +(parseInt(paddingBottom)¦¦0))+'px'})());
Remarks on expression():
The expression is applied to HTML. I think it's the tag with the lower risk of style's declaration collision.
The current URI for the spacer is /s.gif. Place the image file at the root of you site. Feel free to move it elsewhere, and update the URI accordingly. Note that the image path is relative to the HTML page, not to the CSS file.
The script
html {
top: expression(
prevent the expression to be run more than once. (not necessary, see #3).
(typeof _E=='undefined')
wait until DOM tree is built
&& (document.body)
set the "blocking" variable. Shortcut for body's style (read only).
&& (_E=document.body.currentStyle)
we need this shortcut too (write only)
&& (_dbs=document.body.style)
#1
check if body has an image. If yes goto #2
&& ((_E.backgroundImage!='none')
if no image, add a spacer (not forgetting the current bgcolor)
¦¦(_dbs.background=' '+_E.backgroundColor+' url("/s.gif") fixed'))
#1 end
#2
is bgimage fixed? if yes goto #3
&& ((_E.backgroundAttachment=='fixed')
do we have margin=0 ? If yes, add spacer on HTML. (buggy without "height=100%") goto #3
will soon add expression for "body.height=100%" here (needs testing)
¦¦ ((_E.margin=='0px')
&&(this.style.background=' '+'url("/s.gif") no-repeat'))
#2b moving most bg info from BODY to HTML
setting bg on HTML (img, repeat, attachment, default position)
¦¦ ((_P=_E.backgroundPositionX+' '+_E.backgroundPositionY)
&& (this.style.background=' '+'transparent '+_E.backgroundImage
+' '+_E.backgroundRepeat+' '+_E.backgroundAttachment+' '+_P)
clear BODY bgcolor and bgimg ('none' allows background-position to still be modified by CSS)
&&(_dbs.backgroundImage='none')
&&(_dbs.backgroundColor='transparent')
add an expression to update bg position if the style is updated on BODY
&& (setTimeout(function(){
document.documentElement.style.setExpression(
"backgroundPosition",
"document.body.currentStyle.backgroundPositionX+' '
+document.body.currentStyle.backgroundPositionY")},0))
)
#2b end
#2 end
)
#3
we apply "top=0px" to HTML. This results in the removal of the expression()
&& (setTimeout(function(){eval('document.documentElement.style.top="0px"')},0))
);
}
The script is executed once, and removes itself from the CSS rules associated to the DOM node. There is no CPU gain in replacing it with human written rules.
Current version
(disregarding the background color problem when the background image does not cover the entire page). Working only in standard mode.
html {
top: expression((typeof _E=='undefined') &&(document.body) &&(_E=document.body.currentStyle) &&(_dbs=document.body.style) &&((_E.backgroundImage!='none')¦¦(_dbs.background=' '+_E.backgroundColor+' url("/s.gif") fixed')) &&((_E.backgroundAttachment=='fixed') ¦¦((_P=_E.backgroundPositionX+' '+_E.backgroundPositionY) &&(this.style.background=' '+'transparent '+_E.backgroundImage+' '+_E.backgroundRepeat+' '+_E.backgroundAttachment+' '+_P)&&(_dbs.backgroundImage='none') &&(_dbs.backgroundColor='transparent') &&(setTimeout(function(){document.documentElement.style.setExpression("backgroundPosition", "document.body.currentStyle.backgroundPositionX+' '+document.body.currentStyle.backgroundPositionY")},0)))) &&(setTimeout(function(){eval('document.documentElement.style.top="0px"')},0)));
}
How to use ?
You have several possibilities:
How to ensure that the hack is only for IE6 ?
<!--[if lte IE 6.0]><link rel="stylesheet" href="/IE6-fix.css"><![endif]-->IE6-fix.css: (the '\' limits the style to IE6)
h\tml {top: expression(...);}
<!--[if IE 6.0]><link rel="stylesheet" href="/IE6-fix.css"><![endif]-->IE6-fix.css:
html {top: expression(...);}
h\tml {_top: expression(...);}
If you don't want a conditional comment (propositions #1 and #2), you can still use the following method:
@i\mport _url("IE6-fix.css");IE6-fix.css:
html {top: expression(...);}
#1 and #2 will not cause CSS warnings in non-IE browsers. #3 and #4, will probably (not visible to the end-user).
To do
Note
The full text, with correct code, example pages, correct list items indentation, and valid pipe character (¦), is available on this site [motus.ath.cx].
I came across this simple solution, as well. It uses css expressions. But perhaps IE 6 deserves to be 'punished', I don't know, for not supporting fixed as it should. Ironically, fixed, actually is used.
So in the BODY:
background: url("https://") fixed;
And in the style for the top bar:
top: expression( (( t=document.documentElement.scrollTop) ? t: document.body.scrollTop) +'px');
And that's it. Fixed at the top. And no jitters. It works quicks or strict in IE6. And I'd only load it conditionally for IE6 (maybe 5).
As you explain, it would make sense that updating is done first, because that's what we see - no jittering. The https is apparently needed because http could cause a problem. But you don't actually need a background image.
I like having the option of different background tiled images, selected from a preferences menu. But it's not applied to the body, but to a div named, container (or comtainer, or whatever have you), that's become fairly standard practice. So the child of BODY, is this container div. It's got a few of its own 'containers'. But it, rather than body, tends to be treated as the topmost element.
Your inline script does a lot of validating. But could that be more clearly accomplished in the css itself? If you want no margin, just set margin:0 ?
As with you, I have no problem writing a main css, and then loading a fixer css for the down-level IE6. So all this would go in that IE6 specific css, too. As for how to call it, I don't mind calling from the hard coded page. These are typically all generated anyway. So change the template, and regenerate them all again. BUT once the site become substantial, I would agree, you'd want this done just in the main style sheet, once, and that's it. Apparently there are various import hacks for IE6. The problem would be if another browser, or IE8, 9, etc, overlook the hack and load the css. But since this import is only in one place, in one file, I think it's worth the risk. If it needs repair, in future, you know right where to go, and before and after it's good for every old/legacy page on your site.
I noticed particularly with margin, that zooming in/out in IE6 can disturb the relative placement. A item at the bottom of the navbar winds up near the top at full zoom, etc. But that's even true with 0 margin. They still slide around too much. The script module I suggested doesn't do this. Even at full zoom, the various 'fixed' elements are about where you'd expect them to be on screen (with 0 margins set). But of course, they 'jiggle', they 'jitter'. But since the main navbar is now free from that, the distraction might not be as much.
If you use the correct conditional comments to target only IE6, I don't see how you would need to correct the style once IE8 IE9 are available.
Your inline script does a lot of validating. But could that be more clearly accomplished in the css itself? If you want no margin, just set margin:0 ?
I noticed particularly with margin, that zooming in/out in IE6 can disturb the relative placement.
[edited by: Achernar at 1:34 pm (utc) on Mar. 6, 2008]
if (element.style.setExpression) { // IE
element.style.position = 'absolute';
element.style.setExpression('top', "(document.documentElement ¦¦ document.body).scrollTop +'px'");
element.style.setExpression('left', "(document.documentElement ¦¦ document.body).scrollLeft +'px'");
document.body.style.backgroundImage = "url('/pixel.gif')";
document.body.style.backgroundAttachment = 'fixed';
}
else element.style.position = 'fixed'; // !IE
[edited by: MarkFilipak at 8:33 pm (utc) on Mar. 6, 2008]
I agreed with you. I like the idea, though, of doing this in just one place rather than using IE conditionals on every html page. Most of these are hard-coded by an offline generator, not created at the server.
Instead of the slash/underbar hack, perhaps one could use script to attach to the HEAD. Script has to be on, anyway, for this to run.
>I don't see this in my test case.<
Do you have an example of two or three divs that have to be fixed in some relative position to each other. And when zooming in or out in IE6, do they go out of position? Let's say you float a copyright string to the bottom of the navbar. Let's say you have an auxiliary menu on the left, about a "W"s spacing below the bottom of the navbar. And you've got your own custom status bar at the bottom.
I like the fact it can be completely separated and although I haven't read and digested it all yet, I have a question.. I'm not too hot on javascript so excuse my terminology.
I recently heard and have read previously that CSS Expressions continue to execute on every mouse move/user action/window scroll etc but that you can stop that happening or lessen the server calls by making sure something is done in the expression, is that what you mean by
>>We have to ensure that the enclosed javascript code is completely evaluated.
sorry if I'm not terming this correctly.. but if I knew the terms I'd search ;)
this thread is bookmarked!
-Suzy
Overall, beyond this "problem", some expressions are constantly evaluated (when the mouse moves over the window), some only once. This is why you'll notice that in some scripts the coder has used a variable assignment (which forces regular evaluation).
My anti-jittering fix, for example, is constantly executed. Hence it needs to remove itself.
In another script I'm currently testing, I declare 4 functions inside an expression, and it appears to be exectuted only once (if I overwrite one of the function, it does not revert to the original version by miracle).
Expressions don't put load on server, only on the client machine.
If an expression generates a javascript error (syntax error), it is not evaluated more than once (on startup).
If an expression is a "constant", it is not executed more than once.
* {width: expression('10px');}
will screw your display, but will not overload the CPU (at least not on my computer).
[edited by: Achernar at 1:54 am (utc) on Mar. 8, 2008]
I was saying that I agreed it might be better to place it in one file, one place, rather than hard-coding the IE6 downlevel fix into every webpage on the site with IE conditionals. And your slash/underbar hack for the css was the alternative in the one, single css file. But it might conceivably fail in IE8 or 9. You never know. One might otherwise count on the HEAD conditional, however, to be supported by future IE versions.
I notice that you didn't reply about various of these 'fixed' elements holding position with regard to one another when zooming in and out in IE. And I also found it very interesting that you were able to say that you could remove the fix and still have it work every time on scroll. Those css expressions DO fire a lot. It seems that you've overcome perhaps the practical objection to this if you can do so. You'll still have the 'purists' objections that you're 'behaving' in the css, and not 'styling'.
I notice that you didn't reply about various of these 'fixed' elements holding position with regard to one another when zooming in and out in IE.
And I also found it very interesting that you were able to say that you could remove the fix and still have it work every time on scroll.
Those css expressions DO fire a lot. It seems that you've overcome perhaps the practical objection to this if you can do so. You'll still have the 'purists' objections that you're 'behaving' in the css, and not 'styling'.
If a single IE6-fix.css is enough to make a site work in IE, while keeping the page's HTML and CSS valid, it's perfect for me.
And as long as this fix is only slowing IE6 a little, It's not a problem. You see, I'm also testing sites on an old Celeron 450Mhz, and there is no usability problem. It's at least not slower than a site with a fixed background. So...
* html {
background-image: url(image.jpg);
}
* html #nav {
position: absolute; /* position fixed for IE6 */
top: expression(114+((e=document.documentElement.scrollTop)?e:document.body.scrollTop)+'px');
left: expression(35+((e=document.documentElement.scrollLeft)?e:document.body.scrollLeft)+'px');
}