Getting element dimensions: A follow up
In my previous post I discussed the tragedy of the getBoundingClientRect() method and its different implementations. Reader Jose Jeria pointed out that Opera 9.5 has implemented the method as well and has used the Firefox method of originating at (0,0). Distraught over the need to use browser detection for a cross-browser solution, I started playing around a bit and came up with a solution that works in Internet Explorer 7, Firefox 3, and Opera 9.5.
The solution hinges on the fact that IE7 reports the position of document.documentElement at (2,2), whereas the other browsers report it at the expected (0,0). In that case, you can check these coordinates and figure out what to do. For example:
function getBoundingClientRect(element){
if (typeof arguments.callee.offset != "number"){
arguments.callee.offset = -document.documentElement.getBoundingClientRect().top;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
}
The theory behind this function is that it checks the location of document.documentElement only once, the first time it’s called, and stores an offset value on the function itself. That offset value will be 0 for Firefox/Opera and -2 for IE7. All of the dimensions are augmented by this offset before being returned. This works great…unless you’re using IE6.
IE6 returns (0,0) as the coordinates of document.documentElement in both standards mode and quirks mode…which makes no sense because an element absolutely positioned to (0,0) returns (2,2) from getBoundingClientRect(). So my initial test is obviously not optimal. However, since I know that an absolutely positioned element at (0,0) returns (2,2), I can create a new element, position it there, and check the reported coordinates:
function getBoundingClientRect(element){
if (typeof arguments.callee.offset != "number"){
var temp = document.createElement("div");
temp.style.cssText = "position:absolute;top:0;left:0";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
}
I have to say right off the bat that hate this solution. The idea that you need to create an element, measure it, and then destroy it really feels dirty to me. However, this solution does work in all browsers I tested and it only does that initial calculation once, so it’s not a recurring performance hit. Technically, this qualifies as quirks detection but true browser detection would be less expensive.
Side note: Firefox has supported getBoxObjectFor() for a while, but it’s going to removed in future versions. This method was intended for use in XUL only and only leaked into HTML world due to a poorly constructed class hierarchy.
Disclaimer: Any viewpoints and opinions expressed in this article are those of Nicholas C. Zakas and do not, in any way, reflect those of my employer, my colleagues, Wrox Publishing, O'Reilly Publishing, or anyone else. I speak only for myself, not for them.
Both comments and pings are currently closed.




2 Comments
Nicholas,
I think you’re making an assumption
about initial scrolling position of a document (more specifically, that there is no vertical scroll when the method is called for the first time).
But let’s assume that the first method call occurred when document is scrolled for example 50px vertically. Then offset property value will be 48px for IE and 50px for Firefox and Opera.
So you’ll end up with adding the same vertical offset value to all the properties returned by native getBoundingClientRect() method. And it will be added to both vertical and HORIZONTAL properties.
And since the result of the initial offset calculation is cached, then regardless of the future scrolling position of the document the same erroneous value will be added for all subsequent calls.
Am I wrong?
Alexei Gorkov on March 6th, 2008 at 9:19 pm
Indeed, Alexei, you caught me in an assumption. I believe that you are correct and scroll position would have to be taken into account for this to work correctly in all circumstances.
Nicholas C. Zakas on March 7th, 2008 at 12:23 am
Comments are automatically closed after 14 days.