Computed vs Cascaded Style
Most JS frameworks and libraries I’ve seen have a function similar to this:
function getStyle(el, prop) {
if (document.defaultView && document.defaultView.getComputedStyle) {
return document.defaultView.getComputedStyle(el, null)[prop];
} else if (el.currentStyle) {
return el.currentStyle[prop];
} else {
return el.style[prop];
}
}
So what is wrong with this you might ask? Lets add some background and then
we can get back to that question.
Computed Style
Computed style represents the actual computed value of a style in absolute
units. For example ‘100px’ (for width) or ‘left’ (for text-align). The computed
value is never ‘auto’, ‘inherit’, ‘50%’, ’smaller’, ‘1.5em’ etc. These values are
relative to something and the browser needs to convert these values to absolute
values first.
Cascaded Style
Cascaded style is the real style value that is applied to an element when the
style value is set to ‘inherit’. Inherit means that it should get its value from
the first ancestor that has a non ‘inherit’ value. A few CSS properties have
their value set to ‘inherit’ by default. Most common of these are probably
‘color’ and ‘font-size’. If you change the font size of a paragraph you want the
links in the paragraph to change their font size as well.
Inline Style
Inline style is the style value you have added to the style attribute on you
element or the style properties you have set in script using
element.style.propertyName.
Example
<style>
div {
width: 500px;
font-size: 12px;
}
p {
width: 50%;
font-size: 1em;
}
a {
font-size: inherit;
}
</style>
<div>
<p>Some text with <a id=a href=#>a link</a>.
More <b style="font-size:150%">text</b></p>
</div>
| Computed Style | Cascaded Style | Inline Style | |
|---|---|---|---|
| p width | “250px” | “50%” | “” |
| p fontSize | “12px” | “1em” | “” |
| a fontSize | “12px” | “1em” | “” |
| b fontSize | “18px” | “150%” | “150%” |
Browser Support
All browsers except IE has a way to get the computed style. The way to do
this is to use document.defaultView.getComputedStyle. Only IE has a way to get
the cascaded style. This is done using element.currentStyle.
All browsers supports getting the inline style (even IE4) but inline style is
not that interesting because it is empty unless you already set it, and in that
case you don’t really need to query it.
Conclusion
Back to our earlier question. What is wrong with our getStyle function? It
should be pretty obvious now. It will give very different and sometimes
unexpected results depending both on browsers and on the value set by the page
author and even worse, set by user style sheets.
So how do we solve this? We stop supporting IE of course… seriously, with
the market share IE has that is not an option. The best solution is to remove
functions like these from shared code and instead add more specific functions.
One can for example, often calculate the computed value based on the cascaded
value and the ancestors. A good example of that is ‘visibility’. Computing that
is pretty easy. If the value is ‘inherit’ check the parents until a non inherit
value is found. For things like left, width etc you will be better of using
offsetLeft, offsetWitdh etc. There are a lot of cases and sometimes it is just
not possible (or requires iterative testing with different absolute sized
elements).
Comments
Override style
There is one more style type that is worth mentioning and that is the
override style. This is called document.defaultView.getOverrideStyle in the W3C
DOM and element.runtimeStyle in IE. It allows you to override the style property
in such a way that it has (a) highest priority and (b) not serialized from the
DOM when you do things like cssText and innerHTML. getOverrideStyle is not
supported in Gecko so it is still only useful to IE specific code.
It is a bit strange to me that the W3C DOM never added a way to get the
cascaded style. The usecases for this are few but they do exists.
What is even more strange is that MS did not realize the need for a way to
get the compouted style back in the days when they believed in Trident as a
platform.
July 27th, 2007 at 19:44
This works on IE6 and IE7:
var PIXEL = /^\d+(px)?$/i;
function getPixelValue(element, value) {
if (PIXEL.test(value)) return parseInt(value);
var style = element.style.left;
var runtimeStyle = element.runtimeStyle.left;
element.runtimeStyle.left = element.currentStyle.left;
element.style.left = value || 0;
value = element.style.pixelLeft;
element.style.left = style;
element.runtimeStyle.left = runtimeStyle;
return value;
};
July 28th, 2007 at 1:50
I disagree. I find currentStyle much more useful than getComputedStyle and would have preferred non-IE browsers to implement it instead of computed styles (ideally both).
There is always a way to calculate the pixel value using the tools browsers provide. But in FF today, there simply NO WAY to know whether a width value is set in percentages, inches or pixels if the value had been set outside of the style attribute! This is a pretty unbelievable shortcoming.
The offset* and client* functions provide nearly everything you may want in terms of getting the pixel values, and the difference between them helps you calculate padding/border values. In fact they’re so useful that I’ve rarely seen anyone opt for computed style in such situations, even if the target is FF-only.
July 28th, 2007 at 13:39
Dean: That seems to work pretty well in some cases. Which cases? It seems like it would only work for properties that have pixel* equivalent (left, top, right, bottom, width and height).
yn: How would you compute the font size given the following:
* { font-size: 12pt; }
or the background color given:
* { background-color: Highlight; }
July 28th, 2007 at 18:26
[...] Computed vs Cascaded Style The offset* and client* functions provide nearly everything you want for getting the pixel values, and the difference between them helps you calculate padding/border values. I’ve rarely seen anyone opt for computed style in such situations, even for FF. [...]
July 29th, 2007 at 5:52
Erik: It’s not about finding cases where offset* and client* won’t be enough, they certainly exist. It’s about what’s more useful to have if you had to choose one. Why would you need the pixel value of a font set in points? I really can’t think of such a scenario. If you want the height of the line in pixels, which would make more sense, that’s easy to do.
On the other hand, I can easily think of multiple scenarios requiring the actual cascaded properties and I’ve been constantly bumping against these cases, as mentioned above.
Btw, to answer your specific questions:
function getRealBackgroundColor(el) {
var oRG=document.body.createTextRange();
oRG.moveToElementText(el);
var iClr=oRG.queryCommandValue(”BackColor”);
return “rgb(”+(iClr & 0xFF)+”,”+((iClr & 0xFF00)>>8)+”,”+((iClr & 0xFF0000)>>16)+”)”;
}
Your other question was already answered by Dean, his function can take any value in any unit (except % in some cases) and return it in pixels, it’s not limited to .left, he’s just using it to do the conversion. so getPixelValue(document.body,”12pt”) will give you the pixel value for your font under your setup.
Again, I’m not saying anything everywhere can be converted to an absolute value, I’m just saying that currentStyle is a lot more useful.
July 30th, 2007 at 8:52
yn: Thanks, the queryCommandValue is a great example how to do this.
August 3rd, 2007 at 7:21
@yn: Your getRealBackgroundColor function is really interesting. For different reasons, I’ve been looking for a way to get the rrggbb values corresponding to a user’s CSS system colors, but I actually gave up a while ago.
However, I can only get your code to work in IE. Any chance you know a cross-browser way of accomplishing this?
August 3rd, 2007 at 21:17
Papandreou: In any other browser it’s trivial to get the real background color, via getComputedStyle. IE is the only one that makes this a bit of a challenge.
To create a cross-browser function simply check if the getComputedStyle method exists (FF, Opera, etc.), and if so use it, otherwise see if the createTextRange method exists (IE), and if so use it.
A *real* challenge would be to get the cascaded value (”red”, “highlight”, etc.) set in external CSS in Firefox.
BTW, Erik: Your article is mistaken. Opera supports .currentStyle properly, allowing you to get the cascaded style (from the article: “Only IE has a way to get the cascaded style.”).
August 6th, 2007 at 7:38
[...] erik’s weblog » Blog Archive » Computed vs Cascaded Style [...]
August 29th, 2007 at 6:21
Another interesting tidbit is that computed style does not take into account margin collapse. For instance, the marginTop value reported does not necessarily represent the actual margin value in effect. Quick example:
Yo
Using just default styles, the computed style on body will be 8, but it’s not very useful, because the effective margin is 21 (body margin collapsed in favor of h1).
August 29th, 2007 at 6:23
Sorry, I should’ve escaped the markup:
<body>
<div>
<h1>Yo</h1>
</div>
</body>
February 15th, 2008 at 6:49
For some reason when I use this it returns “auto” for top and left values. The object I am interrogating has no styling applied, but I was hopinh it would tell me where it was located.
March 26th, 2008 at 4:53
Why all the trouble of defining document.defaultView.document.defaultView.document.defaultView.document.defaultView.document.defaultView ??
use .getComputedStyle directly