Friday, October 17, 2008

One-time execution of IE CSS expressions

You know guys, CSS expressions in IE (6 and 7) is a nice and powerful tool, which offers an excellent way to overcome compatibility issues. Dozens of missing W3C features can be emulated simply by an expression. It's so elegant and comfortable to solve the issues of lack of standards with them. You can make behaviours for :first-child, max-height, :hover, cellspacing/cellpadding, different kinds of selector operators etc. Ok, we know that, but hey, they say at Yahoo that CSS expressions are nasty, they want me to avoid them and instead use javascript. Yahoo says that expressions are evaluated so frequently that that is untenable. Well, the fact is, he's right.

But I love expressions.

Last time, I wanted to use the inherit value, which is not supported in IE6/7. Pretty tiny problem, the solution is:
.elem {
background-position: expression(parentNode.style.backgroundPosition);
}
The place where class .elem elements appeared was deeply in a tag soup of a complex menu, where some layered CSS sprites inherited the containers background-position, so it was a hit on the responsiveness of the menu hover effect, of course. It got executed on every mouse movement for every menu item in the structure and, overall, thousands times in some seconds.

What to do now. Yahoo says: „One way to reduce the number of times your CSS expression is evaluated is to use one-time expressions, where the first time the expression is evaluated it sets the style property to an explicit value, which replaces the CSS expression.” This sounds exciting, but don't know how to do that. To put it short, someone asks about this in the comments in the above linked Yahoo page, and Steve Souders answers with a link to a testpage. He calls an external function in the expression which function then sets the element's style property:
<script>
function setOnetimeBgcolor(elem) {
elem.style.backgroundColor = <some calculation>;
}
</script>
<style>
P {
background-color: expression(setOnetimeBgcolor(this));
}
</style>
This can be rewritten in one expression:
/* It works. */
P {
background-color: expression(
new Function('elem', 'elem.style.backgroundColor = <some calculation>;')(this)
);
}
Here we are, this works. The expression will be evaluated only one time. Now comes the interesting part. When I copied this solution in my working context to emulate inherit on the background-position property, it didn't work:
/* WON'T WORK */
.elem {
background-position: expression(
new Function('e', 'e.style.backgroundPosition = e.parentNode.style.backgroundPosition;')(this)
);
}
The inherit behaviour itself was live, but it was as terribly sluggish as before. I made a test with a counter and it turned out that the expression actually didn't overwrite itself and was getting evaluated continuously. The method failed. But why does it work on Steve Souders' example page?

After some debugging I've found that, unlike background-color, a background-position expression can't overwrite itself with a static value. It seems that CSS properties differ in the manner of which one of them can or cannot overwrite themselves. For example, background-color can. Display can. Background-position can't. Float and height also can't, but clear can...!

Very interesting, but it's the very truth.

So now I took an 'assistance' property which is self-overwritable, let this be clear. Watch it, it's only used as a dummy assistance property. Then set the value of background-position in it, which is not self-overwritable, and lastly overwrote the assistance property. This trick even allows us to avoid function creation:
.elem {
background-position: inherit;
*clear: expression(
style.backgroundPosition = parentNode.style.backgroundPosition,
style.clear = "none"
/* debug: */
, window.expc = window.expc || 0,
window.defaultStatus = expc++
);
}
This is it. The menu was absolutely responsive. The expression runs only one time. You can experiment, the debug lines show the number of executions in the browser's status bar. I think it is the recommended way of CSS expression usage with any CSS property, when working around compatibility issues.

It's important to note that you cannot use the underscore hack (_clear) in case of expressions intented only for IE6, because it will run on IE7 too. In such situations you have to separate it by other ways.

UPDATE

Yet, it's still not the end, there's another catch. If you remove the debug lines from the above example, IE gently will hang up. Put a simple value, e.g. a 0 in place of that, and it will work:
/* Final example, the ultimate way of writing one-time CSS expressions */

.elem {

/* we want to implement the unsupported 'inherit' value */
background-position: inherit;

/* 'clear' is a dummy property */
*clear: expression(

/* this is the actual assignment */
style.backgroundPosition = parentNode.style.backgroundPosition,

/* overwriting dummy property, 0 needed to avoid crash */
style.clear = "none", 0
);
}

24 comments:

  1. Any script execution at the Style Sheet stack of the browser is a performance issue and more importantly a security threat.

    Good news is Microsoft is ending expressions in Internet Explorer as announced on Thursday earlier this week.

    A very nice move towards the standard-compliant browser.

    ReplyDelete
  2. Furthermore, any script execution at all is a performance issue and more importantly a security threat.

    ReplyDelete
  3. Nice job, this is exactly what I was looking for.

    While expressions aren't ideal, they can still be useful at times. For example, if you want to use properties like "max-width:" which is supported by IE7 but not IE6. You can segregate expressions by using conditional statements to prevent users in IE7 from experiencing any security or performance issues.

    Users in IE6 should be fairly familiar with it's security and performance issues by now anyway, it's over 7 years old.

    ReplyDelete
  4. I wouldn't think we should talk about that blurred 'security issue' topic in this case, as the one who writes the expressions is you, the author of the site. Further on this logic, web developer himself is the biggest security issue in a site.

    ReplyDelete
  5. When you want to target IE6 and not IE7 precede the expression with a hyphen followed by a dollar sign. For example, -$clear:expression( )

    ReplyDelete
  6. Any idea what other properties would be able to be used. I would like to not use clear as it could have a use for other things. Prehaps zoom. seeing as how this is an i.e only propertiy anyway.

    ReplyDelete
  7. Nice hint with a -$ thing.

    Anonymous: give some a shot, and share your experiences :)

    ReplyDelete
  8. Hey, thanks for the post. I'm only starting work on CSS, so it's great finding examples like these online, and comments to help me understand things better. Thanks! :)

    ReplyDelete
  9. "It's important to note that you cannot use the underscore hack (_clear) in case of expressions intented only for IE6, because it will run on IE7 too."
    Are you sure?
    _property seems to be filtering out IE7

    ReplyDelete
  10. IE7 just won't match it with the related CSS property, thus won't apply (unlike IE6). But both browsers WILL process it. So if you use expressions to inject code with underscore, it will behave the same. Test it.

    In other words, underscore doesn't get filtered out in parsing phase, * does.

    ReplyDelete
  11. Its like you read my mind! You appear to
    know so much about this, like you wrote the book in it or something.

    I think that you can do with some pics to drive the message
    home a little bit, but other than that, this is fantastic blog.
    A fantastic read. I'll certainly be back.
    Here is my blog :: fat loss

    ReplyDelete
  12. That is very interesting, You're an overly skilled blogger. I have joined your rss feed and look forward to in search of more of your excellent post. Also, I have shared your site in my social networks

    Also visit my web site :: vegetarian

    ReplyDelete
  13. As for the surfing, I have loudly applauded. farmacia on
    line's discovery was serendipitous. The drug was a sponsor of the ultrasound bill, because farmacia on line has obvious side effects, there is invariably randy banter online among literary geeks about poetry's sexiest verses.
    The norming group seems to be able to take the country into a disastrous, decade-long war
    based on lies will always remain free. You should not
    take farmacia on line.

    Also visit my web page :: more information

    ReplyDelete
  14. Director of the business practice many paphos car hire agencies remain in touch with representatives of
    paphos car hire companies. You will have to finalise a bailout of the Cypriot economy down.
    Be sure to contact the rental offices to inquire ahead of time.
    Others complain of "mystery charges" added to credit
    cards at up all over the area there are still plenty of older traditional
    properties becoming available. Median household income: $41,
    415Population: 4, 802, 740 23rd highestUnemployment rate: 9 percent 18th highestPercent below poverty line: 20.

    ReplyDelete
  15. Paphos Car Hires are just one days trip from island resort.
    Although these hills are not really aware about the
    city roads and the top deals will be calculated as well as business people.


    Also visit my web blog: car hire paphos airport cyprus

    ReplyDelete
  16. Have you ever considered about adding a little bit more than just your articles?
    I mean, what you say is valuable and all. But think of if you added some
    great visuals or videos to give your posts
    more, "pop"! Your content is excellent but with images and clips, this site could definitely be one of the very best
    in its niche. Excellent blog!

    Feel free to visit my homepage :: Cheap Nike Jerseys

    ReplyDelete
  17. Hello, the whole thing is going perfectly here and ofcourse every one is sharing data, that's in fact good, keep up writing.

    My homepage :: cuteteenporn.net

    ReplyDelete
  18. Its such as you learn my thoughts! You seem to know a lot approximately this, like you wrote
    the e book in it or something. I feel that you simply can do with some percent
    to force the message home a bit, however instead of that, that is excellent blog.

    A great read. I will certainly be back.

    my homepage xxxmoviegalls.com

    ReplyDelete
  19. Write more, thats all I have to say. Literally, it
    seems as though you relied on the video to make your point.
    You definitely know what youre talking about, why waste your intelligence on just posting videos to your
    blog when you could be giving us something enlightening to
    read?

    my website ... party hardcore

    ReplyDelete
  20. Hi there friends, its wonderful post on the topic of tutoringand entirely defined,
    keep it up all the time.

    Here is my web site; www.jnmassage.info

    ReplyDelete
  21. Someone necessarily lend a hand to make critically posts I
    would state. This is the first time I frequented your web
    page and to this point? I amazed with the analysis you
    made to make this particular put up incredible. Great job!


    Check out my webpage; Nike Air Max

    ReplyDelete
  22. Hi there! This post could not be written any better! Reading through this post reminds me of my old room mate!
    He always kept chatting about this. I will forward this article to him.
    Pretty sure he will have a good read. Thanks for sharing!


    Feel free to visit my website :: Cheap Oakley Sunglasses

    ReplyDelete
  23. When someone writes an paragraph he/she retains the plan of a user in his/her
    mind that how a user can know it. So that's why this post is outstdanding. Thanks!

    Also visit my page ... Louis Vuitton Bags ()

    ReplyDelete
  24. Hi there, You have done a great job. I will certainly digg it and personally suggest to my friends.
    I am sure they'll be benefited from this site.

    Here is my web page ... Sac Louis Vuitton Pas Cher

    ReplyDelete