A cool new browser event just recently landed in the latest Firefox nightlies: an event announcing when the browser re-draws a portion of the page.
This particular event, called MozAfterPaint fires whenever something is drawn to the screen.
The event object contains two properties: .clientRects and .boundingClientRect, both of which refer to the result of the associated DOM methods.
In a nutshell, boundingClientRect gives you a single rectangle encompassing the entire area in which a paint operation could’ve taken place whereas clientRects gives you a number of rectangles, each encompassing an individual area that was drawn.
To test this I created a quick demo using CNN.com (only works in the latest Firefox nightlies).
To use the demo let the page load then move your mouse around (triggering new paint events). When you’re done simply click anywhere on the page to draw the overlay.
You can use the tracking script on any page, simply copy this bookmarklet to your toolbar in a Firefox 3.1 nightly: Track Paint. Then activate it on a page where you wish to track paint events. (Clicking anywhere on the page will show the results.)
For example I ran the bookmarklet on the jQuery.com homepage, moved my mouse over the navigation, hovered over the tooltips, and finally focused on the checkboxes before clicking – creating this complete visual effect:
The important part of the tracking code is as follows:
function log(e){ store.push( e.clientRects ); } // To start logging... window.addEventListener("MozAfterPaint", log, false); // After logging is done... window.removeEventListener("MozAfterPaint", log, false);
You have to be sure to do all of your logging in a separate part of the application (don’t do any rendering inside the MozAfterPaint event handler, otherwise you’ll cause some recursion to occur). Thus before you render the results the MozAfterPaint handler should be removed from the page.
It’s likely that the primary use of this event will be within extensions (such as Firebug) mostly due to the fact that the overhead incurred by initiating this event is so high.
Nonetheless I think this addition to the browser is absolutely fantastic. Developers have been craving more information about what’s going on underneath the hood and this is starting to provide exactly that.
Ryan Breen (October 13, 2008 at 5:33 pm)
Very cool. How feasible is it to use this to determine the point in time at which rendering was completed on initial page load? Is it possible to do that unambiguously?
Render time would be a nice metric to add to the stable of DOM complete, onload, etc.
Bertrand Le Roy (October 13, 2008 at 5:53 pm)
Pretty cool but would it be possible to also get a list of DOM elements that were redrawn? That would enable even more scenarios, right?
kourge (October 13, 2008 at 8:31 pm)
This is some awfully useful feature for debugging; it is akin to what Flash Player provides with “Show Redraw Regions” ever since version 8.
(http://livedocs.adobe.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001768.html)
Mike Rundle (October 13, 2008 at 10:19 pm)
Man this is really cool. Of course now that you’ve written about it, no doubt the WebKit guys are busy working on their own version lol.
JulienW (October 14, 2008 at 1:34 am)
I suspect this feature exists primarily to support ARIA live regions.
Tom Robinson (October 14, 2008 at 3:29 am)
Nifty.
I’ve modified the example to act more like the “Quartz Debug” app on OS X, flashing updates in real time: http://tlrobinson.net/blog/?p=56
Eric Shepherd (October 14, 2008 at 7:13 am)
The docs for MozAfterPaint now include a copy of this script because it’s a great example. Thanks, John. People like you make my job easier. :)
John Resig (October 14, 2008 at 8:16 am)
@Ryan Breen: Well, you could track the repaints that occur before window.onload and see when the last one was – that’s a start, at least.
@Bertrand: It’s not really a 1-to-1 correlation between DOM elements and the area re-drawn. Frequently what’s redrawn is a sub-section of a DOM element. I use to think that having events relate directly to DOM elements would be better but I’m less sure now.
@JulienW: Don’t think so, no. Firefox has had live region support for quite some time now (and as I understand it, live regions is slightly different – tracking DOM mutations rather than the end rendering result).
@Tom: That’s pretty cool – I wonder if it would be sufficient enough to do a 0ms timer delay.
@Eric: No problem :-)
mosquito (October 14, 2008 at 12:18 pm)
Super cool. The browser war is still alive and well !!
Tom Robinson (October 14, 2008 at 8:46 pm)
@John: Delay of 0ms works fine (no recursion problems), but the flash is so brief it’s difficult to distinguish sometimes.
As a compromise I added the code to re-enable and record the paint events during the 100ms flash, then display them in the next 100ms “round”. This seems to work pretty well.
Jared Verdi (October 17, 2008 at 9:55 pm)
Very cool indeed. Could this also be used to get around the window resize even happening only when the mouse is released.. performing some javascript as the viewport is being resized?
Boris (October 18, 2008 at 11:10 am)
Tracking repaints before onload probably won’t work, since the final _paint_ can well happen after onload fires.
Callum (February 11, 2009 at 2:36 am)
This might be useful for another reason: I’ve often wanted to listen for changes to the
window.location.hash
. When someone clicks their back button, sometimes it just changes the URL #fragment without really changing the page (noonload
event is fired). The only workaround I’m aware of is to poll thelocation.hash
every couple of seconds, but that’s slow and ugly.But this MozAfterPaint event gives me an idea. In CSS, you could add a
:target
pseudo class to furtively change a background colour or other property whenever a certain element is targeted in the ‘hash’, and then use the paint event of that element to fire whatever function you like.