Unobtrusive custom confirmation dialogs with jQuery
I need confirmation dialogs, I need them to be dynamic, unobtrusive, custom styled and flexible. Most of the conditions rule out the built-in confirm()
function in Javascript.
So I decided to build my own with jQuery and share the basic ideas behind the solution.
Background
First off – quick overview about what a Javascript confirmation dialog is and what it does (perhaps someone is not familiar with it). Javascript has a built-in global function named confirm
which takes one argument, a string, as the content.
It returns a boolean value (true/false) depending on if the user clicked on either “ok” or “cancel” buttons.
The basic usage looks like this:
alert("Seems like you do!");
}
You can click the link below to see how it looks in action.
Click me
Round one: stating the goal
What I want to achieve is that I could attach a confirmation dialog to any element and event, without having to wrap my code around if(){..}
statements. I also want my dialogs to look cool (i.e custom styled).
By using Eric Martins SimpleModal plugin to render the dialogs, the “cool look” part is resolved.
The expected usage would look something like this:
<div class="delete_button">
<a href="javascript:void(0);" class="confirm {message: 'Are you sure you want to delete this item?'}">Delete this item</a>
</div>
...
In the example, the link would submit an AJAX request, which triggers the actual delete action.
I want to be able to hook into that event, and inject my confirmation dialog before the actual AJAX is done, and control the flow of the invocation according to which option the user chooses from the dialog.
With the help of the awesome Metadata plugin, I can provide the confirmation message dynamically as metadata on the element I want to “hijack”.
Round two: writing the implementation
One more thing before I go on – perhaps there is need to display confirmations on other events too, like change, keydown, keypress
etc.
This can be supported with little code, by just adding another parameter to the metadata, for example event
.
Now I have all the requirements in place, nothing else to do than write some Javascript code!
jQuery(".confirm").each(function(){
/** Query the metadata info */
var element = jQuery(this);
var event = element.metadata().event;
var message = element.metadata().message;
/** Bind an actual handler to the event(s) configured
* As jQuery supports the event binding in formats of "click" or "click, change" etc,
* you can configure it to trigger on several events */
element.bind(event, function(event){
/** This basically "consumes" or kills the event, preventing it from
* being passed to any other handlers on this element */
event.stopImmediatePropagation();
/** Create a new modal window from HTML already present in the document */
var window = jQuery(".confirmation_dialog").modal({
opacity: 50,
closeHTML: "",
closeClass: "confirm_cancel"
});
/** set the configured message to display */
window.d.data.find("span.message").text(message);
/** focus the OK button (you could focus on cancel if you like) */
window.d.data.find("a.confirm_ok").focus();
/** bind an event on the ok button, both for click and keypress events */
window.d.data.find("a.confirm_ok").bind("keypress click", function(e){
if(e.keyCode == 13 || typeof(e.keyCode) == "undefined"){
window.close();
}
});
});
})
});
Important note: As I want to attach my confirmation handler to elements before anything else is attached (so it would be fired first), this code needs to be placed before any other event attachment code.
The example above should now render a custom dialog with “ok” and “cancel” buttons (or whatever the design of the dialog defines), just that clicking the buttons does nothing besides closing the dialog and consuming the event.
So what I need is something to track the users action with plus somehow invoke the original event bound on that element.
This can be achieved in two ways – unbinding the confirmation dialog event handler from the element and then invoking the original handler, or storing the state of the users action on the elements data (jQuery(element).data()
).
As I’m reluctant to extract my confirmation handler for easy un- and rebinding, I’ll go with the second option.
element.bind(event, function(event){
/** if the elements propagation is already stopped
* set it to false (reset it) and return true, skipping the dialog display etc logic */
if(element.data("propagationStopped")){
element.data("propagationStopped", false);
return true;
}
...
/** bind an event on the ok button, both for click and keypress events */
window.d.data.find("a.confirm_ok").bind("keypress click", function(e){
if(e.keyCode == 13 || typeof(e.keyCode) == "undefined"){
window.close();
/** set the propagation as stopped, needed for the next invocation of this handler */
element.data("propagationStopped", true);
/** trigger the handlers for 'event' on the element */
element.triggerHandler(event);
}
});
...
Notice the calls to element.data("propagationStopped")
and element.triggerHandler(event)
.
The logic is that once the user clicks “ok”, the state is saved to the elements data and all the handlers on the element are triggered again, which also results in the confirmation dialog event code to be ran again.
That’s why we check if the elements data contains “propagationStopped” key, and if it’s value is true, it means the user has pressed “ok” and are dealing with the subsequent invocation of the event.
So now, instead of showing the dialog again, the “propagationStopped” value is reset and the method will return instantly, skipping all the confirmation dialog logic and letting the other attached handler do their tasks.
In theory this code should be usable with any kind of modal dialog You like, the only bits that need changing are the dialog initialization and handling the button clicking events.
1 Comment
Could you post an example -link, zip, whatever- to see the real functioning?