By greimer on Feb 11, 2008
reg library. It provides an object named, naturally enough,
reg. It stands for register. With it, you can register behaviors, like this:
reg.click('ul.foo > li > a.bar', myFunction);
Once that bit of code runs, regardless of whether the entire DOM has finished loading into the browser, click events on elements matching
ul.foo > li > a.bar will be handled by
myFunction, which is passed a reference to the active element and the event object. This happens without any DOM traversal, and without any events ever being attached to any
<a> elements. Even if the entire contents of
document.body are subsequently overwritten by
innerHTML, all those new element nodes implicitly inherit the correct behavior. No re-walking of the new DOM subtree ever occurs. No re-attachment of events ever occurs.
How is this even possible?
Two facts conspire to make this feasible. 1) The
document.body) is available almost immediately. 2) Most events bubble. All you need to do is to stand on
document.body, and you're privy to almost every event that occurs on the page, right out of the gate. No need to go seeking out the elements you need, they literally bubble their way to you. All you do is grab the event's target and ask the question, does this element, or one of its ancestors, match 'ul.foo > li > a.bar'? If so, run the associated function, if not, ignore it. This is really just event delegation, and it's nothing new, but we've made little use of it on Sun.COM before now.
Limitations, caveats, dangers
I harbor no illusions this is a perfect solution. There are limitations. Besides the fact that not all events bubble, much of time, behavior depends exclusively on preprocessing, especially if you're doing progressive enhancement. You don't want non-JS users to see useless expand/collapse signs or widgets laying around, so you build the widgets only if scripting is enabled. And the only time to build the widgets is
onload. And the only way to build the widgets in the right place is... \*sigh\* traversal. Some of this can fortunately be avoided by relying as much as possible on CSS and making your widget styles depend on a dynamically-added class name, such as
body.jsenabled, so there's some workaround potential at least.
There's also an inherent performance limitation. Sitting in the driver's seat on
document.body isn't always a relaxing cruise through the countryside. It can easily turn into rush-hour gridlock, with a flood of events each demanding to be checked. For that reason, I dare not use this technique to handle
mousemove events. That would cause a veritable torrent of events. Even
mouseover events are iffy. We have the capability and it appears to work reasonably well, but time will tell whether it's really viable. Click events, on the other hand, because of their relative infrequency, are a good candidate. Of course, as we develop this thing further, we'll be looking for ways to mitigate performance risks.
So there you go
It isn't live yet, but hopefully will be soon. After this code has been chugging away out there in the wild and woolly world of production Sun.COM for a while, I may post more about this, what works, what doesn't, unexpected wins and losses, etc. Stay tuned.