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