Monday Feb 11, 2008

A Look at Sun.COM's New Event Delegation Library

(A followup to my last post.) We're on the brink of releasing a new JavaScript mini-library to sun.com, which we call the 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 <body> (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.

Monday Feb 04, 2008

On the Inelegance of Current JavaScript Paradigms

As cool as unobtrusive JavaScript is for building the behavioral layer, it's nevertheless based on some pretty kludgy foundations, especially when contrasted with CSS's rather elegant method of declaratively building the presentation layer.

Kludge #1: Waiting for DOM load.

It seems unavoidable. If you want to be unobtrusive, there will be a delay before your functionality is available. Furthermore, DOMContentLoaded and friends don't truly solve the problem. Interestingly, a parallel problem exists in the CSS world: the FOUC (Flash Of Unstyled Content). FOUC is generally not a problem in modern web clients. I wish I could say the same for the "flash of behaviorless content" problem.

Kludge #2: Procedural DOM traversal and event attachment.

Why do I have to seek out the elements that interest me and add behavior, element by element? What a pain. On sufficiently complex pages this means walking the entire tree. If you add new elements as you walk the tree, hall-of-mirrors-style infinite loops and other pitfalls plague you. If innerHTML gets added during the course of the page's life, you have to go back and re-walk those parts of the DOM, re-attaching events as needed. Once again, contrast this with CSS, where the user agent abstracts away the traversal and the attachment of styles, allowing you to declare once--up front--which elements get which styles. Pity we can't do it this way for the behavior layer too.

So what?

Of course these are well known problems. Such is the hand we've been dealt, and tools and techniques exist that make these problems less painful, so why the fuss? In my mind anyway, leaky abstractions on top of kludges aren't an ideal state of affairs, and so I rant. But I also wanted to establish a little background for a future post where I'll describe some tools we're about to deploy on sun.com that, in certain instances, avoid these problems altogether.


[Update]: I've posted a followup with a bit of information about how our new library works.

Wednesday Aug 01, 2007

Front-End Quiz Part 3, Adding Events to Elements


What's the best way to add JavaScript "onclick" event behavior to a link?

  1. Add an onclick="" attribute to the <a> tag.

    My opinion: this is a nasty, outdated way of doing things that needs to be euthanized ASAP. It's the most prevalent method as of 2007, but mixing scripting commands with HTML is just as bad as mixing presentational commands with HTML, and for the same reasons. Presentation should be removed from content via external CSS files, and behavior should be removed from content via external JavaScript files. Even if it seems justifiable to throw in a quick onclick="" attribute for convenience's sake, it risks getting accidentally overwritten by more sophisticated event attachment methods that deploy via external scripts.

  2. In a script, use document.getElementById("someId").onclick = function() { ... }

    Better, but still nasty, IMO. This method is a vast improvement over inline event handler attributes such as <a onclick="">, but it still only lets you assign one event handler per element, and succumbs to the common problem of events being accidently overwritten, since there is only a single onclick property per element.

  3. Use event attachment, preferably via some kind of event library that abstracts away browser differences.

    My personal favorite. Event attachment lets you assign an arbitrary number of events to the same element. This is especially useful for the window "onload" event, which often needs to spawn multiple actions.


About

My name is Greg Reimer and I'm a web technologist for the Sun.COM web design team.

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today