JavaScript Speeds

The JavaScript engines of popular web browsers vary greatly in performance. The variance in performance is surprising.

Running this test: http://celtickane.com/labs/web-browser-javascript-benchmark/

On my laptop, here are my results (lower is better, your results my vary):

Safari 4: 128
Firefox 3.5: 233
Camino: 615
IE 7: 1181

IE 7 is almost 10 X slower than Safari 4. With more and more sites building rich interfaces and AJAX driven interaction this can be a serious issue. Recently I was working on a redesign of some category navigation pages. The new interface used AJAX loaded content and multiple tabs to display content. It worked great under Firefox, IE 6, IE 8, Safari 3, Safari 4, but was painfully slow under IE 7. Slow enough that in deference to the high number of customers still using IE 7, and the potential sales loss, we had to remove much of the rich interface.

Be sure to test your rich interfaces in IE 7 and pay attention to performance. Until people move off of IE 7, we are all somewhat hamstrung with regards to how much JavaScript and AJAX we can use on eCommerce sites.

One thing to keep in mind is to try to limit the number of AJAX requests. IE 7 (and many other browsers) limit concurrent requests to 2, meaning if you’re loading 12 fragments via AJAX, it will take 6 full requests/response cycles, each one taking ~200-500 ms. That adds up very quickly. You can pull everything down in one large AJAX request, parse that response’s DOM tree, and extract the pieces you need, but A) that’s more JavaScript you’re using, and B) at some point you’re better off just loading a new page.

Another thing to consider is that while JavaScript and AJAX allow us to build better interfaces for our users, allowing them to be better served by our sites, it’s often easy to get distracted by what you can do, and by buzzwords, and lose sight of what actually benefits your users and what doesn’t.

Friends don’t let friends use IE 7.

Why I like jQuery (over Dojo)

With the caveat that I’m not an expert with Dojo/Dojox/Dijit, I’m currently switching a site off of Dojo onto jQuery. I’ve used jQuery on a few other sites, but typically just simple effects type stuff. This site has a lot of AJAX driven stuff going on: AJAX catalog navigation where we replace about 12 elements on the page with AJAX loads of other URLs serving back fragments, dynamic tabs which hide themselves if the AJAX source URL returns certain stuff instead of other stuff, etc… I’ve been writing lots of this JavaScript based on the Dojo framework, as that’s what was handed to me, so I’ve done a lot of on the job Dojo learning. I was able to get things to work, so that was good. However, now that we’re transitioning off of Dojo and onto jQuery, I’m amazed at how much easier the jQuery framework makes things. Aside from being smaller, simpler, and easier to understand (for me at least) I’m able to do the things I want to do, much more easily.

*note: some of the examples might be bad Dojo coding (some mine, some inherited) or might be due to me missing the “best” way to do something, however with both Dojo and jQuery I’m mostly using their documentation and/or Googling for how to do something, so any failings in the Dojo implementation are related to the quality and clarity of the documentation and available related pages.*

For instance, the Dojo code that was handed off to AJAX load a page into a div looks like this:

	dojo.xhrGet( {
	    // The following URL must match that used to test the server.
	    url : url2,
	    handleAs : "text",
	    timeout : 5000, // Time in milliseconds

	    // The LOAD function will be called on a successful response.
	    load : function(response, ioArgs) {
		dojo.byId("brandsDiv").innerHTML = response;
		return response;
	    },

	    // The ERROR function will be called in an error case.
	    error : function(response, ioArgs) {
		console.error("HTTP status code: ", ioArgs.xhr.status);
		return response;
	    }
	});

That times 12 for the 12 divs we’re updating via AJAX. With jQuery I’m replacing that block with:

$("#brandsDiv").load(url2);

Simpler, no?

Also, with Dijit tabs, there’s no easy way to hide a tab and resurrect it later. You have to save off the panel into a JavaScript variable, and reinstate it from that variable later. See how I handled hiding Dijit Tabs/ContentPanes in a TabContainer.

With jQuery Tabs I just use the built-in disable and enable methods along with one line of CSS to solve the same problem. Much easier.

I’m also loving the looks of the new jQuery Tools library from the FlowPlayer folks. Hopefully I’ll be using that library soon on a few projects.

Hiding and Showing a Dijit ContentPane in a TabContainer

There is no simple method of hiding and showing a Dijit ContentPane.

If you are displaying a number of ContentPanes within a TabContainer you may need to hide (and possibly redisplay) one or more of the ContentPanes. For instance if the tabs are displaying information based on a selected product/user/etc… some tabs might not be relevant depending on the selected product/user/etc… If you can change the selected product/user/etc… via JavaScript/AJAX, you need to be able to hide and redisplay ContentPanes.

You can remove a ContentPane by calling removeChild on the TabContainer, however if you want to be able to re-display the ContentPane later, you need to save the Dijit widget to a global JavaScript variable before you remove it.

For example assuming your page has this:

<div id="MyTabContainer" dojoType="dijit.layout.TabContainer" >
    <div id="MyTab" preload="true" extractContent="true" 
        dojoType="dijit.layout.ContentPane" href="somePage.jsp"
        title="My Tab" selected="true" >
    </div>
    ......
</div>

You can hide MyTab like this:

saveMyTabWidget = dijit.byId('MyTab');
dijit.byId('MyTabContainer').removeChild(dijit.byId('MyTab'));

And then, to redisplay, you’ll want to check if the saveMyTabWidget variable has been set, and if so, add it back as a child to the TabContainer, like this:

if (typeof saveMyTabWidget != "undefined") {
	saveMyTabWidget.setHref(theURL);
	dijit.byId('MyTabContainer').addChild(saveMyTabWidget, 0);
	saleProductsTabWidget = undefined;
}

Making Your DHTML or AJAX Application Bookmarkable

As more and more websites are embracing “web 2.0″ standards and evolving into more responsive dynamic applications many of them are losing basic functionality such as bookmark-ability.

In order to present a richer interface or to reduce page load times and improve responsiveness it can be beneficial to alter the content on a page dynamically, using client-side JavaScript and optionally XMLHTTPRequests, without making the browser request a whole new page. This approach is a core technique of the “web 2.0″ umbrella approach to site design and interaction.

For a basic example, envision a product page with the product image and high level information at the top and multiple tabs below that. The default tab might include a detailed product description and color options. The next tab might show user reviews. When the user clicks on the Reviews tab, the displayed tab changes using JavaScript, but the browser does not request a new page (although the review data could be loaded via an AJAX request to speed the initial page load time – although note that this would keep the reviews from being seen by user agents which do not support JavaScript, like the Googlebot).

Assume I am a user of the site: I read through the reviews and see one that is particularly useful. I’d like to bookmark this page, or perhaps e-mail this page to a friend of mine who has been looking for a product like this. The URL in the browser hasn’t changed from the initial page state, so when I bookmark or share the link with a friend, the Reviews tab isn’t selected and I have to include instructions to my friend to click on the Reviews tab after opening the link I have sent. Now this may not seem like a significant burden, but now assume that the reviews are paginated within the tab in groups of 10, and the review I want my friend to read is on “page” 7 of the reviews. Or assume that the product page allows you to select different SKUs (different colors or versions for instance), and the data displayed and reviews are loaded based on the SKU that was selected. In either case, I now have to include detailed click path instructions to my friend, or remember them myself if I am bookmarking the page.

The solution is to make use of the URL’s hash or anchor feature to store page state without forcing a page reload. The hash is the part of some URLs at the end prefixed with a hash sign: #. This is commonly used for anchors when navigating a long page, which is a means of storing page state in the URL, we just want to take it to another level.

There are two parts to this. The first is that we need to store the state data in the URL’s hash when the state changes. The second is that when the page loads for the first time we have to check for state data in the URL’s hash, and if it exists setup the page state accordingly.

Let’s start with the basic example of the product page with two tabs above. When the user selects the Reviews tab, some JavaScript is being called in order to change the displayed content from the default tab to the Reviews tab. This could be as basic as a link with an onclick JavaScript attribute, or it could be driven by one of the many JavaScript frameworks that support tabs (Dojo, RichFaces, jQuery, etc…). For this post I’ll assume it’s using the onclick attribute of a standard link as it’s the most basic case. If you are using a specific framework you’ll need to read the documentation on binding a method to the tab change or reading an event, etc…

If the Reviews tab link looks like this:

<a href=”javascript:void(0)” onclick=”javascript:swapTab(‘Reviews’);”>Reviews!</a>

You can store the state data in the URL’s hash by changing the link to this:

<a href=”javascript:void(0)” onclick=”javascript:swapTab(‘Reviews’);window.location.hash=’Reviews’;”>Reviews!</a>

Now, when the link is clicked, the Reviews tab is displayed, and the URL in the browser is rewritten from:

http://www.mysite.com/product.jsp?prodId=1234

to:

http://www.mysite.com/product.jsp?prodId=1234#Reviews

Now we’ve successfully stored the page state data in the URL in a way that is bookmark-able and shareable. The next step is to check for and act on that state data when the page loads.

Assuming most of your javascript for that page is in an external file called product.js, you would add a new function call handleHashOnLoad which might look like this:

function handleHashOnLoad() {
	// This method looks for anchor tags, or hashes, in the URL and sets up the tabs appropriately
	var hash = location.hash;
	//alert("hash:"+hash);
	// if we have a hash value from the URL
	if(hash != "") {
		// We need to strip off the "#" which is included in the hash value
		var tabName = hash.substring(1);
		// Now we call the function to setup the correct tab
		swapTab(tabName);
	}
}

Now you just need to call that new function after the page loads. Since the swapTab function probably manipulates the DOM tree, it is important to not call this function until after the DOM tree is complete. You can do this using a onload call, or your 3rd party JavaScript library may have a better solution (such as Dojo’s addOnLoad feature).

Now you have a page with dynamic tabs with a URL which can be bookmarked or sent out via e-mail or IM. This was a very simple example and on many pages there may be more than one piece of data defining the state. For instance, if the Reviews are paginated you might have a pagination start point, or if the product has dynamically selectable SKUs you might need to store the selected SKU. This can be done by loading the data into the hash with prefixes and separators.

http://www.mysite.com/product.jsp?prodId=1234#sku555:tabReviews:reviewStart35

Then in your handleHashOnLoad function you parse out the various values by splitting on the seperator (in this case the colon) and handling each value separately based on the prefix.

The two places you need to be careful in this approach are:

Ensure that you’re setting up the data correctly and are not overwriting the location.hash with just the one state point that was changed by the click, but are overwriting the particular value you wish to change.

Ensure that your handleHashOnLoad function is calling the DOM altering JavaScript functions in the correct order. For instance if the selected SKU changes the reviews that are loaded into the Reviews tab, it would be important to handle the selected SKU before you handle the displayed tab and the pagination of the reviews within that tab.

I may write up some basic JavaScript to assist with these areas of risk, and if so I will share it here. There may be some good options out there already but, as I’m currently 37,500 feet up, I can’t Google for them.

Photo by Ei! Kumpel

Preventing Multiple Submits On An ATG Form

Often, you’ll want to prevent impatient users from clicking a submit button multiple times, as you can end up with multiple actions taking place, or object state can get in a bad way leading to errors. For this example we’ll assume you have a final Submit Order form that actually places the order, auth’s the credit card, etc…

You can’t simply disable the submit button onclick with ATG as typically the submit button is the input field that actually activates the handle method. I tried a bunch of things, before I was able to get something working, so I wanted to share that here.

First thing is to write some javascript that will handle all of the magic (this example uses jQuery):


Second, you need to move the input that calls the correct handle method out of the submit button and into a hidden form field:


Thirdly you’ll want to replace the submit input with a submit <a> which will call your javascript:

<a href="javascript:submitform()" id="commitOrderButton">
    <img src="/myapp/img/button/submitOrder.gif" alt="Submit Order" border="0" class="submitOrder" />
</a>

And that’s it.