getBoundingClientRect is a method, first introduced by Internet Explorer 5 (and now included in Firefox 3), which provides the top, right, bottom, and left offset coordinates of an element. After PPK mentioned this method in a recent post of his, poo-poo-ing its applicability, I thought I’d take some time to show how useful it can be. What makes this method so especially handy is that the alternative means of computing an element’s offset position is so horribly slow and buggy that it borders on the comical.
The general purpose of this method, or of similar implementations, is to allow the user to get the visual top/left position of an element (such that if they absolutely position an element from the document it would be on top of the desired element – making things like tooltips possible, to name a single application). This means that you have to take into account a huge number of quirks. In no particular order here are some choice bugs which make this very difficult:
- Handling table border offsets.
- Fixed positioned elements.
- Scroll offsets within another element.
- Borders of overflowed parent elements.
- Miscalculation of absolutely positioned elements.
For example, looking at jQuery’s implementation of .offset() (which provides the top/left offset of an element) we can see a number of these quirks worked around. A phenomenal amount of work has gone into the method, by the excellent Brandon Aaron, in order to “get it right” in a cross-browser manner. Check out the extensive suite of tests that are backing it up, as well.
This is where getBoundingClientRect comes into play. For a visual comparison look at the green boxes (representing the getBoundClientRect-based implementation) versus the red boxes (representing the normal cross-browser implementation).
The reason why the getBoundingClientRect implementation is more than 1 line is due to two facts:
- It doesn’t include the document scroll offset (which is a simple addition).
- Internet Explorer’s handling of <html/> element border widths is completely whack.
In comparison to the default technique, however, this is a veritable walk-in-the-park.
Now, not only, is this method simpler to use it’s also insanely faster. Stupifyingly so. Considering that you no longer have to perform all sorts of hacks, or walk the tree to rectify miscalculations, the result is quite impressive.
View the following jQuery UI drag-and-drop sorting demo page in Firefox 2 (drag the items in the grid, at the bottom) and then view it in Firefox 3, doing this same:
http://dev.jquery.com/view/trunk/ui/tests/sortable.html
Night and day. I’ve also uploaded a simple movie demonstrating the difference.
I absolutely encourage everyone to check this method out. It can serve as a near-drop-in replacement for your offset needs – and will gracefully work in new browsers that support the method.
Mike Fitzgerald (March 18, 2008 at 1:17 am)
I might have to spend some time getting my thesis code working on Firefox 3; this method alone would make it run considerably faster! I have no doubts about its applicability, at least as far as making my own life easier. :)
Robert O'Callahan (March 18, 2008 at 2:55 am)
There’s a spec for getBoundingClientRect which is fairly thorough, for example it defines how getBoundingClientRect works on SVG elements…
http://www.w3.org/TR/cssom-view/
In FF3 we’ve also implemented getClientRects, which is pretty interesting for complex elements like inlines that contain line breaks.
trevelyan (March 18, 2008 at 7:54 am)
This is a major and much-needed improvement for Firefox. Thank you for blogging about it.
I work on a web service that uses a javascript-API to semantically analyse text and provides information about it through a popup. Positioning the popup on the page is a cross-browser mess. Solving this problem really matters for web-services that do content display through remote APIs.
James Dempster (March 18, 2008 at 8:16 am)
Just another reason why jQuery and the likes are so important.
This abstraction from the JavaScript / DOM allows the library to implement lovely features and then seamlessly accelerate them using browser specific optimizations.
I love jQuery for these reasons and more. It make cross browser scripting so much easier.
It’s great to see things like this.
timothy (March 18, 2008 at 8:27 am)
Does jQuery1.2.3 have this built in? Or is it due for a later version of jQuery?
Brandon Aaron (March 18, 2008 at 9:12 am)
The offset method was moved to the core in 1.2. The specific usage of getBoundingClientRect has been there since its first appearance within the core.
Hedger Wang (March 18, 2008 at 12:33 pm)
Hi:
I wonder why not use Dom.Element.getBoxObjectFor() to get the dimension and position of an element on Firefox? It had been available since Firefox 1 and I found it very useful just like getBoundingClientRect();
Brandon Aaron (March 18, 2008 at 1:07 pm)
It is primarily for XUL apps and the usage within HTML was discouraged. I believe the last time I looked at it, it did not pass all the required tests.
Bertrand Le Roy (March 24, 2008 at 1:43 pm)
More on the horrible quirks one has to work around in the absence of this method:
http://weblogs.asp.net/bleroy/archive/2008/01/29/getting-absolute-coordinates-from-a-dom-element.aspx
rektide (March 24, 2008 at 7:00 pm)
I used to use jquery dimensions for all this work. It’ll be fun to compare, thanks John!
George E. Papadakis (March 26, 2008 at 7:35 am)
Try nesting an element inside another element having its style.overflow property to hidden.
Then check what happens when you try to grab the getBoundingClientRect() of the nested element in case it is outside the visible area of the container.
Richard Hou (May 29, 2008 at 11:24 am)
I tested document.documentElement.clientLeft and document.documentElement.clientTop in IE6/win and found in both modes (standard and quirks), they can reflect the css settings ‘html { border:0;}’. The test result is different from the comments in offset.js.
Is my test is right?
Richard Hou (May 29, 2008 at 3:51 pm)
In IE6/win quirks mode, document.documentElement.clientLeft and document.documentElement.clientTop are allways 0 no matter what value is used to set html element’s border. On the contrary, in IE6/win standard mode, clientLeft and clientTop properties are always equal to the html’s border width. So in offset.js,
add(-doc.documentElement.clientLeft, -doc.documentElement.clientTop)
only affects IE6/win standard mode, which makes the element’s left and top values small.
Actually, the element’s left and top values are already correct before this adjustment. Those values are relative to the origin of document. But after this adjustment in IE6/win standard mode, those values are relative to the inner edge of html element.
Christian S. (June 2, 2008 at 1:51 pm)
Unfortunately i found out, that getBoundingClientRect does not work, if there is an element with position == “relative” or “absolute” between and the element of question.
The body’s margin is then not added. Event wors when you have a with position:relative, or another relative positioned element with a margin.
This method seems too buggy to me. I tested it on IE 7 / WinXP.
Anybody has an idea?
Christian S. (June 2, 2008 at 2:10 pm)
Ok, i must admit, it was my fault in testing. I positioned an “pointer” element, which was in the body, to the element of question. And of course, if the body is relative, the absolute position of the “pointer” element, refers to the body. Sorry…