Thursday Apr 03, 2008

A SAX Parser Based on JavaScript's String.replace() Method?

I've often wished browsers would offer native SAX implementations. SAX is lightweight and fast. Not only that, SAX is easy because it lets you ignore what's not interesting, unlike DOM, where you have to traverse the whole mess and keep it hanging around in memory. SAX also uses callback functions, which any JavaScript programmer should feel comfortable with.

[Read More]

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, 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:' > li >', myFunction);

Once that bit of code runs, regardless of whether the entire DOM has finished loading into the browser, click events on elements matching > li > 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 ' > li >'? 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 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:

function hasClassName(el, cls){
  var exp = new RegExp("(\^|\\\\s)"+cls+"($|\\\\s)");
  return (el.className && exp.test(el.className))?true:false;
  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.

Wednesday Aug 22, 2007

Front-End Quiz Part 6, Separating Content, Behavior, Presentation

Where/how is content best separated from behavior and presentation?

  1. On the client-side, using semantic HTML plus external JS and CSS files.

    This is where I set up camp. Server-side methods exist for separating content, presentation and behavior, but they have different ideas of what "presentation" and "behavior" mean. In terms of separating out the visual presentation and client behavior layer, CSS and unobtrusive JavaScript should be the first line of defense.

  2. On the server-side, using features of your rendering framework/MVC architecture.

    If, by "presention" you simply mean HTML code, and by "content" you mean information stored in the application, and by "behavior" you mean controller logic, then yes. On the other hand, if you're coding font tags, layout tables, spacer images, onclick="" and onload="" attributes directly into your JSPs or page renderers, then I think that's a serious red flag. With CSS handling the visual details and unobtrusive JavaScript handling client interactivity, your web renderer is free to focus on saying "now a heading," "now a list," "now a submit element," etc.

Wednesday Aug 15, 2007

Front-End Quiz Part 5, Users who Disable JavaScript

What's the best approach for dealing with the approximately 7 - 10% of visitors who disable JavaScript?

  1. Preemptively send them to a sorry, but you must activate JavaScript in order to use this site screen.

    Not a good way to make friends. This is like saying, "go away, we don't want you" or, "we only want 93% uptime for our website." I think the business case for this is rare indeed.

  2. Condition your plan on building it to work without JavaScript, then use JavaScript as an enhancement.

    I like this approach the best. This is known as the "progressive enhancement" method, and it's the best way to build bullet-proof websites in the modern age.

  3. For each feature needing JavaScript, use an inline this feature requires JavaScript <noscript> section.

    Still not very friendly. This is marginally better than blocking access to the whole site.

  4. Place a block of alternative content in a <noscript> section containing equivalent functionality.

    Workable, but inefficient. <noscript> functionality is really a suboptimal solution, since it requires you to basically code your functionality into the page twice.

  5. Just let certain features break, it's not really that big a deal.

    Possibly confusing. Not only do visitors not get the functionality they were looking for, now they're confused as well, without any explanation of why it won't work.

  6. Avoid using JavaScript in the first place.

    Maybe not the best, but it's an option. JavaScript offers a lot of capability that has come to be expected on the web. Your site might seem outdated without it.


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


« July 2016