Now that we’ve gone through the basics of the ASP.NET AJAX History feature and seen how it works server-side, we’re better equipped to see how to leverage this functionality from a client-side perspective. The last article highlighted the easability of adding AJAX History in circumstances where using the UpdatePanel makes sense, but in pure AJAX applications, that kind of logic needs to be achieved via JavaScript, which the previous article didn’t satisfy. Luckily, all the behavior that you saw in the last post can be fully replicated using the ASP.NET AJAX libraries. The second part of this series will show how to develop the exact same application as we did in part 1, but doing so completely on the client-side. We’re going to be adding the new functionality to the same page as the last article, and as such, reusing the existing code (ScriptManager, CSS, etc.)
We begin by creating the same list of quarterbacks that we saw in part 1, but in this case we’re not going to use a server control, but rather straight HTML:

If I ran this right now I’d see the following:

Notice that we don’t have to deal with LinkButtons as we did in our last article, but rather we just have a nice and simple unordered list. Now we need to enable the ability for users to select their favorite quarterback. Since we can’t lean on the BulletedList control’s Click event, we’ll have to write some JavaScript code. We’ll add a new JavaScript file to our project, and reference it using our ScriptManager:

Keep in mind that the EnableHistory property also applies to client-side history, so make sure you have that set to true regardless which approach you take (client/server). The EnableSecureHistoryState properperty however does not apply to client-side history, so make sure you only store trivial data in history when using the client-side approach. Let’s add some initialization code to make the list selection work. We’ll add the ubiquitous pageLoad method to our new script file:
var quarterbackItems; function pageLoad() { quarterbackItems = $get("quarterbacks-list").getElementsByTagName("li"); for (var i = 0; i < quarterbackItems.length; i++) { $addHandler(quarterbackItems[i], "click", quarterbackClicked); } }
All we’re doing here is grabbing the list items from our quarterbacks list and wiring up their individual click events. The logic for the quarterbackClick method looks like so:
function quarterbackClicked(e) { var name = e.target.innerHTML; selectQuarterback(name); }
We’re just grabbing the inner content (the quarterback’s name) of the item that fired this event, which is the list item, and then calling a method called selectQuarterback, passing it the selected quarterback’s name. The code for the selectQuarterback method looks like so:
function selectQuarterback(name) { for (var i = 0; i < quarterbackItems.length; i++) { var element = quarterbackItems[i]; if (element.innerHTML == name) { Sys.UI.DomElement.addCssClass(element, "selected"); } else { Sys.UI.DomElement.removeCssClass(element, "selected"); } } }
Here we’re simply looping through every list item in the quarterbacks list and marking the proper item as being selected and ensuring that any previously selected items are unselected. You should notice that the logic for this exercise is practically 1:1 from the previous article, we’ve just ported it to use JavaScript and the ASP.NET AJAX libraries. We’ll reuse the same stylesheet from the last post since we’re leveraging the same HTML output and class names. If I were to run this right now and click on Tom Brady, I’d see the following:

Since we’re reusing the same stylesheet from the last article, this looks exactly like before, only without the LinkButtons. If I select Carson Palmer I’d see the following:

So it’s clear that our app functions perfectly now. Unfortunately, regardless how many times I select a different quarterback I still don’t get any love from my back and forward buttons:

Up to this point we’ve just created a neat little JavaScript app. The browser has no clue what we consider state in this scenario, and we certainly haven’t made any effort to tell it. It would make the most sense to add our custom history point logic in the quarterbackClicked event handler. The way we did this in our last article was by calling the AddHistoryPoint method on the ScriptManager control. Well instead of using the ScriptManager, we’re going to use the Sys.Application class in the ASP.NET AJAX libraries. It also has a method called addHistoryPoint that serves the exact same purpose as it’s server-side counterpart. Let’s modify the quarterbaclkClicked event handler to include the call to addHistoryPoint:
function quarterbackClicked(e) { var name = e.target.innerHTML; Sys.Application.addHistoryPoint({FavoriteQuarterback: name}, name); selectQuarterback(name); }
Notice that instead of passing it two parameters for the key/value pair that represents our state, we’re passing it a JSON object. “FavoriteQuarterback” is our history key, and the value is the name of the selected quarterback. The second parameter represents the browser title associated to this history point.
If I run the app, and then select a list item, I see the following:

Great, so we’ve got our list selection and history points working, but now we need to tell the app how to recreate the user’s state upon request. In the last article we used the ScriptManager’s Navigate event, and as you might have already guessed, in this article we’re going to use the Sys.Application class’s navigate event. Let’s modify our pageLoad event to include a handler for the navigate event:
function pageLoad() { quarterbackItems = $get("client-side").getElementsByTagName("li"); for (var i = 0; i < quarterbackItems.length; i++) { $addHandler(quarterbackItems[i], "click", quarterbackClicked); } Sys.Application.add_navigate(function(sender, e) { selectQuarterback(e.get_state()["FavoriteQuarterback"]); }); }
The event arguments that is passed into the event handler has a property called “state” that returns back the key/value pair of history points. In this case we’re simply retrieving the FavoriteQuarterback value and sending it to the selectQuarterback method.
So what does our URL look like when using AJAX History from the client-side? If I run the app and select Donnovan McNabb, the URL looks like the following:
http://localhost:49652/Default.aspx#FavoriteQuarterback=Donnovan%20McNabb
Notice that the structure of the URL is identical to what we saw in the last article, with a few exceptions. You may notice that the double ampersand is gone. Like was mentioned previously, the double ampersand is used only to prefix server-side history points, which in this case we don’t have any of.
There is still one minor change that we need to make to our JavaScript code before moving on, and it is related to the main difference between the client and server approaches. When you call the ScriptManager’s AddHistoryPoint method, all it does it modify the current URL, which effectively registers the history point with the browser. The Navigate event doesn’t fire until you use the back or forward buttons, or return from a bookmarked history point. The Sys.Application class’s addHistoryPoint method on the other hand does trigger the navigate event after being called. So what does that mean for our appication? Well, when I add the history point in the quarterbackClicked event handler, we are also then calling the selectQuarterback method. The “problem” is that the call to addHistoryPoint will trigger the navigate event, which in turns calls selectQuarterback. When the user manually selects a list item, selectQuarterback is being called twice, whereas when they navigate between history points (via back/forward buttons or bookmarks), it would only be called once. To fix this, we can just modify the quarterbackClicked method by removing the call to selectQuarterback, since we know our navigate event handler will take care of that for us:
function quarterbackClicked(e) { var name = e.target.innerHTML; Sys.Application.addHistoryPoint({FavoriteQuarterback: name}, name); }
Perfect! Now I’ll run the app and select Carson Palmer:

I then select Drew Brees:

I then select Peyton Manning:

I hit the back button twice, and then hit the forward button once:

Beautiful. Our app works perfectly, and it was written completely using JavaScript and HTML. Hopefully this exercise highlights how easily you could developer very rich and compelling web applications using the ASP.NET AJAX libraries in conjunction with the new AJAX History feature. It is so rediculously easy to add history to an AJAX application that it really opens up the realistic oppurtunities developers have to enhance web applications, whether they’re existing or brand new.
The final scenario of interest is intermingling server-side and client-side history together. Can it be done gracefully? How exactly does it work? Why would I ever use it? In the final part of this series, we’ll examine the migration points between each approach.
Hey Jonathan –
Keep up the great posts. It’s good to see you’re settling in to the new job.
– Galen