Monday, July 30th, 2007

Computed vs Cascaded Style

<>p>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:

javascript
< view plain text >
  1. function getStyle(el, prop) {
  2.   if (document.defaultView && document.defaultView.getComputedStyle) {
  3.     return document.defaultView.getComputedStyle(el, null)[prop];
  4.   } else if (el.currentStyle) {
  5.     return el.currentStyle[prop];
  6.   } else {
  7.     return el.style[prop];
  8.   }
  9. }

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:

javascript
< view plain text >
  1. var PIXEL = /^\d+(px)?$/i;
  2. function getPixelValue(element, value) {
  3.   if (PIXEL.test(value)) return parseInt(value);
  4.   var style = element.style.left;
  5.   var runtimeStyle = element.runtimeStyle.left;
  6.   element.runtimeStyle.left = element.currentStyle.left;
  7.   element.style.left = value || 0;
  8.   value = element.style.pixelLeft;
  9.   element.style.left = style;
  10.   element.runtimeStyle.left = runtimeStyle;
  11.   return value;
  12. };

Related Content:

Posted by Dion Almaer at 1:45 am
9 Comments

+----
1.6 rating from 95 votes

9 Comments »

Comments feed TrackBack URI

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.

Comment by joggink — July 30, 2007

Unfortunately the link is broken. Would like to have seen some words by Dean how his code is supposed to work.

Comment by Fabian Jakobs — July 30, 2007

If value could be negative I suppose this test is quite better, am I wrong?
var PIXEL = /^\-?\d+(px)?$/i;

Comment by Andrea Giammarchi — July 30, 2007

@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?

Comment by Dean Edwards — July 30, 2007

@Dean. Thanks a lot. I think I’ve got it now. Nice code BTW.

Comment by Fabian Jakobs — July 30, 2007

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.

Comment by Marc Grabanski — July 30, 2007

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.

Comment by Martin — July 30, 2007

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;
}
});

Comment by Morgan Knicely — September 3, 2007

I had a similar problem the other day requiring the computed style in px not the currentStyle or style value. The following function seems to correctly convert the style to px and ems and also handles styles in % and child elements where the parent is in % but the child is set to “auto”.

http://www.strictly-software.com/CSSStyleObject.asp

Comment by RobR — March 15, 2009

Leave a comment

You must be logged in to post a comment.