On 01/07/2011 23:44, William Gill wrote :
Hi,
One of us is not following, and I'm not sure if it's you or me.
It was me!
Now suppose a user is on page 10 (of 14), has answered 3 of the 5
questions on this page, but one of the questions makes them rethink a
previous answer. Here's where the problem occurs. Instead of clicking
one of the two submit buttons (the one labeled "back"), they use a
mechanism they are already familiar with: the browser back button. They
go back however many pages, change the answer, then return to where they
left off. Since they left page 10 without properly submitting it, the
three answers they had previously entered were never updated anywhere.
Some have implied that the answers may be retained in the browser cache,
but even that is unreliable. Anyway assuming the fields are empty and
all three empty fields appear on the user's browser they should see them
and reenter the information, but there is no guarantee they will notice
or refill them. to exacerbate the situation, if page 10 spans two or
three screens, the likelihood they will notice and correct things is
diminished. Either way, the user is annoyed because they have to do
anything over.
Thank you for your explanation. As Denis and Tim have suggested, you can
use many approaches.
The first approach uses client-side state keeping, with cookies. It is
simple to use, you can build a StateManager which loads all form data
when the page is loaded, and persists it each time a form field is changed.
The second approach uses server-side state keeping, with Ajax or other
techniques (see <URL:
http://www.jibbering.com/faq/#runServerScript>).
Your StateManager lives on the server-side presentation layer, which is
fine as well.
Both approaches look fine to me. I can see a tiny usability risk with
the client-side state keeping, in that users, seeing data in the form,
could be led to believe that they have already submitted the data
(forgetting they did not), and continue the workflow without submitting
it. On the other hand, I can spot some performance issue with the
server-side state keeping: depending on which event you transmit your
data, many users filling in the form at the same time can quickly have
lots of requests generated.
I would slightly favor the server-side state keeping, but YMMV.
So to restate my concern is as simple, and inclusive a manner as
possible: Is there any way to force a submit() regardless of how the
page is exited?
The FAQ says it is difficult: <URL:
http://www.jibbering.com/faq/#sessionExpired>. The point is to identify
which event can be used to submit the data. "onunload", as explained by
the FAQ, may not be reliable. "onbeforeunload" is reliable but I do not
know if it is implemented consistently in modern browsers. Therefore,
what about using "onchange", for each field?
As an illustration, have a look at the following script (my personal
"Good Morning Javascript!" broadcast). It is built in two parts: a
StateManager definition, which you can insert as a regular script
include, and a StateManager initialization, which you have to position
into a SCRIPT element in the main page, either in a window.onload event,
or after the form you want to monitor.
The initialization part :
---
StateManager.init(document.forms[0]); // reference to the form
---
The main script:
---
var StateManager = (function() {
function QueryStringBuilder() {
this.parts = [];
}
QueryStringBuilder.prototype.add = (function (el) {
switch (el.type.toLowerCase()) {
case "text":
case "hidden":
case "textarea":
case "password":
this.parts.push(pair(el.name, el.value));
break;
case "checkbox":
case "radio":
if (el.checked) {
this.parts.push(pair(el.name, el.value || "true"));
}
break;
case "select":
case "select-multiple":
for (var ii=0; ii<el.options.length; ii++) {
if (el.options[ii].selected) {
var option = el.options[ii];
this.parts.push(
pair(el.name, option.value || option.text)
);
}
}
break;
}
function pair(name, value) {
return encodeURIComponent(name) +
":" +
encodeURIComponent(value);
}
});
QueryStringBuilder.prototype.getValue = (function (el) {
return this.parts.join("&");
});
function addListener(target, evt, listener) {
if (target[evt]) {
target[evt] = (function(oldListener) {
return (function() {
oldListener.apply(this, arguments);
return listener.apply(this, arguments);
});
})(target[evt]);
} else {
target[evt] = listener;
}
}
function persistData(form) {
var img = new Image();
var qs = new QueryStringBuilder();
var qs_value="";
for (var els=form.elements, ii=0; ii<els.length; ii++) {
qs.add(els[ii]);
}
qs_value = qs.getValue();
if (qs_value) {img.src="workflow?" + qs_value;}
}
function fieldListener(evt) {
persistData(this.form);
}
return ({
init : (function(form){
for (var els=form.elements, ii=0; ii<els.length; ii++) {
if (els[ii].type.toLowerCase() != "submit") {
addListener(els[ii], "onchange", fieldListener);
}
}
})
});
})() ;