I was recently on a project where I needed a really flexible persistent data storage mechanism. Normally, this would be fairly easily handled via server-side data persistence code but in this case, each page of the app was basically a single-page XHR app in that to render the page required a JSONP call and lots of DOM rendering. What I was trying to accomplish was back-button support for these dynamically rendered pages and as most know, hitting the back-button on dynamically rendered DOM elements won't set them back to their original state. To further complicate the problem, the pages were being built based on user input into search forms which included input fields, select boxes and even fly-out accordion style controls. Basically, I was dealing with storing a large amount of data across page views and cookies definitely were not the solution. Also considering that the browser requirements started at IE6, none of the current localstorage mechanisms were viable.

<strong>Persistent Session Variables in JavaScript</strong>

After searching quite a bit, I finally found a solution that worked like a charm. <a href="http://www.thomasfrank.se/">Thomas Frank</a> had created a library called <a href="http://www.thomasfrank.se/sessionvars.html">Sessvars.js</a> that leveraged the <em>window.name</em> attribute. The reason for using <em>window.name</em> is that values stored in it persist across page views making it ideal for my needs. The one caveat is that it can only store a string value which at first seemed to throw a wrench in my plans. I needed to store complex data, not just strings. Thankfully, Thomas had come up with a novel way of dealing with this by building a JSON stringifier which serializes and deserializes a JSON object. This allowed me to create a complex data store that could then be converted to a string on the fly. Loved it!

What I did was grab the file name and used that as a key value to identify the specific information for that page. So my JSON data would look something like this:

[javascript]
{ &quot;page1.html&quot; : { &quot;formWrapper&quot; : &quot;#search-box&quot;, &quot;formHash&quot; = &quot;field1=foo&amp;field2=21&amp;field3=Mike&quot; },
&quot;page2.html&quot; : { &quot;formWrapper&quot; : &quot;#adv-search-box&quot;, &quot;formHash&quot; = &quot;field21=red&amp;field22=blue&amp;field56=February&quot; },
...
 }
[/javascript]

The benefit of this is that it allowed me total flexibility in storing only the affected form fields for that specific page as well as allowing for unique form field names on a page by page basis.

Actually storing that data was extremely easy. I simply defined Sessvars.js in my document:
<code>
&lt;script type="text/javascript" src="sessionvars.js"&gt;&lt;/script&gt;
</code>

and then stored my data into a property that I created in the default sessvars object:

<code>
sessvars.myObj = { "page1.html" : { "formWrapper" : "#search-box", "formHash" = "field1=foo&field2=21&field3=Mike" },
"page2.html" : { "formWrapper" : "#adv-search-box", "formHash" = "field21=red&field22=blue&field56=February" },
...
 }
</code>

The library attaches a method to the window's unload event which automatically handles the stringification of the JSON data once you leave the page so basically assigning the value is all I needed to do to persist the data.

<strong>Updating Key Values</strong>

The great thing about Sessvars.js is that when a page is loaded, it immediately restores the persistent data making it immediately accessible to your scripts. We were using jQuery on this project so I created a method that would determine the current page, search through my sessvars persistent data and if it found a key, reload the form with the user's values as well as re-run any previous JSONP calls to ensure the results were populated:

<code>
myApp.state = (function(){
	return ({

		resetForm : function( key ) {
			if (sessvars.myApp.state[key]) {
			    var o = {};
			    var queryString = decodeURIComponent( sessvars.myApp.state[key].form.hash );
			    var wrapperEle = "", formEle= "", vals = "";
			    
			    queryString.replace(/([^=]+)\=([^&]+)&?/ig, function(item, a, b){
			    	a = a.replace( /\&/g, " ");
			    	b = b.replace( /\+/g, " ");
			    	if (o[a] && typeof o[a] !== "object") {
						o[a] = o[a] + "," + b;
					} else {
						o[a] = b;
					}
				});	 		    
	    
	    		wrapperEle = $( sessvars.myApp.state[key].form.wrapper );
	    
		    	for (var index in o) { 
			   		vals = o[index].split( "," );
			   		
			   		formEle = wrapperEle.find("input[name=" + index + "], select[name=" + index + "]");

			   		if (formEle.length > 0) {
			   		
				   		if (formEle[0].tagName == "INPUT" && (formEle[0].type == "checkbox" || formEle[0].type == "radio")){
				   			formEle.attr( "checked", "");
				   			for (var i=0; i < vals.length; i++) {
		   	   			
				   	   			if (index == "jurisdictionSet") {
					   	   			wrapperEle.find("input[name=" + index + "][value=" + vals[i] + "]").trigger( "click" );	
				   	   			} else {
									wrapperEle.find("input[name=" + index + "][value=" + vals[i] + "]").attr( "checked", "true" );				   	   			
				   	   			}
				   	   			
				   			}
				   		} else {
				   			formEle.val( o[index] );
						}
						
					}
				}
    		}			
		},
		resetPage : function( key ) {

			if (sessvars.myObj.state[key]) {
				this.resetForm( key );
			}
			
			.... call various other methods to reset the page ....
			
		});

}());

$(document).ready( function(){
	myApp.state.resetPage( myApp.utils.getPageName() );
});
</code>
