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

Developing Ajax Applications That Preserve Standard Browser Functionality

by Mark Schiefelbein
01/24/2006

Abstract

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.

Do Ajax Applications Require a Back Button?

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.

Why Do Ajax Applications Frequently Break the Back Button?

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:

  1. Create history
    1. Save meaningful state
    2. Generate a corresponding URI
    3. Push the URI onto the browser stack
  2. Restore history
    1. Detect URI change
    2. Recreate the state from a URI

The Basic Design of Supporting the Back Button in Ajax

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
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:

  1. Create history
    1. Save meaningful state
    2. Generate a corresponding URI
    3. Push the URI onto the browser stack

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
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:

  1. Restore history
    1. Detect URI change
    2. Recreate the state from a URI

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.

Case Study: Ajax Forum with Back Button and Deep Linking

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:

  1. A developer browses the forum reading threads from different topics.
  2. The developer copies the URI of the thread, pastes it into an e-mail, and sends it to a friend. The friend copies the URI from the e-mail into a browser and opens the same forum thread.
  3. The developer clicks the back button to read a previous thread.

The state of the forum interface after several user interactions

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.

Figure 3
Figure 3. Initial state of the forum with Ajax URI (click the image for a full-size screen shot)

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.

The state of the forum interface recreated in another browser window

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.

Figure 4
Figure 4. Forum state recreated in a new browser window (click the image for a full-size screen shot)

The state of the forum interface after the user has clicked 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.

Figure 4
Figure 5. Forum state after clicking the back button (click the image for a full-size screen shot)

Ajax Applications Do Require a Back Button!

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!

Conclusion

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.

References

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.