UPDATE (Mar 2010): I've been looking for more easy to implement browser history management solutions and I think that
YUI is a better candidate than RSH in terms of the ease of implementation. Here is my new post on overview of history management solutions and a sample YUI implementation -
Implementing YUI Browser History ManagerHow to use RSH Really Simple History - perhaps one of the best solutions to the Ajax history back button problem. For those who don't know about the ajax browser history and back button problem - browser's back button doesn't work for Ajax asynchronous calls because Ajax results in partial rendering of pages and therefore browser history is not recorded/saved for all Ajax asynchronous calls. Why - again because all Ajax calls result in partial web-page rendering and for browser history to work (for browser to know when to save the current state in history), a full page refresh has to happen. Browser's are just built that way and we can't really control the history management right out of the box. Another problem is that
browser's don't have any events for back and forward buttons. So even if we use something like URL Fragments to get the browser's back button to work for Ajax applications, still we wouldn't know if the user is going back or forward without any system that knows how to store the browser history in a stack and pop it when user hits browser's back button (specifically for ajax ofcourse).
After looking at Gmail, Facebook and Orkut, I figured that a good Ajax browser history back button solution will use iframes and URL fragments in some way. Yeah, Gmail, Facebook and Orkut have flawless ajax history (back button) management. Yeah,
gmail has the most awesome ajax back button solution in place. Kudos google coders and designers! Facebook is also not lagging behind, you can see the URL fragments in Facebook's URL but it doesn't always seem to work properly (some times the page redirects!). Orkut too, is using some solution that relies on URL fragments and possibly iFrames. So looking at the ajax browser history (back button) managements in these social networking sites, I got some idea about what I'm looking for and found this thing called RSH (Really Simple History) that seems like the perfect solution (so far).
RSH (Really Simple History)
uses iFrames (and more for that matter!) to maintain the stack of browser's history. RSH (Really Simple History) also
uses URL fragments to store the browser history. Why - because remember that for an ajax asynchronous call browser doesn't even "activate" the back button. This URL fragments technique used by RSH (Really Simple History) is a hack to "activate" the back button because remember that the
browser's back button works for all URL fragment clicks. So, by updating the URL fragments Really Simple History (or RSH) create a "state" in browser's history. Yeah, Really Simple History is using iFrames to actually store the browser history stack for ajax calls and it's using URL fragments to make the browser's back button work for each of those ajax calls (tricking the browser into thinking that user navigated to some link! Neat RSH trick!).
Ok, enough with the talk,
lets get back to how to implement RSH or Really Simple History. (Yes, that's kinda the main point of the blog because very few sites explain how to use or how to implement Really Simple History (RSH). Please remember that the code samples I provide below come from ASP.NET development environment but being Javascript/JQuery (clientside code basically) there's not much difference.
1: Download
JQuery and include in your web page (skip if already using them). JQuery can be downloaded from
JQuery Download for RSH (Really Simple History) and JQuery version (1.2.6 in the code below) will change as per the latest JQuery version you download.
<script type="text/javascript" src="<%= ResolveUrl("~/js/jquery-1.2.6.min.js") %>"></script>2: Download
RSH (Really Simple History) library from
http://code.google.com/p/reallysimplehistory/downloads/list and include the the minified JQuery and
JSON files (again the version numbers will change as per your latest RSH and JSON versions download).
<script type="text/javascript" src="<%= ResolveUrl("~/js/json2007.js") %>"></script>
<script type="text/javascript" src="<%= ResolveUrl("~/js/rsh.compressed.js") %>"></script>
(JSON2007.js and JSON2005.js were included in the Really Simple History package so I didn't have to download JSON separately).
3: FTP or Upload blank.html (that is there in RSH package) to your website where you want to use Really Simple History. This is important because Really Simple History uses the blank.html file to store the browser history stack (as an iframe in your main web page).
4: Create a method that handles any changes in history events. Please remember from earlier in this article that browsers don't have any events for back and forward buttons so we can't handle them (the back button navigation specifically). Now, Really Simple History can handle back (and forward) button on your behalf and all you need to do is to provide a function that takes a stored history state (that you store a bit later in this article) and decides what to do. If this doesn't make a lot of sense, hang on and just look at the sample code for that method below. I'll explain it in more detail in a little bit.
function historyChange(newLocation, historyData)
{
ShowOldContent(newLocation, historyData);
// OR to simplify
alert('hey I'm going back to ' + newLocation);
}
5: Execute the dhtmlHistory.initialize and dhtmlHistory.addListener methods to your onload function to initialize Really Simple History and create the appropriate listeners respectively. Sample code follows and please take a look at the comments for more clarification -
window.onload = function() {
//Simply initializes the dhtmlHistory object. No change needed.
dhtmlHistory.initialize();
//Adds a listener for browser's back/forward navigation. Just change "historyChange" to
// your method of choice. Note that "historyChange" comes from step 4 above.
dhtmlHistory.addListener(historyChange);
//This adds a starting point to the whole browser history thingy. Not really optional though.
dhtmlHistory.add('default.aspx','link');
};
Please note that whatever you put in dhtmlHistory.add() function, as the first argument, will show up as a URL fragment in your webpage. So if your webpage is http://www.mywebpage.com/ then it will become http://www.mywebpage.com/#default.aspx if default.aspx was your first argument to dhtmlHistory.add() function.
6: Call dhtmlHistory.add() function wherever you want to store the browser history for ajax calls! For example, in your buttons that do some ajax calls, you can call it in the onclick() method. What the dhtmlHistory.add() function will do is to call your historyChange() method in turn and execute your history management code.
So, the final sample code looks like -
<script type="text/javascript" src="<%= ResolveUrl("~/js/json2007.js") %>"></script>
<script type="text/javascript" src="<%= ResolveUrl("~/js/jquery-1.2.6.min.js") %>"></script>
<script type="text/javascript" src="<%= ResolveUrl("~/js/rsh.compressed.js") %>"></script>
<script type="text/javascript" language="javascript">
window.onload = function() {
dhtmlHistory.initialize();
dhtmlHistory.addListener(historyChange);
dhtmlHistory.add('default.aspx','link');
};
function historyChange(newLocation, historyData)
{
ShowOldContent(newLocation, historyData);
// OR to simplify
alert('hey I'm going back to ' + newLocation);
}
</script>
Yes! You are done! It's as simple as that!
Sample Implementation: Now let's go over a sample implementation because so far it was all pretty much up in the air and you might not be sure about what to put in historyChange() method and what to pass as arguments in dhtmlHistory.add() method and so on. So, I'll present two sample implementations of RSH (Really Simple History) . One is a a wizard like problem and the other is navigating using links in a fancy ajax way. This is what I had to do myself in various different projects.
How to use RSH (Really Simple History) to implement (anchor tags) navigation - This is a common problem in which the user clicks a link (anchor tag) and the whole page goes back for a second as new page is loaded. This interferes with the user's overall experience and might even drive him away the more impatient ones. No need to say that a seamless link (anchors) navigation can help most e-commerce websites considerably. This was always considered a necessary evil though but with RSH (Really Simple History) has come to our rescue and here I present one easy solution to integrate Ajax with page navigation -
1: Attach dhtmlHistory.Add() to all anchor tags's onclick() event handlers
2: Call $('#
').load() in your historyChange() function
And you are done! Yeah, that was it! I'll be writing in more details about it though as I think it's a rather big topic in itself if presented with all the required code etc.
How to use RSH (Really Simple History) to implement Ajax driven wizards - Let's say we have a drop down (select) as step 1 and 2 radio buttons in our body tag -
<h1>Ajax driven Wizard</h1>
<div id="divStep1">
<asp:DropDownList ID="ddlStep1" runat="server">
<asp:ListItem Value="">Please Select</asp:ListItem>
<asp:ListItem Value="itep1">item1</asp:ListItem>
<asp:ListItem Value="itep2">item2</asp:ListItem>
</asp:DropDownList>
</div>
<div id="divStep1">
<asp:RadioButton runat="server" groupName="rbStep2a" ID="rbStep2" />
<asp:RadioButton runat="server" groupName="rbStep2b" ID="rbStep2" />
</div>
We attach the javascript event handlers to above as follows (one of the many ways of attaching event handlers) -
protected void Page_Load(object sender, EventArgs e)
{
ddlStep1.Attributes["onchange"] = "moveNext('step2')";
rbStep2a.Attributes["onclick"] = moveNext('step1')";
}
"moveNext" is just some method here that shows the "next" step of the wizard and and saves the data to database so that you can keep track of the intermediate steps of a wizard in case user leaves in the middle
function moveNext(nextStep)
{
if(nextStep == 'step2')
{
$("#divStep2").show();
$("#divStep1").hide()
}
else if(nextStep == 'step1')
{
$("#divStep1").show();
$("#divStep2").hide()
}
dhtmlHistory.add(nextStep,'WizardStep');
saveToDatabase(nextStep);
}
Yes, the above code will create a cyclic not-so-much-of-a-wizard! Please give special attention to the bolded line. This line actually adds the "next step" to browser history and as soon as it's executed you will see # as a URL Fragment. Yeah, that's RSH at work right there.
Using above as a reference, we can write historyChange method as below. This implementation too just shows the "next" div and looks a lot like moveNext() method above but it can change to do anything as per your requirements.
function historyChange(newLocation, historyData)
{
if(newLocation == 'step2')
{
$("#divStep2").show();
$("#divStep1").hide()
}
else if(newLocation == 'step1')
{
$("#divStep1").show();
$("#divStep2").hide()
}
}
Please note that I haven't use the historyData object at all in the above example because I simply didn't need to store anything else. But if I need to store something then yes, we can put anything we want in the historyData object. So, thats how we can use RSH (Really Simple History) for an Ajax driven wizard that allows the user to use back button to go back to the last question (and saves the data to the database as a bonus) with refreshing the page!