Implementing History Support using jQuery for AJAX websites built on asp.net AJAX

Posted by anil.kasalanati on ASP.net Weblogs See other posts from ASP.net Weblogs or by anil.kasalanati
Published on Wed, 29 Dec 2010 05:08:00 GMT Indexed on 2010/12/29 14:55 UTC
Read the original article Hit count: 621

Problem Statement:

Most modern day website use AJAX for page navigation and gone are the days of complete HTTP redirection so it is imperative that we support back and forward buttons on the browser so that end users navigation is not broken. In this article we discuss about solutions which are already available and problems with them.

Microsoft History Support:

Post .Net 3.5 sp1 Microsoft’s Script manager supports history for websites using Update panels. This is achieved by enabling the ENABLE HISTORY property for the script manager and then the event “Page_Browser_Navigate” needs to be handled. So whenever the browser buttons are clicked the event is fired and the application can write code to do the navigation.

The following articles provide good tutorials on how to do that

http://www.asp.net/aspnet-in-net-35-sp1/videos/introduction-to-aspnet-ajax-history

http://www.codeproject.com/KB/aspnet/ajaxhistorymanagement.aspx

And Microsoft api internally creates an IFrame and changes the bookmark of the url. Unfortunately this has a bug and it does not work in Ie6 and 7 which are the major browsers but it works in ie8 and Firefox. And Microsoft has apparently fixed this bug in .Net 4.0. Following is the blog

http://weblogs.asp.net/joshclose/archive/2008/11/11/asp-net-ajax-addhistorypoint-bug.aspx

For solutions which are still running on .net 3.5 sp1 there is no solution which Microsoft offers so there is  are two way to solve this

o   Disable the back button.

o   Develop custom solution.

 

Disable back button

Even though this might look like a very simple thing to do there are issues around doing this  because there is no event which can be manipulated from the javascript. The browser does not provide an api to do this. So most of the technical solution on internet offer work arounds like doing a history.forward(1) so that even if the user clicks a back button the destination page redirects the user to the original page. This is not a good customer experience and does not work for asp.net website where there are different views in the same page.

There are other ways around detecting the window unload events and writing code there. So there are 2 events onbeforeUnload and onUnload and we can write code to show a confirmation message to the user. If we write code in onUnLoad then we can only show a message but it is too late to stop the navigation. And if we write on onBeforeUnLoad we can stop the navigation if the user clicks cancel but this event would be triggered for all AJAX calls and hyperlinks where the href is anything other than #. We can do this but the website has to be checked properly to ensure there are no links where href is not # otherwise the user would see a popup message saying “you are leaving the website”.

Believe me after doing a lot of research on the back button disable I found it easier to support it rather than disabling the button. So I am going to discuss a solution which work  using jQuery with some tweaking.

Custom Solution

JQuery already provides an api to manage the history of a AJAX website -

http://plugins.jquery.com/project/history

We need to integrate this with Microsoft Page request manager so that both of them work in tandem.

The page state is maintained in the cookie so that it can be passed to the server and I used jQuery cookie plug in for that –

http://plugins.jquery.com/node/1386/release

Firstly when the page loads there is a need to hook up all the events on the page which needs to cause browser history and following is the code to that.

jQuery(document).ready(function() {

            // Initialize history plugin.

            // The callback is called at once by present location.hash.

            jQuery.history.init(pageload);

 

            // set onlick event for buttons

            jQuery("a[@rel='history']").click(function() {

                //

                var hash = this.page;

                hash = hash.replace(/^.*#/, '');

                isAsyncPostBack = true;

                // moves to a new page.

                // pageload is called at once.

                jQuery.history.load(hash);

                return true;

            });

        });

The above scripts basically gets all the DOM objects which have the attribute rel=”history” and add the event. In our test page we have the link button  which has the attribute rel set to history.

<asp:LinkButton ID="Previous" rel="history" runat="server" onclick="PreviousOnClick">Previous</asp:LinkButton>

<asp:LinkButton ID="AsyncPostBack" rel="history" runat="server" onclick="NextOnClick">Next</asp:LinkButton>

<asp:LinkButton ID="HistoryLinkButton" runat="server" style="display:none" onclick="HistoryOnClick"></asp:LinkButton>

 

And you can see that there is an hidden HistoryLinkButton which used to send a sever side postback in case of browser back or previous buttons. And note that we need to use display:none and not visible= false because asp.net AJAX would disallow any post backs if visible=false.

And in general the pageload event get executed on the client side when a back or forward is pressed and the function is shown below

function pageload(hash) {

                  if (hash) {

 

                      if (!isAsyncPostBack) {

                          jQuery.cookie("page", hash);

                    __doPostBack("HistoryLinkButton", "");

                }

               isAsyncPostBack = false;

               

            } else {

                // start page

            jQuery("#load").empty();

            }

        }

 

As you can see in case there is an hash in the url we are basically do an asp.net AJAX post back using the following statement

__doPostBack("HistoryLinkButton", "");

So whenever the user clicks back or forward the post back happens using the event statement we provide and Previous event code is invoked in the code behind.  We need to have the code to use the pageId present in the url to change the page content.

And there is an important thing to note – because the hash is worked out using the pageId’s there is a need to recalculate the hash after every AJAX post back so following code is plugged in

function ReWorkHash() {

            jQuery("a[@rel='history']").unbind("click");

            jQuery("a[@rel='history']").click(function() {

                //

                var hash = jQuery(this).attr("page");

                hash = hash.replace(/^.*#/, '');

                jQuery.cookie("page", hash);

                isAsyncPostBack = true;

               

 

                // moves to a new page.

                // pageload is called at once.

                jQuery.history.load(hash);

                return true;

            });

 

     }  

This code is executed from the code behind using ScriptManager RegisterClientScriptBlock as shown below –

      ScriptManager.RegisterClientScriptBlock(this, typeof(_Default), "Recalculater", "ReWorkHash();", true);

 

A sample application is available to be downloaded at the following location –

http://techconsulting.vpscustomer.com/Source/HistoryTest.zip

And a working sample is available at –

http://techconsulting.vpscustomer.com/Samples/Default.aspx

© ASP.net Weblogs or respective owner

Related posts about History support

Related posts about .NET FAQ