Calculating Correct Computed CSS Styles Cross Browser

The other day I had to do some work which involved working with fonts and other styles using Javascript. It involved extracting inline or CSS style values, calculating sizes in pixels and converting various units from em to px or pt to px etc. As with most tasks that should be simple it was a real leason in browser incompatibility and ended up taking far too much of my time. In W3C standard compliant browsers you can use the getComputedStyle function e.g


window.getComputedStyle("someID", "").getPropertyValue("font-size");

This will return the actual size in pixels computed by the browser. So if the element had a style set to 12pt it would return 16px. This is great but like most things in life you can guarantee that IE will do things differently and only half as good. They use a function called currentStyle e.g


document.getElementById("someID").currentStyle["fontSize"];

The first difference is that you refer to the style in CamelCase rather than separated by hyphens as you would in the stylesheet. The second is that the returned value will not be in pixels but will be in the same units as defined in the stylesheet or inline style. Now if you wanted to return the size in pixels like the computedStyle function it means having to do quite a lot of work as you need to convert the unit that currentStyle retuns into px. Obviously I didn't realise quite how much work it would be and after I read a few articles I naively decided to write my own function to correctly compute styles in IE combining all the tasty tit bits I had come across in a nice simple object.

Conversion Problems

Trying to convert certain units to pixels is not so much of a problem and there are different ways to accomplish this. Some conversions like points to pixels are simple math depending on whether or not how accurate you want to be. Others such as percentages or ems have to be done in relation to their parent elements. I came across Dean Edwards runtimeStyle hack which jQuery also uses but it didn't handle percentages accuratley so I looked at the hidden div measurement function where a div is inserted into the DOM with the required style and then measured before removing it again. This seemed to handle most units apart from IE6 in certain cases. With percentages you also have the problem of "auto" which means that the currentStyle value is returned with the same value as the parent and you need to work out whether to calculate the computed value as a percentage of the parent or set it to the same value as the parent.

Other font-size styles such as smaller and larger cannot easily be converted to px as they depend on the browsers current text-size or zoom settings and are also taken in relation to other styles e.g two nested DIVs both with a font-size of larger means that the child DIVs font-size is larger than its parent which in turn is larger than the style it inherits from. For the values xx-small, x-small, small, medium, large x-large and xx-large you can start off with a simple lookup function that takes into consideration whether the browser is IE in quirks mode but then once the user starts changing their browser settings to zoom in or increases or decreases the text-size then these values don't mean jack and as far as I know there is no way to get the current browsers text-size setting to use as a multiplier on the original lookup value.

Fun and Games with IE6

Also least I ever forget there is the joy of IE6 which meant that as soon as I changed the css stylesheet of my article from my blog to this current one it meant that my previously working font-size function suddenly started adding extra units to my previous working values. I tracked down the rule that causes this:


#main {
	background: url(http://www.blogblog.com/thisaway_blue/bg_content.gif) repeat-x left top;
}

But have yet to find a reason why it affects the measurement of a hidden div when appended to the DOM so if anyone knows why then let me know. Therefore I have had to add a few IE 6 specific hacks into the function to handle its odd behaviour. Although not perfect and taking into consideration all I have mentioned about certain unit calculations I have run some test cases and it seems to work okay when working with a series of nested DIVs each with different font-style units and values. If there are issues with the code and I am sure there will be then please let me know. Like always with me this started off as a little 10 minute function and ended up as one of those "I will not be beaten" battles that you wish you never started. Only after reading one too many articles do you realise that there is a probably a good reason why no-one has managed to do this properly before. What bugs me the most is why Microsoft when redeveloping their Javascript and rendering engine for IE8 didn't think that it might have been a good idea to include a function to return the correct computed value.

CSSStyle Object Function Results

The following is an example of my object in action. I have created some nested HTML elements with various inline and CSS styles and then when run the function will test each element in turn and output the following to the results container:

  • The style.fontSize, style.cssText, currentStyle, runtimeStyle and computedStyle values.
  • The value that JQuery's curCSS function returns.
  • The value that Dean Edwards hack returns (IE only).
  • The value that my CSSStyle getComputedStyle function returns.
  • The values that my CSSStyle FontSize function returns.

The values returned should match those that are labeled below in the relevant DIV containers. Also the results from running this in Internet Explorer should roughly match those from running in Firefox, Opera, Chrome, Safari etc. The HTML I am checking the styles for is below:


<div id="d1" style="font-size: small;">
	<div id="d2" style="font-size: 12pt;">
		<div id="d3" style="font-size: 1.5em;">
			<div id="d4" style="font-size: 75%;">
				<div id="d5" style="font-size:auto;">
					<div id="d6" style="font-size: 0.9em;">DIV 6: Size 0.9em = 16px
						<div id="d7" style="font-size:150%">DIV 7: Size 150% = 24px</div>				
					</div>	
				</div>
			</div>
		</div>
	</div>
</div>
Results will appear here when the window has loaded.

The following HTML contains the nested DIVs that the function will be looking at.

DIV 1: Size Small = 13px
DIV 2: Size 12pt = 16px
DIV 3: Size 1.5 em = 24px
DIV 4: Size 75% = 18px
DIV 5: Size auto = 18px
DIV 6: Size 0.9em = 16px
DIV 7: Size 150% = 24px

Font Size Conversion Function

As you will see if you compared the results across multiple browsers including IE the results from the FontSize function are more accurate than those from the getComputedStyle and other functions. The Font-Size Style Function is designed specifically towards font sizes as it also "tries" to handle the unit types xx-small to xx-large and gives details about em's for that element. You call it by passing either a reference to the element in question or passing an ID and you are returned a small object with a number of useful properties:


var fsize = CSSStyle.getFontSize("myID");
var s = fsize.style; // current inline style value if set otherwise the computed style.
var px = fsize.px;  // computed size in px.
var em = fsize.em; // size in em.
var emu = fsize.emu; // size in px that 1em equates to on this element.

As you can see this is all useful info related to the style value. The CSSStyle.style property will hold the inline style if set otherwise the current style or computed stylevalue. So for an element with an inline style of 12pt in Firefox it return 12pt in both Firefox and IE. The CSSStyle.px property will hold the computed value in px which is very useful for IE as you cannot get this value with calculating it yourself. The CSSStyle.em property holds the size in ems (which is a relative value) that the size in px equates to. If the body has a fontSize of 10px then 1em = 10px. If I then define a div with a fontSize of 1.2em this equates to 12px and 1em at this level is 12px. This is the value that the CSSStyle.emu propery holds.

Hacks, tweaks and other bodges

As stated earlier I had to use a few hacks to handle IE6 behaviour. I also had to handle the fact that there is no way I know of to extract the word "auto" or determine whether a style is defined as auto. I know that in some cases with certain styles that "auto" is returned but defining it as the value for fontSize means that in IE you will get back the current style of the parent that its inheriting its value from. This becomes a nightmare when for example you have two nested divs like the following:


<div id="p2" style="font-size:50%;">Font Size 50% will compute as half the number of px of its parent
	<div id="p3" style="auto;">Font Size auto will compute as the same amount of pixels as its container element</div>
</div>

Whereas Firefox, Safari, Chrome, Opera and the others have no problems and returns the computed style value in px. IE will return 50% as the value for the inner DIV which is the same style value as the outer DIV which it inherited the value for. For a while this made my life difficult when I was trying to calculate the size in px as I needed to determine whether to calculate the size as 50% of its parent or keep it the same value. This is where the em value came to my rescue as the logic of ems state that the size in pixels of 1em on a child element should equal the current elements size in pixels. Using this logic I can flag when an elements style in % equals its parent, calculate the size in px it would be if it was actually a true percentage (and not auto) and then compare this to the child elements size of 1em. If they differ I know its style is auto not a true percentage. This does mean that to calculate computed values for certain styles in IE I have to look at related elements so its not particularly quick in that respects and I have built in a little object cache to speed things up so that the same element is not checked twice unless the style value for it changes.

Download the full source code here.

Further Reading and related links

  1. History of Mac and Windows font size differences.
  2. Article about CSS units and the history of px and pts.
  3. W3C specification on length units.
  4. Font Size and Ems explained.

Post Comments

As this script is not part of the blog if you would like to post comments please click this link and then respond to the following article.