I am building a back-office administration tool for a New York-based client. The application manages their customer data, tracks invoices and payments, application forms, travel information, manages their corporate Web site’s content and media, and a whole lot more. It’s pretty damned slick.
Despite it’s numerous (and not-yet-fully-tested!) successes, there’s one fatal flaw with this new application I’ve built: the primary users are coming directly from a Microsoft Office Access ’97 form-driven database. (I know what you might be thinking. This gets good. Read on.) Their current Office system works well, but it’s been patched, band-aided, and added onto countless times since the early part of this decade. In fact, when it loads, the Access ’97 “dashboard” has 20 or 30 differently-colored bricks which when clicked take users on a magical ride through literally hundreds of custom queries, and thousands of records in one single gigantic table. Each of the company’s employees is fluent in the myriad codes and mystical shorthand that make up each row and column of data. It’s something to behold, let me tell you.
Over the past months, through endless revisions of specification documents, writing line after line of semantic code, and creating ingenious ways to handle complex scenarios, one of the simplest issues arose. It is a direct result of my client’s full-time use of and dependence on Access ’97, and it’s fun save-as-you-move-from-field-to-field feature. Users never have to click a “save” button, and can fly around updating customer data in a dash. Not so with the new web application. Here’s how we fix the problem.
Preventing Users from Leaving Without Saving Their Data
There are two views in the new software where numerous text fields, checkboxes and select boxes grace the page. During some initial testing, we found that users were changing data and immediately bouncing along to another view without saving their changes – an action they were quite comfortable performing in the past. It became clear I had to write some code that stopped users from leaving the page unless they saved their changes.
I immediately turned to the DOM’s onbeforeunload event. We’re already using a short and sweet function on the multi-step customer application form which is somewhat obtrusive; a user cannot go anywhere, close the browser, or even refresh the page without being hit with a JavaScript confirmation box asking them if they’re sure they want to go. The code looks like this:
var needToConfirm = true;
window.onbeforeunload = confirmExit;
function confirmExit() {
if (needToConfirm)
return "Are you sure you want to leave?";
}
This wonderfully-crafted little snippet comes from 4GuysFromRolla. Although nice, I needed something “smarter” that only prompted a user if and only if they made a change on the page. Scott Mitchell, the 4Guys author, continued in his article with an example of how to achieve this by creating two JavaScript arrays and then looping through them comparing the values. Great inspiration, Scott. I couldn’t use this script because I have two dozen fields on one view (it actually looks pretty good) instead of his six and typing out each field ID would take too much time. So I used his example, and wrote this relatively short set of functions:
//markhealey.org
//Inspiration from Scott Mitchell: http://www.4guysfromrolla.com/webtech/100604-1.shtml
var f = $$('div#myform input');
var needToConfirm = true;
//Get all form field values
var formDBVals = '';
for(var i=0; i
formDBVals += "'" + escape(f[i].value) + "', ";
}
formDBVals += "''";
//New arrary to hold DB vals, before changes
var the_values = new Array(formDBVals);
//http://www.breakingpar.com/bkp/home.nsf/Doc%21OpenNavigator&87256B280015193F87256BFB0077DFFD
function areArraysEqual(array1, array2) {
var temp = new Array();
if ( (!array1[0]) || (!array2[0]) ) { // If either is not an array
return false;
}
if (array1.length != array2.length) {
return false;
}
// Put all the elements from array1 into a "tagged" array
for (var i=0; i
key = (typeof array1[i]) + "~" + array1[i];
// Use "typeof" so a number 1 isn't equal to a string "1".
if (temp[key]) { temp[key]++; } else { temp[key] = 1; }
// temp[key] = # of occurrences of the value (so an element could appear multiple times)
}
// Go through array2 - if same tag missing in "tagged" array, not equal
for (var i=0; i
key = (typeof array2[i]) + "~" + array2[i];
if (temp[key]) {
if (temp[key] == 0) { return false; } else { temp[key]--; }
// Subtract to keep track of # of appearances in array2
} else { // Key didn't appear in array1, arrays are not equal.
return false;
}
}
// If we get to this point, then every generated key in array1 showed up the exact same
// number of times in array2, so the arrays are equal.
return true;
}
function confirmExitAccountInfo() {
var formChangedVals = '';
for(var i=0; i
formChangedVals += "'" + escape(f[i].value) + "', ";
}
formChangedVals += "''";
var the_changed_values = new Array(formChangedVals);
if(needToConfirm) {
if(!areArraysEqual(the_values,the_changed_values) ) {
return "It appears you changed some info. Did you mean to leave without saving it?";
}
}
}
window.onbeforeunload = confirmExitAccountInfo;
Let’s examine this script. We first need to set the variable “needToConfirm” to true, so as soon as the page loads, we assume the user is going to get the save-before-you-leave prompt (unless they click “save”). Then using the Prototype function $$(), we load the values of every field in my form into an array called formDBVals by using a simple for loop. Since most of the fields on the view are pre-loaded with information from our database, we easily have access to this data. But you could fill your array with null values. We ultimately want to capture any changes the user makes, and prompt them to save them before leaving the page.
To track changes the user makes, we’re not actually doing anything fancy… just re-looping through the form fields, and comparing the original array (formDBVals) to the values in a second array (formChangedVals) when the form is saved. Using a snazzy script I found at BreakingPar, we quickly compare the two arrays. If they’re different, that is, if the user changed anything, we fire the save-before-you-leave prompt. If the two arrays are the same, then the user hasn’t changed any values… and of course, they’re allowed to leave. We also have to add “needToConfirm=false;” to the submit button’s onclick.
What started with obtrusive prompts which get really annoying – especially if nothing’s been changed – has now evolved into a smart script that’s completely reusable in any form. Remember, you need to include the Prototype.js for this to work.
Use it in your app, just please give 4Guys and me some credit. Download the JavaScript. And check out the live demo at 4GuysFromRolla.
Update: Sorry friends, I forgot to mention: This script does not detect checkbox or select box activity; only text fields. If you have that working, let me know and I’ll append this script for everyone.












1 Comment
Jump to comment form | Comments RSS | Trackback URI