9

Firstly, I know that CSS expressions are defunct and wrong in so many ways, and also to avoid using !important where possible. This is for a special case stylesheet.

In Short

My question is... Is there any way for a CSS expression to set the !important flag?

E.g. this doesn't work:

a { color:expression('red !important'); }

[Edit: thanks to MarmaladeToday's comment below]. This also doesn't work:

a { color:expression('red') !important; }

Can this be done some other way?


In Detail

What I'm actually trying to do is mimic the inherit value in IE6 & 7. This works:

color:expression(
    this.parentNode.currentStyle ?
        this.parentNode.currentStyle.color: 'red'
    );

But I also want to set the !important flag, and this doesn't work:

color:expression(
  (
    this.parentNode.currentStyle ?
        this.parentNode.currentStyle.color: 'red'
  ) + ' !important');

I'm aware that, in JavaScript, it isn't possible to set !important via an element's style object. E.g. this won't work:

element.style.color = 'red !important';

However, it is possible to set !important via the element's style attribute:

element.setAttribute('style', 'color:red !important;');

So... are CSS expressions limited to interacting with the style object, and therefore, what I want to achieve is impossible - or is there any way for an expression to affect an element's attributes, or pass !important in some other way?


Starting a Bounty

No solid answers so far, so I'm starting a bounty.

Ideally, I'm looking for a CSS-based solution for mimicking inherit !important in IE6 and IE7, either with or without CSS expressions. (Please do verify that your suggestions work before posting).

At least, a link to some authoritative reference telling me that this is impossible would mean I could lay this train of thought to rest - I've not seen anything mentioning the use of CSS expressions with the !important rule.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Prem
  • 15,911
  • 11
  • 31
  • 35
  • I wonder if this question would be better over at http://doctype.com ? – Prem Oct 27 '09 at 12:00
  • @artlung - why's that? I don't have any experience so far of doctype.com, but it seems HTML/CSS focussed. Do you think this is too technical a question? – Prem Oct 31 '09 at 21:08

6 Answers6

3

The reason those CSS expressions don't work is because IE only evaluates the expression for the last property in the cascade.

E.g. if you have an HTML document with a link inside it and the following "CSS",

a {
    color: expression(function(e){
        alert('success');
        e.runtimeStyle.color = 'blue';
    }(this));
}
a { color: red; }

you will never see that alert (nevermind the style change), because the CSS expression is never evaluated. So no, you can't use an expression to set the !important flag.

That is to say, not when you try to set it on the same property. You can cheat. But that does make the expression a bit more complicated:

a {
    filter: expression(function(e){
        e.runtimeStyle.color = 'blue';
        alert('success');
        e.style.filter = '';
    }(this));
}
a { color: red; }

There are a few things to note here.

If you simply use another CSS property, you can be sure that the expression will be evaluated. Or at least, a little more sure, because if there's another rule further down the cascade that uses the same property already, you're still out of luck.

Secondly, you have to use runtimeStyle instead of currentStyle. If you used currentStyle here, the second rule would still end up overwriting it. runtimeStyle overrides all other CSS (except !important declarations). So it's the JScript equivalent of !important.

Also note that I'm resetting the filter property itself as well. That prevents the expression from being continuously re-evaluated. But as much as that may reduce performance, I don't think it's super critical. The main reason I put it in here is because I added alert()s in those expressions, and you definitely don't want to have those pop up forever.

It is in fact also possible to use any other property you make up. This works too:

a {
    bogus: expression(function(e){
        e.runtimeStyle.color = 'blue';
    }(this));
}

However, since the bogus property doesn't actually exist, you can't reset it using Javascript, so this will be re-evaluated continuously.

mercator
  • 28,290
  • 8
  • 63
  • 72
  • Wow. Thanks, Mercator. You clearly understand IE's expressions well. I may want to ask you more q's if they arise (by email?). Your response most directly answers my question, because it uses CSS expressions to mimic inheritance in IE6/7. I like the fact that it doesn't require external scripting files. Maybe we should use something like `bogus`, so that the rule doesn't get overwritten. And I guess we do want to continuously execute the expression, since the value to be inherited may change. (Please do let me know if you're interested in contributing this to the CleanSlateCSS stylesheet). – Prem Oct 31 '09 at 22:12
  • I e-mailed you. Whether I can actually answer any of your questions is another matter. – mercator Nov 01 '09 at 01:39
2

color:expression('red') !important;

  • Sorry - I meant to include that in the original description. No, this doesn't work either. – Prem Oct 16 '09 at 15:33
  • Thought I had, but nope. Figured trying the obvious worthwhile as it's often overlooked. Have you seen this: http://meyerweb.com/eric/thoughts/2007/04/23/inherit-expression-for-ie/ might be food for thought. Annoying I can't comment on other answers here.... Nate is almost correct in that IE6 sometimes doesn't recognise !important apparently, one of the reasons it was used as a hack once upon a time. But I guess it's for over-riding instances like that which your special case style sheet is for. – MarmaladeToday Oct 16 '09 at 17:38
  • Yes, that Eric Meyer post was one of things that got us started on expressions as a possible way out of our inheritance hole - but it looks like perhaps they can save us after all. – Prem Oct 20 '09 at 15:32
2

You could try the behavior property, it's a lot more versatile than expressions.

Try this,

inherit.htc :

<SCRIPT language="JScript">
    //add your own inheritance logic here, you can use element.parentNode etc.
    var prop = element.currentStyle.behaviorProp;//specified in stylesheet
    var val = element.currentStyle.behaviorValue;//specified in stylesheet
    element.style[prop] = val;//set as inline style, you can also use setAttribute here.
</SCRIPT>

css:

body #div1 {
    color:blue;
}

#div1 {
    color:red;/*stays blue, because 'body #div1' is more specific*/
    behaviorProp:color;/*specify styleproperty for the .htc*/
    behaviorValue:green;/*WILL become green, because the .htc sets it as an inline style, which has priority.*/
    behavior:url(inherit.htc);
}
I.devries
  • 8,747
  • 1
  • 27
  • 29
  • Thanks for giving this example, Vatos. It looks like this would work for us. Using `behavior` would be a bit clunky and proprietary, and require an additional `.htc` file, but it does have the advantage over standard JavaScript that it can be called from within the CSS stylesheet, and so it wouldn't require users of our stylesheet to have to add an addition ` – Prem Oct 31 '09 at 21:53
1

This sounds sick and evil, but:

color: expression((function (_this) {
    window.setTimeout(function () {
        _this.setAttribute("style", "color:" +
            (_this.parentNode.currentStyle ?
                _this.parentNode.currentStyle.color : 'red') + " !important";
    }, 0);
    return _this.parentNode.currentStyle ?
        _this.parentNode.currentStyle.color : 'red';
})(this));

In other words, this:

  1. Returns "red" (or the parent color) immediately.
  2. Sets a timeout that will set "red !important" (or whatever) immediately after.

Keep in mind that this could end up hosing your browser though. I didn't test it, and stuff involving CSS expressions tends to go terribly wrong.

Anthony Mills
  • 8,676
  • 4
  • 32
  • 51
  • Thanks for the suggestion, Anthony. Out of interest, why do you use a `setTimout`, instead of directly calling `setAttribute`? We had previously tried `setAttribute`, but not with `setTimeout`. Neither way seems to work, though. The attribute is not set and !important is not applied. – Prem Oct 20 '09 at 15:27
1

The venerable ppk of quirksmode has done some work on CSS, JavaScript and the !important keyword. (Note that for test in Firefox or any other non-IE browsers, use cssRules instead of rules below.

Here's the link: W3C DOM Compatibility

You can apparently read whether a style rule in a stylesheet is important by doing this (this would get the importance level of the color property in the 2nd css rule in the 2nd stylesheet, IN ALL BROWSERS EXCEPT MICROSOFT INTERNET EXPLORER. How's that for a bummer.

document.styleSheets[1].rules[1].style.getPropertyPriority('color')
//Returns important when the style is marked !important.
//Returns an empty string otherwise.

Now, his research reveals yet another way to set !important, unfortunately this also apparently only works in non-Internet Explorer browsers

// where x is a DOM element
x.style.setProperty('color','#00cc00','!important');
// Set the color of x or the second rule in the second style sheet to green.
document.styleSheets[1].rules[1].style.setProperty('color','#00cc00','!important');

Apparently this strange syntax, if you are not setting things as !important, requires setting the third parameter to null:

// where x is a DOM element
x.style.setProperty('color','#00cc00',null);
// Set the color of x or the second rule in the second style sheet to green.
document.styleSheets[1].rules[1].style.setProperty('color','#00cc00',null);

Now, MSDN has a property called "cssText" that dangles off the style object of a DOM element. It even indicates that CSS expressions are okay. The usage is:

x.style.cssText = 'color: #00cc00 !important';

Now, this may be important (heh), the MSDN page says (emphasis mine):

DHTML expressions can be used in place of the preceding value(s). As of Internet Explorer 8, expressions are not supported in IE8 mode. For more information, see About Dynamic Properties.

It may be worthwhile to check out ppk's tests of cssText as well.

I unfortunately don't have the relevant versions of Windows Microsoft Internet Explorer available on this laptop, but perhaps this is of some use. I'm curious to how this shakes out, it's something I'd run into (setting !important in JavaScript) but found other ways to get around it, typically just shifting around classes or applying specificity by using an ID in the relevant selectors.

Best of luck on an interesting question!

artlung
  • 33,305
  • 16
  • 69
  • 121
  • Hi artlung. Thanks a lot for this thoughtful answer. What you describe is a way to interact with the `!important` rule via JavaScript. Actually that can be done most simply by setting a `style` attribute on the element, as I mentioned in my question - like this: `element.setAttribute('style', 'color:red !important;');`. What I'm looking for, though, is a way to set `!important` from within the CSS stylesheet. – Prem Oct 31 '09 at 21:03
-3

It's been my understanding that !important is largely these days as a hack... specifically because IE does NOT recognize it.

Nate Wagar
  • 640
  • 2
  • 7
  • 18
  • 4
    Hi Nate. No, `!important` isn't a hack. It's primarily designed to be used in user stylesheets to override the specified styles on the page. It is also supported in IE6+, although in IE6 there is a bug where, if a block of rules contains a particular property that has `!important`, and then the same property is set within the same block without `!important`, the latter will prevail. No doubt IE has some other quirky bugs too, but essentially it works. The problem that I am coming up against is that IE6 and IE7 don't properly support the `inherit` value. – Prem Oct 16 '09 at 22:31
  • Important is clearly in the CSS2 spec: http://www.w3.org/TR/CSS2/cascade.html#important-rules – Eli Oct 30 '09 at 16:44