This post was previously on the Pathfinder Software site. Pathfinder Software changed its name to Orthogonal in 2016. Read more.

I spent last week holed up writing part 4 of Ajax Overhaul, my series of articles for IBM developerWorks. Aimed squarely at Ajax beginners, the series shows how to progressively enhance Web 1.0 sites with jQuery and Ajax. Each installment starts with the pre-Ajax version of an example e-commerce application and takes readers through the steps of retrofitting it to improve and modernize the user experience. The tagline for this installment is “Streamline multi-step processes with tabs and Ajax forms,” a topic that allowed me to employ two of my favorite plug-ins for the jQuery open-source Ajax toolkit:

I feel like I’ve run on and on about my enthusiasm for jQuery on this site, but I can’t help it. One of the cool thing about its plugin ecosystem is the ease with which you can cross-pollinate a couple of plugins to create novel effects. In this case, I wanted to take a series of web forms – the checkout process for my example shopping site – and turn them into a single-page, tabbed interface in which each tab represented one step of the process. The biggest additional requirement was progressive enhancement; with JavaScript absent or disabled, the checkout process has to work like it did before I retrofitted it. All it took to accomplish these goals was a judicious mix of my two plugins.

jQuery UI Tabs allowed me to pull several individual form pages into a single HTML shell via Ajax. It also allowed me to control which tabs were selectable at any given time; that way, users could jump back in the process to re-submit one of the earlier forms but couldn’t jump forward to fill out forms out of order. This was important because in my example application, input in the earlier forms can affect the output of subsequent forms. Jumping back allows you to change your input, but it forces you to repeat all the intermediate steps over which you skipped.

Luckily, jQuery UI Tabs allows you to enable and disable tabs with ease. You can disable them as a group in the options bundle you use to initialize the tab set. Then you can enable or disable individual tabs by calling the appropriate methods on them. In my example code, it made sense to disable all but the first tab by default. Then I provided a custom callback function that executes each time a new tab is selected, cycling through the list and disabling all but the previous tabs in the process. The resulting code, executed when the DOM is ready, looks like this:

var tabSet = $('ul.navTabs').eq(0).tabs(	{		/*apply a nice visual effect to tab activation*/		fx: { height: 'toggle', opacity: 'toggle' },		/*disable all but the first tab by default*/		disabled: [1,2,3,4]	}).bind('select.ui-tabs', function(event, ui) {		/*		ensure that each time a new tab is activated		all subsequent tabs are disabled. This will		prevent users from jumping around in the process		*/		var currentTab = parseInt(ui.tab.id.substring(3));		var tabSetLength = 5;//hack; UI tabs doesn't know its own length		for (var i = 0; i < tabSetLength; i++) {			if (i > currentTab) {				tabSet.tabs("disable", i);			}		}});

jQuery Form, meanwhile, provided the plumbing for the individual forms that I plugged into the tabs I’d just created. It allowed me to redirect the submission of each form so that it occurs via Ajax and the response gets rendered inside the next sequential tab. This all happens thanks to the power of the ajaxSubmit method that the plugin adds to the jQuery object. jQuery Form does offer a simple Ajax submittal method, ajaxForm, but the more versatile ajaxSubmit method provided the flexibility I needed. It let me pass in a target DOM node in which to render the response, override the action URL of the original form tag and define a custom callback function to enable and select the tab representing the next step in the process. The code for each individual form varies a bit, especially for steps in the process that include detours (such as logging in or applying a discount code). But here’s a typical example:

//bind the form and provide a callback function$('#sform').submit(function() {

	//submit the form via ajax	$(this).ajaxSubmit({ 		target:     '#billingDetails', 		url:        'checkout3-fragment.html', 		success:    function() { 			var tabSet = $('ul.navTabs');			tabSet.tabs("enable", 2);			tabSet.tabs("select", 2);		}	});

	//don't actually submit the form normally	return false; });

As always, it will take a few months for my draft to make it through the editing process and onto the developerWorks site. But you can take a peek at the above-quoted code in action over at Pathfinder Labs. FYI for anybody peeking under the hood: My example app is just a front-end mockup with no server-side code to support it.

If anyone’s remotely interested, you can also monitor the Ajax Overhaul page on developer works to see when the actual installments get published.