Published on dev2dev (http://dev2dev.bea.com/)
http://dev2dev.bea.com/pub/a/2006/01/ajax-back-button.html
See this if you're having trouble printing code examples
by Mark Schiefelbein
01/24/2006
Ajax applications are praised for their richness, interactivity, and responsiveness, which are achieved by loading data dynamically using the XMLHttpRequest object instead of loading new pages. Among the hype and excitement, a few critical voices have pointed out that Ajax applications break several important browser features, including support for the back/forward button.
This article begins with an explanation of why the back/forward button and other browser functionality will not work unless explicitly built into an Ajax application. The article will then outline how developers can address these issues. Finally, we will look in detail at how the Backbase Ajax engine provides support for the back/forward button and other standard browser functionality.
The promise of Ajax is that it will allow developers to create visually attractive and highly interactive Web applications based solely on standard Web browser technology—often referred to as DHTML.
Developers previously had to choose between rich (an attractive user interface with a high degree of interactivity) and reach (a front end that works in any Web browser without additional client-side installations). Ajax applications should result in front ends that have both "rich" and "reach."
But what exactly does it mean for an interface to be "rich" and for an application to have "reach"?
The concept of "rich" is hard to define but easily recognized intuitively: You will know a rich interface when you see one. Desktop applications like Microsoft Office are rich. Rich interfaces use advanced UI controls like tabs and context menus. They provide advanced means of interaction such as drag-and-drop and highlighting of UI elements that have focus. Traditional browser applications are not rich. They are limited to simple controls such as forms, and interaction consists mainly of clicking links to new pages. Just look at Microsoft's e-mail clients to see the difference: Outlook is rich. Hotmail is not.
Ajax applications have been praised for their richness. Google's GMail is one of the most frequently cited examples. Other Ajax applications made by Google (Google Suggests, Google Maps), Microsoft's upcoming Web mail client, code-named "Kahuna," or the Backbase RSS Reader also contain advanced controls and interaction models. Look at Dan Grossman's list of Top 10 Ajax Applications for an impressive list of rich interfaces.
I would therefore argue that Ajax applications clearly satisfy the "rich" criteria. But what about "reach"?
In its most basic form, an application has "reach" if its interface runs inside a Web browser. Ajax applications are based on browser standards and therefore can be accessed from a Web browser.
However, being accessible from a Web browser is not enough. In his 2000 article Flash: 99% Bad, Jakob Nielson pointed out that Flash "breaks with the Web's fundamental interaction style." End users expect a certain interaction style when using Web applications. Applications need to comply with the traditional interaction style of the Web and provide the following usability features:
A look at the list of Top 10 Ajax Applications shows that most of the currently discussed Ajax applications do break the Web's fundamental interaction style. In the next section, I'll take a look at why many Ajax applications do.
The Web as we know it today is firmly rooted on the following three principles:
Ajax programming makes interfaces richer by pushing the limits of what can be achieved within the boundaries determined by the above principles. As explained in my previous article, A Backbase Ajax Front-end for J2EE Applications, Ajax introduces extensive use of JavaScript (the "J") for the creation of rich UI controls and interaction. Ajax also introduces asynchronous XML communication (the "A" and "X") by means of the XMLHttpRequest object to load new data and presentation logic without a page refresh. The current Ajax model, however, does not address how to handle URIs.
As a direct consequence of the changes in how to use (D)HTML and HTTP, Ajax applications break the back button and other elements of the Web's fundamental interaction style. In the remainder of this section, I explain how to fix this by handling URIs in an Ajax way. I'll first look at how URIs are related to user interaction in traditional Web applications.
User interaction in technical terms means a state change of the user interface. The end user initiates the state change. The browser client handles the state change by means of a page request to the server (REST principle). The server will generate the new interface state by sending a new page and a new URI to the client.
In summary, every user interaction is handled by means of a server roundtrip that results in:
The Web usability features are enabled because the browser records successive URIs in its history stack and shows the current URI to the end user in the address bar, where the user can copy it and send it to a friend. When the user clicks the back button or pastes a URI from an e-mail into the browser's address bar, a roundtrip to the server is triggered. As the server is responsible for state management, the server can generate the corresponding page.
The major difference between Ajax applications and traditional Web applications is that Ajax applications can handle user interaction without page reloads. Loading data from the server through the XMLHttpRequest object is one example. Using JavaScript to handle drag-and-drop client side is another.
In both cases, the state changes without generation of a new URI. Therefore, clicking the back or refresh button will have unexpected effects, and there is no deep-link URI in the address bar.
To provide the traditional Web usability features, the Ajax application therefore needs to handle URIs client side in much the same way as the server does in traditional Web applications. The Ajax application needs to:
With the above, the browser history will work, and the browser's address bar will show a URI that can be sent to a friend.
An additional difficulty is being able to determine when the Ajax engine should do the above (for example, which state changes should result in the creation of a new URI). In the traditional model every page refresh resulted in a URI update. In the Ajax model, every client-side event is a candidate for pushing a new URI onto the browser stack. Interaction designers and developers will have to make decisions as to which state changes are meaningful. URIs need to be generated for meaningful state changes only.
I'll summarize the client-side requirements for an Ajax application that provides Web usability features:
|
In this section, I discuss the basic steps you need to take to support the back button in Ajax applications. I include a simple code sample that illustrates the necessary steps. More complete discussions of how to implement back-button support in a cross-browser-compatible way are already available. Two articles I particularly like were written by Mike Stenhouse (Content with Style) and more recently Brad Neuberg (OnJava).
My simple sample application, shown in Figure 1, will be a select box with two values: "Year 1" and "Year 2." For the purposes of this article, I will track history when the value of the select box is changed. This means that a user may first select "Year 2" and then click the back button to be taken back to the previous selection.

Figure 1. A simple sample application with the select box
The starting point for the sample application is a simple HTML form with a JavaScript getter and setter for the select box values:
<html>
<head>
<script language="JavaScript" type="text/JavaScript">
function reportOptionValue()
{
var myForm = document.make_history;
var mySelect = myForm.change_year;
return mySelect.options[mySelect.selectedIndex].value;
}
function setOptionValue(value)
{
var myForm = document.make_history;
var mySelect = myForm.change_year;
mySelect.options[value-1].selected = true;
}
</script>
</head>
<body>
<form name=make_history>
<select name=change_year>
<option value="year_1">Year 1</option>
<option value="year_2">Year 2</option>
</select>
</form>
</body>
</html>
I'll first tackle requirement 1: Create a history of states. As mentioned above, the requirement consists of the following three steps:
The state I want to save is every change to the select box. Therefore I'll create a new URI that includes the select box state information.
To remain compliant with Internet standards, I'll use the fragment identifier part of the URI. As stated in the IETF RFC 3986, "...Fragment identifiers have a special role in information retrieval systems as the primary form of client-side indirect referencing, <...> the fragment identifier is separated from the rest of the URI prior to a dereference, and thus the identifying information within the fragment itself is dereferenced solely by the user agent, regardless of the URI scheme...."
Using the fragment identifier, I can create an "Ajax-URI," composed of a client-side and a server-side part, separated by the hash ("#") sign.
JavaScript provides the window.location() function to update the browser's history and address with a URI. In addition, you can directly access the fragment identifier with window.location.hash().
In the following snippet, you can see how I have extended the code with an onchange event handler on the select box that updates the browser history and address bar with an "Ajax-URI":
<html>
<head>
<script language="JavaScript" type="text/JavaScript">
function makeHistory(newHash)
{
window.location.hash = newHash;
}
function reportOptionValue()
{
var myForm = document.make_history;
var mySelect = myForm.change_year;
return mySelect.options[mySelect.selectedIndex].value;
}
function setOptionValue(value)
{
var myForm = document.make_history;
var mySelect = myForm.change_year;
mySelect.options[value-1].selected = true;
}
</script>
</head>
<body>
<form name=make_history>
<select name=change_year
onchange=
"return makeHistory(reportOptionValue())">
<option value="year_1">Year 1</option>
<option value="year_2">Year 2</option>
</select>
</form>
</body>
</html>
As Figure 2 shows, the browser address is now updated with every change to the select box. Please note that there are some issues with Internet Explorer (IE) that require using a hidden frame to get the correct behavior. You are again referred to Mike Stenhouse's or Brad Neuberg's articles for complete details.

Figure 2. The history stack is updated on state change
You now have an event handler that creates a new URI when the value of the select box is changed. The new URI uses the fragment identifier to store the information to recreate the previous state. With this you can move on to the next requirement:
In Step 1, I updated the URI client side through the window.location.hash() function. The call does not result in a server roundtrip or a page refresh. Instead, I need to handle the URI change the Ajax way (client side).
I'll first add a polling function that will regularly check the URI in the browser history. I'll register pollHash() in the onload event of the page and then re-execute it every 1,000 milliseconds.
The polling function will call the function handleHistory(), which checks whether the URI has changed since the previous check. I do this using the global variable expectedHash.
The final piece is to determine whether the URI has changed because of the event handler on the select box or because the end user clicked the back button. I do this by setting expectedHash accordingly in the event handler of the select box.
<html>
<head>
<script language="JavaScript" type="text/JavaScript">
var expectedHash = "";
function makeHistory(newHash)
{
window.location.hash = newHash;
expectedHash = window.location.hash;
return true;
}
function reportOptionValue()
{
var myForm = document.make_history;
var mySelect = myForm.change_year;
return mySelect.options[mySelect.selectedIndex].value;
}
function setOptionValue(value)
{
var myForm = document.make_history;
var mySelect = myForm.change_year;
mySelect.options[value-1].selected = true;
return true;
}
function handleHistory()
{
if ( window.location.hash != expectedHash )
{
expectedHash = window.location.hash;
var newoption = expectedHash.substring(6);
setOptionValue( newoption );
}
return true;
}
function pollHash() {
handleHistory();
window.setInterval("handleHistory()", 1000);
return true;
}
</script>
</head>
<body language="JavaScript"
onload="return pollHash()">
<form name=make_history>
<select name=change_year
onchange="return makeHistory(reportOptionValue())">
<option value="year_1">Year 1</option>
<option value="year_2">Year 2</option>
</select>
</form>
</body>
</html>
This concludes my simple example that shows how to record state in a URI, push that URI to the browser history track, detect an address change from the back button, and finally recreate the required state.
The example is still lacking several features such as:
Complete handling of all traditional Web usability features in a robust way that works across all browsers is not easy to implement. An alternative approach is to use an Ajax toolkit with built-in support for these features.
In the following section, I describe how the Backbase Ajax engine provides these features. I do so by looking at the implementation of the Ajax forum on the Backbase DevNet.
|
The Backbase Ajax engine is a mature and feature-rich Ajax software package. Solid support for all traditional Web usability features is one of the strengths of Backbase. The Backbase DevNet contains information about Backbase and Ajax for developers. A developer forum is part of the DevNet.
The Backbase Web presence including the DevNet and its discussion forum has been built using Backbase. To demonstrate the rich and reach features of this forum, I will take you step by step through a typical use case of the forum:
I'm going to take a look at the state of the forum interface as well as the corresponding URI in the address bar after the developer has navigated to the "BXML" forum and then selected the post entitled "Issue with vertical and horizontal menus."
The forum and post are selected and highlighted visually. The discussion thread is displayed for reading. The URI contains all corresponding information in the fragment identifier. After the hash, we see the complete state recorded for bookmarking and deep linking: "forum" indicates that the developer is browsing the forum section of the Web site; "forum=2" indicates that the BXML forum is selected, and "thread=211" records the currently selected thread. Finally, information in brackets, "[5]", indicates the handling of multiple back and forward steps in combination with bookmarks.
|
|
If you go to the Backbase forum, you can see how the URI is updated with every state change, even the ones that are handled client side or involve a partial page update through the XMLHttpRequest object.
Let's now look at what happens when the developer sends the current URI to a friend. The friend opens the URI in a browser window and expects to see the same interface state. The state needs to be recreated in a new browser from scratch. For my case study, I simulate this scenario by copying the URI from a Firefox window into a newly opened IE window.
Entering the URI in the address bar of the browser will initially result in a server-side request. Using the part before the hash, Backbase.com is loaded, and during that process, the Backbase Ajax engine is initialized. The activated Backbase engine will then read the part of the URI behind the hash. From that information, the Backbase engine creates the corresponding state by going to the section "forum" and selecting forum BXML (id=2) and thread 211. This is done without a page refresh by loading additional content from the server and updating the interface partially client-side.
For continued handling of browser functionality, a new URI has been pushed onto the browser history. The new URI is visible in the address bar and can be used for deep linking. The "[0]" indicates that there is no previous state to go back to using the back button.
|
|
The first step of the case study showed how the URI was updated in line with state changes of the interface triggered by user interaction. You'll now see the opposite: A new URI is requested and the corresponding state is recreated.
By clicking the back button, users navigate to the post they were reading previously. The browser handles the back button by retrieving the previous URI from its history stack. The Backbase Ajax engine detects the change, reads the new URI from the history and recreates the corresponding state by going to section "forum" and selecting forum "BXML" (id=2) and thread 192. The new URI according to the same syntax as above is visible in the address bar.
This concludes the case study.
|
|
For the past several years, Web developers have opted to build Web interfaces because the market demanded "reach" and was willing to accept less "rich." However, the current interest in Ajax clearly shows that this willingness was only temporary in nature. The market now emphatically demands Web interfaces with the richness, interactivity, and responsiveness of desktop applications.
At the same time, end users have become accustomed to the interaction style of the Web. It enhances productivity with its common pattern for how to interact with any Web interface. End users expect working back/forward buttons, the ability to create bookmarks, deep linking, a working refresh button, the possibility of viewing the source, searching the page using "find," and search engines that can index Ajax applications.
The Ajax community today must accept the following: The technology is available for providing support for the back/forward button and other traditional browser features in Ajax applications, as this article shows. Although they are not easy to implement and come at an additional cost, success within the community will require traditional browser features built into Ajax applications. I therefore urge Ajax developers to build Ajax applications that support these features!
In this article, I highlighted why Ajax applications need to comply with the traditional interaction style of the Web and provide the traditional Web usability features. I established that these features can be programmed into Ajax applications by creating "Ajax URIs" that contain client-side state information in the fragment identifier.
Looking at the code involved, you saw that a complete and generic solution is quite difficult to achieve, given state handling code in general is not trivial and because of the usual cross-browser inconsistencies. The Backbase Ajax engine provides the solution by providing the required functionality out of the box.
Mark Schiefelbein has been the director of product management at Backbase since January 2005. Mark drives the global product rollout of the Backbase Rich Internet Application technology.
Return to dev2dev.