Segunda-feira, 23 de Março de 2009
I suppose at some point of their lives, everyone wished they could just CTRL-Z (note: when I say everyone, I really mean geek people). The ability to undo one's actions is a basic principle of HCI, as it gives the user freedom to explore the application and make mistakes. Surprisingly, however, is the number of Web Applications which actually implement an undo feature in their user interface. Rather than the possibility of undoing actions, users are usually presented with the dreaded confirm box:

This kind of behaviour only serves to interrupt the user's workflow, and actually adds little value: most people would just click OK without reading the confirmation text. When developing an application, it's normal to fantasize about users
paying attention to your instructions, but you know what?
They don't. Also, the confirm box is an omen of something even more terrible: It shouts out to the user: "Be careful, once you click OK there is absolutely NO GOING BACK!".
So how do we solve this mess? How do we provide our users with a safety net that does not interrupt their workflow and also allows them to recover from mistakes? We turn to Javascript, and the 'Awesome Undo Event Queue' (yes, I came up with the name myself, thank you very much).
The implementation I'm proposing is so easy to write and adapt that I'm baffled more Web Applications aren't doing this right now. The code base is taken from
this very good post which speaks volumes about this very issue, but I've adapted it in order to provide more flexibility. Everyone should be able to implement this code throughout the SAPO Campus tailoring it to their specific undoing needs. Let's get cracking:
Assume you wanted to add undo support for the deletion of elements. Firstly, include the
undo.js file in your page(s). To actually start using the Undo Queue, you need to declare a new UndoQueue object, like this:
var undoDeleteQueue = new UndoQueue();
After this, it's time to start defining your custom functions. Currently, there are two mandatory functions which must be implemented:
doInit() and
commitToServer().
undoDeleteQueue.doInit = function () {
// Handler for the delete link.
$(".widget .closeButton").click( function(event){
var widget = $(this).parent().parent().parent().parent();
// Hide the newly deleted widget.
widget.slideUp();
// Get the ID of the to-do item. If it doesn't have one, throw
// an error.
theId = widget.attr("id");
if(theId == null ){
alert( "Error: Widget needs an id." );
}
// Add the to-do item to the event queue.
undoDeleteQueue.EVENT_QUEUE.push(widget.attr("id"));
undoDeleteQueue.updateUndoLink(undoDeleteQueue.message);
});
// Handler for clicking on the Undo link
undoDeleteQueue.undoLink.click( function(){
// Get the last to-do item added to the event queue and un-hide it.
$( "#" + undoDeleteQueue.EVENT_QUEUE.pop() ).slideDown();
undoDeleteQueue.updateUndoLink();
return false;
})
undoDeleteQueue.syncEventQueueWithCookie();
}The
doInit() function handles the initial binding of events to elements of your choice. In this example, we're adding an event to a link on the widget chrome that hides the widget and, most importantly, we add the widget ID attribute to the
EVENT_QUEUE property of our
undoDeleteQueue object. This property is simply an array that holds all the IDs of widgets the user deletes.
Next up we're defining what happens when the user clicks the undo link. This one is easy. You simply get the last ID out of the
EVENT_QUEUE, select it using jQuery and do whatever you want to make it appear again (in this case, the opposite of
slideUp,
slideDown).
Worthy of note is the function
syncEventQueueWithCookie(). If the user opens the application in different browser windows , the actions he performs on one do not sync with the other. This means that, if she deletes a widget in one window, the deletion won't be undoable in the other. We solve that with a cookie. No need to worry too much about this, just make sure you call the function at the end of your
doInit() handler. This functionality requires the
jQuery Cookie plugin.
Finally, we implement the
commitToServer() handler and initialize our undo queue, like this:
// Implements undoQueue.commitToServer
undoDeleteQueue.commitToServer = function() {
// your AJAX call to a page executing some server-side magic goes here.
}
// Initializes the object
undoDeleteQueue.init();Inside the
commitToServer() function, you would perform some sort of AJAX call to your server that would actually delete the items. This is called by default when the user leaves the page, but you can call call it manually whenever you wish. Afterwards, just initialize the object and you're good to go!
Here is the list of other properties you can set:
- undoLink: jQuery object of the element which can be clicked to undo actions. Default is $('.undo');
- message: The text you wish to display before undoLink. Default is "This action can be undone.";
- supportsCtrlZ: Boolean that decides whether to use keyboard binding functionality. Defaults to true. The combo is CTRL-Z for Windows/Linux and COMMAND-Z for Mac OS X. Depends on the js-hotkeys jQuery plugin.
Last, but not least, here is the
source file. Have fun!
UPDATE: Support for CTRL-Z (and COMMAND-Z for Mac OS X) is in. Use the property supportsCtrlZ to control the functionality.
Great! Extra coffee for Bruno :)
Comentar: