Archive for May, 2008

Performance is in the Details

This past week, some colleagues and I sat around a brainstorming table trying to isolate what was causing a particular page to load incredibly slowly in Internet Explorer 6. Drawing on our combined years of experience, and our mutual disdain for IE6, we came up with what we believed to be a solid list of potential bottlenecks that included (among other things):

  1. DOM Parsing (the DOM is very large for this particular page)
  2. Slow / Inefficient Ajax requests
  3. Parsing large JSON strings returned by Ajax request
  4. Too many event handler assignments

We tested each of these theories among the 5-6 others that we had collected and could not find any particular area that was acting particularly slow. Unfortunately, the lack of any serious profiling tools for IE made the task of doing real benchmarks almost impossible. We scratched our heads and adjourned for our 3-day weekend.

In the process of fixing random software defects, one of our other lead engineers stumbled across this line of code that one of our more junior engineers had checked in:

for(var i=0; i < $$('div.contents').length; i++)

My head was spinning just looking at that one statement. For those unfamiliar with Prototype, the $$ method is a sometimes useful, but horribly inefficient means of searching the DOM. In plain English, this method will walk the entire DOM of your document, and check if it matches the CSS selector contained within, and add a reference to that element to an array if it does. When the traversal is complete, the array is returned. Do you see the problem now? Each time the loop ran (conceivably 50+ times in this case, however many elements matched div.contents) it would have to do an entire DOM traversal. Additionally, it would have to calculate the length of the returned array each time!

For reference, as a service to all of the other engineers-those who don't yet know as well as those who have had to clean up after someone who didn't know, better practice would be:

var contents = $$('div.contents');
var length = contents.length;
for(var i=0; i < length; i++)

However, it would be far more advisable to restrict your search of the DOM using prototype's Element.select method to only check the portions of the DOM.

The important take-away here is that you need to exercise extreme caution when using any method that traverses the DOM; especially as long as IE6 is a large player in the browser market as its DOM traversal is notoriously slow. Prototype's $$ is an especially dangerous method (even when used properly) and should only be employed in cases when it truly makes sense—that is, when you need to check the entirety of the document. Far more often, you will know a containing element from which to base your search:

var oddRows = $("container").select("li.odd");

Finally, always remember to calculate all of your parameters outside of a for statement (as in example 2 above). Doing so as part of the statement frankly reeks of the kind of amateur development practice one might get from reading a collection of some of the worst technical books on the market as for some reason they all seem to advocate this kind of thing.

DOM Traversal is expensive. Know what you're looking for and determine the best way to find it; not the easiest.

Technorati Tags: , , , , ,

Keeping Track of Focus

One annoying limitation of Javascript is that there is no easy way to track which element currently has focus. In general, we don't really need this information; however, I've recently run across a case where it would be a good idea to be able to know which element currently has focus. Here's a little prototype based Javascript that provides a simple way to track focus throughout your document using event delegation:

var FocusReader = Class.create({
 
    initialize: function(){
 
	this.currentFocus = null;
        var focusRead = this.focusRead.bindAsEventListener(this);
        document.observe("focus", focusRead);
    },
 
    focusRead: function(e){
        this.currentFocus = e.element();
    },
 
    getCurrentFocus: function(){
        return this.currentFocus;
    },
 
    getCurrentFocusId: function(){
	if(this.currentFocus.identify) return this.currentFocus.identify();
    }
 
});

Notice how we've used event delegation to ensure that we can see the focus anywhere in the document without having to add an event handler to every element that may receive focus. We've also provided two ways to represent the focused element--a pointer to the element itself as well as its ID.

Download: focusReader.js (1kb)

Technorati Tags: , ,

An Introduction to Javascript Event Delegation

As development in Javascript moves further away from rollovers and other simple effects into developing true Rich Internet Applications, the number of events we have on our pages increases seemingly exponentially. Catch a left click, a right click, focus, blur, etc.--we're constantly pushing the boundaries of the Javascript event model. Through all of this, one aspect of Javascript events very often gets overlooked: event bubbling and it's power for enabling event delegation.

Reading the specification is often overly complicated, so I will summarize. Event Bubbling specifies that a Javascript event will not only register for the actual element on which the event occurred, but on all of that elements ancestors all the way up to document. For example say we have a form with a text box and we want to perform some action when a user clicks on the text box:

<form onclick="handleClick()">
    <fieldset>
          <input id="bubbleexample" type="text" />
    </fieldset>
</form>

Notice that our event handler has been set on the form and not on the text box itself. When the text box is clicked, the event generated will first be called on the text box itself. Then it will move up to the fieldset. Next it will hit the form element. It will continue to affect each ancestor element until it reaches the root element (document). This is called event bubbling.

Notice also that we are handling that event on the form element and not on the the text box itself. This is called event delegation.

You may, at this point, be asking yourself: what's the advantage of this technique? In the case above where we are only looking at a single event, there is no advantage. However, as the number of event handlers on your page grows, you can see significant performance increase both in terms of processing time and memory use. A complete discussion as to the exact reasons why is beyond the scope of this tutorial; but the important aspects are:

  1. The event handler only has to be applied to one element (reduces processing time)
  2. Only one copy of the event handler will reside in memory (reduces memory consumption)

In this example, I've applied event handlers using event delegation, and included metrics regarding processing time differences between using delegation and adding an event handler to each item individually.

There is, of course, a caveat to using event delegation: events you had no intention to handle will likely need to be handled gracefully by your handlers. Notice on line 12, of the example, the event handler checks to make sure it is not acting on the parent container; only the elements within that container. Obviously, as the DOM becomes more complicated beneath your chosen delegator element, so too will become the handler that must check what element it is being called on.

That said, the benefits do outweigh this potential pitfall. As you'll notice the code for the handler itself merely determines which element it was called on, checks that the element qualifies for action, and passes the element to another function which performs the actual action. This is good architecture; as the code to determine what to do with a particular event grows and multiple paths become possible, your code will be much easier to read and work with when you separate the actual action from the event handling.

So there you have it; a very brief introduction to using event delegation and its advantage over the more traditional paradigm.

Technorati Tags: , , , ,

JavaScript Event & Event Method Bugs and Workarounds

Today I spent a good deal of my time dealing with Javascript event handling and delegation using the Prototype javascript library with relation to some forms in our current project. In addition to simply firing and catching events using actual events, this applications also make use of the click() and focus() methods to fire these events in certain circumstances without user interaction. The issues I'm discussing here are specific to radio buttons and checkboxes across the three major browsers--but primarily focused on Firefox and Safari. The provided example does not use Prototype--it is plain vanilla Javascript. However, the proposed workaround does rely on the Prototype library (sorry, it was just faster that way--and it's also the implementation I used to solve the problem on my own).

What (I believe) the spec calls for:

When a radio button or checkbox is clicked with the mouse, the mouse click event will fire with it's target object set to the radio button that was clicked. The radio button will also gain focus.

Both Internet Explorer and Firefox exhibit this behavior exactly (on true user clicks--we'll get to the event simulation methods shortly). Safari, however, only fires the click event. The checkbox or radio button that was clicked does not receive focus as it should.

So let's just always observe clicks and leave focus to the birds

I told you we'd get back to the event simulation methods in a minute. Both Firefox and Internet Explorer exhibit some strange behavior when using these methods vs. actual events. In Firefox, the click() method generates an event with the target set to the calling element--not the actual target of the click. This is inconsistent with both Firefox's focus() method and Internet Exlorer and Safari's handling of both the click() and focus() methods. This example (Firefox, Safari, and Opera compatible) demonstrates the issue.

So where does the problem start?

In the case when you need to force a particular radio button to be selected by default and you have additional Javascript logic that must run based on that selection. Let's say you have 4 radio buttons as in the previous example and you wish the 3rd radio button to be selected by default (we'll assume there's some Javascript logic that must happen based on the user's selection that must also occur with the default selection). Because the user will not be interacting with the default selection, we must rely on event methods to fire our events. So we have some challenges:

  • If we use click() and have our radio buttons listen for a click, Firefox will believe the target of the event is document. The radio button will be selected, but any additional logic based on knowing what was clicked (using event.target) will fail.
  • If we use focus() and have our radio buttons listen for focus, Safari will see the radio button receive focus correctly when the page loads as we will be using focus(). However, any actual user interaction will fail as the focus() event will not fire.

A workaround

I intend to open a bug in the Firefox bug tracker for this issue (if one is not already open; I didn't find one with a quick glance through Bugzilla). Until then, I've written a little workaround that requires the Prototype library to function.

First, ensure that your radio buttons are set up to respond to focus events (since we know that Firefox will only react properly to those events when called programmatically.

$('my-form').getInputs("radio").invoke('observe', 'focus', eventHandler);

Now, observe for clicks to forward on:

$('my-form').getInputs("radio").invoke('observe', 'click', fakeClick);

Your fakeClick method should look like this:

fakeClick: function(e){
   var el = e.element();
   if(el.identify) { /* Filter out click() for FireFox */
      if(this.focused.identify() != el.identify()) {
         e.element.focus(); /* Throws the proper focus() for Safari */
      }
   }
}

The other aspect of this is that within your actual event handler, you need to be sure to set the this.focused to the element that currently has focus.

I've created an example Prototype-enabled JS class to show how the functionality works.

Conclusion

So there you have it, some basic information about a bug in Firefox and a bug in Safari that together make for some interesting times when handling events; and a workaround which I hope you will find useful in getting around these two bugs. Please note that the code is more of an example on how to implement it; though it can be copied verbatim if you wish.

Download ClickFix.js (2kb)

Technorati Tags: , , , , ,

Adobe Flex 3: Training From the Source - Finally, a good Techincal Book!

I've been reading technical books for years on topics ranging from beginning programming guides with C to Adobe Photoshop tips and tricks.  Like most people in this field, I've long been a fan of O'Reilly books (I think my first one was a Perl 5 book somewhere around the 1999 timeframe).  However; recently, I've been hearing some negative things about their books and haven't really picked any up.  My most recent one is a several-editions old version of Javascript: The Definitive Guide.

I was also a fan of the Pragmatic Programmers Agile Web Development With Rails that I managed to purchase just at the wrong time--about 2 weeks before Rails 2.0 was released.  While the book was well written, it has been less than helpful with regard to learning to use Rails.

Despite these (and other) gems, I think most agree that technical books suffer from a lot of problems and are generally very poorly done.   I'm happy to say that I've found my most recent technical book purchase, Adobe Flex 3: Training From the Source to be among the best technical books I've read in many years.  It follows the paradigm of taking you step-by-step through building an application--and does so in such a way that it is easy for novice Flex developers to follow; and at the same time allowing more experienced developers to skip over details that aren't needed.  The book is organized into 26 "Lessons" each adding to the features of the application and employing new concepts.  One of the great parts about these Lessons is that each of them begins with a summary page that gives solid insight into what topics will be covered, and a surprisingly accurate estimate of how long the Lesson will take to complete.

If you're looking to get into Flex development, I highly recommend this title from Adobe.

Amazon: Adobe Flex 3: Training from the Source

Technorati Tags: , , ,

Design for Objectives; Not Requirements

Yesterday I was evaluating early design comps for a new project just getting underway here at AutoTrader.com and I fell into the trap of what I call Designing for Requirements, Not Objectives.

Neil and I were discussing the new versions of the comps that have surfaced since we returned from San Francisco and the improvements around them--at least, some of them.  With one comp in particular, Neil began to repeat the phrase "yeah, but it's not poppin' off!"  and the only argument I seemed to be able to formulate in reply was "True, but it's doing this to meet business requirement x."  After a few minutes of this, I was forced to concede the point that Neil was right and I was arguing from a hollow perspective.  Ask anyone who knows both of us and they'll tell you that Neil has taken to using me as affirmation--as in, "Oh you know I must be right if Crescimanno is agreeing with me!"  Long story short, we disagree often--if only as an exercise to keep the other thinking on the right path.

I'd fallen into the trap of seeing the design purely as a function of meeting the business requirements that have been defined for the project and failed to evaluate it based on the stated project objectives.  While the design presented did a fantastic job of meeting all of the business rules and requirements that had been created around it, the design completely fails to accomplish the primary goal of the project.  When there are many, many forces pulling projects in many, many directions, it's easy to get lost in things like business requirements, advertising specs, reporting, and other measures and to lose sight of the reason the project exists in the first place. It happened to me here; it's happened to many others in the past, and it will likely happen to others in the future.

In design, we often have to cater to multiple, often very disparate groups.  That's certainly the case in this project, and while this design caters to one group quite well--it doesn't serve the other at all.

Remember, when designing anything:

  1. Take into account all of your audiences and do your best to serve all of them (recognizing that no one perfect solution for all of them at once exists)
  2. Always keep the primary objectives of the project at the forefront of the design--and fit the business requirements within that framework.

At the end of the day, you can satisfy every business requirement in the specs--but if your design fails to meet it's objectives, it won't be successful.

Technorati Tags:

Adobe’s Open Screen Project: What does it mean?

Adobe today announced the creation of the Open Screen Project to help facilitate the adoption of Flash and Flash-based technologies on a wide range of platforms. There's plenty of coverage out there today about this initiative--and it's no surprise. For years developers have been calling for an opening up of Flash and it looks like Adobe is ready to give it to us.

The big takeaway: Developers are now free to create their own versions of Flash Player.

First, this is fantastic news for the open source community and projects like Ubuntu who now have the opportunity to create a true "free software" version of the Flash player. Free software purists around the world rejoice!

But there's more to it than just Free Software. Apple's current SLA for the iPhone SDK would not allow Adobe's Flash Player to be ported to the iPhone platform (barring special exception, which I'm not entirely convinced Apple would be unwilling to give). Could Apple develop its own version of Flash Player optimized for the iPhone? The Open Screen Project certainly looks that way--and I honestly can't help but wonder if that's part of the reason Adobe's decided that now is the proper time to announce this project. Given the number of partners (and the conspicuous-in-their-absence-Apple) I'd hesitate to say that the iPhone SDK was a cause for this action; but it certainly may have influenced the timing. One thing's for certain; it very much puts the ball back into Apple's court on the issue of Flash on the iPhone.

I am one of the camp that believes Apple has no interest in seeing Flash on the iPhone. Flash competes too directly with a major Apple technology--Quicktime--for Apple to want to see it there. That said, with the onus now on Apple with regard to Flash on the iPhone, I think the boys at Adobe might have out-maneuvered ol' Steve on this one.

And that might be the most impressive part of it all.

Technorati Tags: , , , , , , ,