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.

Sunday Jan 27, 2008

Pre-compile and cache yer dang regular expressions

We have a function called hasClassName(el, cls) that checks whether a given DOM element has a given class. More and more, we're relying heavily on this function, and on certain pages it can run thousands of times. In these situations a little performance tuning can go a long way, as demonstrated here:

// BEFORE
function hasClassName(el, cls){
  var exp = new RegExp("(\^|\\\\s)"+cls+"($|\\\\s)");
  return (el.className && exp.test(el.className))?true:false;
}
// AFTER
(function(){
  var c={};//cache regexps for performance
  window.hasClassName=function(el, cls){
    if(!c[cls]){c[cls]=new RegExp("(\^|\\\\s)"+cls+"($|\\\\s)");}
    return el.className && c[cls].test(el.className);
  }
})();

This simple change cut the function's average runtime in half and reduced the page's overall onload setup time by over 25%! (According to Firebug.) All because it caches regular expressions.

Edit: removed unnecessary ?: operator per Nate's comment.

Monday Jan 21, 2008

Python Musings and Farkle Babies

Python is my spare time language. I wish I had opportunity to use it during my day job, but alas, JavaScript is my day job language. Not that there's anything so terrible about JavaScript, but I get wistful sometimes switching back and forth between being able to use list comprehensions and not being able to use list comprehensions.

So anyway, I wrote a breeder program in Python that breeds little Farkle-playing babies and sets them all off playing each other. The ones that win the most games survive to the next round and get to have little Farkle-playing babies, passing on their Farkle-playing genes with various mutations. The ones that don't win, sadly, become the victims of the garbage-collector. After a few hundred generations or so they got pretty good at Farkle, starting at about 20 rounds per game on average and working their way to about 16 rounds per game on average. (The idea is to get to 10000 in as few turns as possible.) Because it's ultimately based on random numbers (rolls of the dice), Farkle is one of those games where skill level (good risk-tolerance sensibilities) only gives you a marginal advantage, so in retrospect it perhaps isn't the best candidate for an EA.

One thing that struck me about Python versus JavaScript is that with Python, my tendency is to sit and think for a long time, then write a few lines of code. Whereas with JavaScript, my tendency is to spew code prolifically, thinking as I type. I don't know if this speaks more about my skill level in the respective languages, or the nature of the languages themselves. It definitely feels like I burn more keytypes in JavaScript getting ready to do, whereas in Python I burn more keytypes simply doing. Maybe we web developers should push to have Python implemented in web clients as an alternative to JavaScript, if for no other reason than to save our hands from the strain of typing all day.

Thursday Dec 20, 2007

Cool Solar Atmospheric Effect

Check out this atmospheric phenomenon that appeared around the sun the other day. Do ice crystals in the higher clouds do this?

[Read More]

Wednesday Dec 19, 2007

Here Goes Nothin'

Woo. Leopard arrived yesterday. Gonna install that bad boy as soon as I get a chance.

[Read More]
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