Monday, July 30th, 2007
Computed vs Cascaded Style
Just as the panel discussed getComputedStyle and how each browser returns something different, Erik Arvidsson blogged about Computed vs Cascaded Style.
Erik discusses the difference between computed, cascaded, and override styles. He shows us why this common function is bad:
-
-
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];
-
}
-
}
-
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).
Dean Edwards then commented with the following:
-
-
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;
-
};
-












I use the first function in one of my own side projects, and for the font-size property IE returns 10pt, FF returns 12px and Opera returns caption…
Too bad you can’t read from the style proporty of an object.
Unfortunately the link is broken. Would like to have seen some words by Dean how his code is supposed to work.
If value could be negative I suppose this test is quite better, am I wrong?
var PIXEL = /^\-?\d+(px)?$/i;
@Fabian - MSIE6+ has special pixelLeft/Width/Height etc properties. They represent the current pixel value of the equivalent style setting. So, if you have style.width=8em then style.pixelWidth would return the pixel equivalent. MSIE also supports an override style called runtimeStyle. Setting properties on runtimeStyle overrides all other style properties.
This trick works by setting style.left and then getting MSIE to convert it by calling style.pixelLeft. To stop the element moving around the screen when we do this, we set runtimeStyle.left with the current left value. After we’ve done the conversion we set everything back to the way it was.
Clear?
@Dean. Thanks a lot. I think I’ve got it now. Nice code BTW.
Dean, you are crazy - in a good way. I would have never thought of converting em to the pixel equivalent.
I needed this function a few days ago when I was trying to calculate the width of an element and having no success in getting it to work cross browser. Thanks Dean.
It is a mess generally. Here is what I have been dealing with today:
Internet Explorer returns (as defined in the Stylesheet)
fontSize: 110% and lineHeight: 1.4
Firefox returns pixel values for both. While this is OK for the font size, it means that you cannot get lineHight from one element and assign it to another element…
P.S.: Nice trick, Dean. Have to remember that one.
I’ve found Dean Edwards’ snippet very useful, it’s helped reduce my IE dev time. I use the MooTools library, so I re-factored the snip as a MooTools extension:
Element.extend({
/**
* Calculates the absolute measurement of the style e.g. "pixels".
* Fails on % values, original value is returned instead.
* @param {String} property : CSS property to be read
*/
getStylePx: function(property){
var PX = /^\d+(px)?$/i;
var value = this.getStyle(property);
if (PX.test(value)) {return parseInt(value);}
if (value.indexOf("%") > -1) {return value;}
var style = this.style.left;
var runtimeStyle = this.runtimeStyle.left;
this.runtimeStyle.left = this.currentStyle.left;
this.style.left = value || 0;
value = this.style.pixelLeft;
this.style.left = style;
this.runtimeStyle.left = runtimeStyle;
return value;
}
});