offsetWidth/offsetHeight useless in IE9, FF4*

OK, I must admit "useless" is a too harsh word but the thing is our beloved offsetWidth/offsetHeight JavaScript DOM properties are now not accurate any more in Internet Explorer 9 and Firefox 4. Firstly introduced in IE4 in 1997, these DOM properties are the simplest and quickest way of getting the computed pixel dimensions (the border-box) of any page element and they used to work perfectly in all browsers released since then. Well, not any more in IE9 and FF4. Read on to learn why and what you can do to workaround the issue...

* A special note for Firefox on Windows/Linux
Firefox 4 on Windows and Linux has this issue only when hardware accelerated rendering is enabled in the Options/Preferences (and, of course, supported on the machine). I always get the issue on Mac.

IE9 and FF4 introduced subpixel font rendering and now calculate and report width/height of page elements using float values even when a web page is viewed at 100% zoom level. This is something no other browser has ever done before. So, right now in IE9 and FF4 you can get computed width/height values for block elements reported like "100.34px", "11.65px", etc. I once heard from a colleague that a client asked him to move a page element by half a pixel because apparently one pixel seemed to much to him. :D Well, I've got good news for that guy, this is now possible!

But seriously, let's get back to the issue. As you already may be guessing, the problem with offsetWidth/offsetHeight in IE9 & FF4 is that these properties are integer values so in these browsers the values are computed by rounding the float values for width/height and adding the padding & border. And the rounding often leads to 1px inaccuracy depending on the calculations done in your scripts and on the exact fonts and font sizes used on the page. You may say 1px is nothing but there are cases when 1px could cause quite notable issues like, for example, text wrapping happening when it shouldn't, etc..

Demoing the Problem

Basically now you can easily stumble upon issues in IE9 and FF4 like on this demo page. In this example, offsetHeight is used to get the computed border-box height of the first DIV and the second DIV has negative top margin applied which is equal to the value returned. The end result should be perfectly overlapping DIVs, so that you could only see the second one. But in IE9 or FF4, you can see either the top or the bottom border of the first DIV sneaking beneath the second DIV for some of the iterations:

A screenshot showing the top border of the first DIV sneaking beneath the second DIV

The Workaround

From the demo above we clearly see we can no longer use offsetWidth/offsetHeight if we want perfect results in these browsers. The workaround is to use the getComputedStyle() method instead which reports the width/height of block elements with float numbers. So basically, the workaround is to get the computed width/height and then add the computed top/bottom padding & border of the element to get the final value. The problem with getComputedStyle() on theory is that for width/height of inline elements it always reports 'auto'. But the good news is these browsers only seem to round the computed float values for block elements and so for inline elements we can still safely use offsetWidth/offsetHeight! Well, if you are still following me, here is a fully cross-browser workaround compatible with all other and older browsers:

function _getOffset(elm, height) {
	var cStyle = elm.ownerDocument && elm.ownerDocument.defaultView && elm.ownerDocument.defaultView.getComputedStyle
		&& elm.ownerDocument.defaultView.getComputedStyle(elm, null),
		ret = cStyle && cStyle.getPropertyValue(height ? 'height' : 'width') || '';
	if (ret && ret.indexOf('.') > -1) {
		ret = parseFloat(ret)
			+ parseInt(cStyle.getPropertyValue(height ? 'padding-top' : 'padding-left'))
			+ parseInt(cStyle.getPropertyValue(height ? 'padding-bottom' : 'padding-right'))
			+ parseInt(cStyle.getPropertyValue(height ? 'border-top-width' : 'border-left-width'))
			+ parseInt(cStyle.getPropertyValue(height ? 'border-bottom-width' : 'border-right-width'));
	} else {
		ret = height ? elm.offsetHeight : elm.offsetWidth;
	}
	return ret;
}
function getOffsetWidth(elm) {
	return _getOffset(elm);
}
function getOffsetHeight(elm) {
	return _getOffset(elm, true);
}

Usage is very simple:

var elmOffsetWidth = getOffsetWidth(elm);
var elmOffsetHeight = getOffsetHeight(elm);

And here is the fixed demo page that includes the workaround.

The Questions

Finally, here are the questions that come to my mind:

  1. Is this the road all other major browsers (i.e. WebKit and Opera) plan to take in the future?
  2. If not, why did IE and FF did it then and shouldn't they revert to the old behavior?
  3. If yes, then shouldn't at least offsetWidth/offsetHeight be fixed to return the preciser float values?

I guess you'd agree it's nice to have these questions answered.

Liked this post?

Don't wait & subscribe to RSS or email updates now.
Quick Tip: Sharing is also easy with the share menu below. :)

Comments:

  • Hey Please help oout with following problem in ie9 browser :

    unable to assign offsetHeight of row of one table to height of corresponding row in another table

    #8 by: Nidhi on May 9, 2014 at 23:42
  • A workaround for these browsers, can be to use toPrecision() function available on offsetWidth/offsetHeight properties:


    function getOffsetWidth(elm) {
    return elm.offsetWidth.toPrecision(5);
    }

    getOffsetWidth(document.body) ===> "320.00"

    see documentation

    #7 by: saiterio on April 15, 2014 at 10:18
  • Thankyou very much for this info (found via google) - you have helped solve the issue I was having with IE9. Very much appreciated!

    #6 by: PaulM on August 24, 2012 at 18:39
  • admin

    It seems that FF5 has fixed this behaviour.

    Hmm, how did you test this? I still get integer values in FF5/Mac for offsetWidth/Height and the demo page still shows the issue for me.

    #5 by: Vasil Dinkov on July 5, 2011 at 10:38
  • This is terrible!
    getComputedStyle is around 5 times slower than accessing offsetWidth in IE9 (even using .width insted of .getPropertyValue('width') ), it'd be a real pain when you have to resize a lot of elements correctly.

    It seems that FF5 has fixed this behaviour. Sadly, the issue with IE9 still remains.

    #4 by: MaxArt on July 5, 2011 at 03:14
  • you can try set the body's height 100% ,and use get the body's height ,you can get the window 's offsetHeight.

    #3 by: leo yin on July 2, 2011 at 17:03
  • admin

    Good question :) As far as I can see, in FF4 getBoundingClientRect() returns even preciser float values for width/height than getComputedStyle() (e.g. 1994.36669921875) while in IE9 the returned values are integers and are equal to offsetWidth/Height. So both browsers are inconsistent here.

    #2 by: Vasil Dinkov on June 13, 2011 at 22:47
  • Does it affect getClientBoundingRect() too? That's not available on FF2 or Safari3 of course.

    #1 by: Oliver on June 13, 2011 at 16:33