Tuesday, January 10, 2012

Save confirmation when leaving a web page

User story

A user opens a web form, fills in values in input elements, do not click "Save" button and clicks any navigation link. A confirmation message is shown: "There're unsaved changes. Do you wish to save them before navigating away?". If the user clicks "OK" - changes are saved and the desired page is opened. Clicking "Cancel" opens the destination page without saving user input.

Implementation

The idea is to have a JavaScript global object which is capable of tracking changes, can be called by custom JavaScript code (in order to notify the object about changes), which shows confirmation message in case of clicking navigation links and calls back to custom code in order let it do server requests and save data.

The first part is the prototype itself and global object instantiation (mind JQuery is required):

function ChangesTracker () {
this.confirmMessage =
"There're unsaved changes on the page. Do you wish to save them before navigating away?";
this.selectors = ["html body table tbody tr td.Toolbar a"];
this.ignoreSelectors = ["a#save", "a#remove"];
this.changedFlag = false;
this.enabled = false;
this.eventPublisher = {};
}

ChangesTracker.prototype.setChanged = function (state) {
this.changedFlag = state;
}

ChangesTracker.prototype.raiseOnLeave = function () {
if (this.enabled && this.changedFlag == true && this.showConfirmation()) {
$(this.eventPublisher).trigger("onLeaveWithChanges");
return true;
}
return false;
}

ChangesTracker.prototype.bindOnLeaveEvent = function (handler) {
$(this.eventPublisher).bind("onLeaveWithChanges", handler);
}

ChangesTracker.prototype.showConfirmation = function () {
return confirm(this.confirmMessage);
}

ChangesTracker.prototype.enable = function (selectors) {
var self = this;
if (selectors) this.selectors = selectors;
var query = $();

for (var i in this.selectors) {
query = query.add(this.selectors[i]);
}

for (var i in this.ignoreSelectors) {
query = query.not(this.ignoreSelectors[i]);
}

query.click(function () {
self.raiseOnLeave();
});

this.enabled = true;
}

ChangesTracker.prototype.disable = function () {
this.enabled = false;
}

var changesTracker = new ChangesTracker();

$(document).ready(function () {
changesTracker.enable();
});

The above code can be put to a separate script file and referenced on the pages which require changes tracking. Referencing the script will initialize a global variable changesTracker which is a ready to use obejct. The highlights about the object are:

<script type="text/javascript" src="JavaScripts/ChangesTracker.js"></script>
<script type="text/javascript" language="javascript">
changesTracker.bindOnLeaveEvent(function () {
$('form#fields').submit();
});

$(document).ready(function () {
$("#table.fields input").change(function () { changesTracker.setChanged(true) });
});
</script>

In our case we assume that there's a table with a number of <input> elements in it. The table is placed within a form with id fields. The 2 things we need to do are:
  • Notify the tracker about changes
  • Respond to tracker event and do the save

Known issues

1. Not working in IE6 and IE9 in comparability view mode
2. Form validation require minor amendments to the solution above (e.g. bindOnLeaveEvent may need args in order let the tracker know whether to leave the page)

No comments:

Post a Comment