jQuery Confirm Dialog Plugin

Posted in Tech on November 11th, 2009 by Stephen DeGrace Link

This is a jQuery plugin to mimic the functionality of the standard Javascript window.confirm(message) function provided by the BOM in all browsers, except that it's prettier. You can download the package here. It requires jQuery, as well as the jQuery UI dialog component, and the draggable component as well if you want the dialog to be draggable.

Before using this dialog, it's a good idea to read this article. The basic limitation of all dialog widgets provided by the big Javascript libraries is that they are fundamentally and by design non-blocking. They can't return anything - the concept of them having a return value is meaningless. They aren't really like the dialogs you might be familiar with from normal GUI programming - they might almost be described more accurately as simulated dialogs.

Any code you place after the dialog call will be executed immediately without waiting for the user response and without the least bit of concern for it. To my knowledge, there's no good way around this. So, if you want to use these types of dialogs, you have to change your mentality to the type of creature you're actually working with. I.e., all code that depends on the result of a user interaction with the dialog must be placed in a callback bound to some control on the dialog.

My confirm dialog does not circumvent these limitations. Once you get used to it I find it works quite well, but it takes a fundamentally different coding mindset to use than the built-in Javascript dialogs. It works by making a one-time binding to the dialog's close event. If the dialog closed with an affirmative exit status (only possible with a user affirmative selection), the callback you pass will be executed, and this is where all the code depending on affirmative user response must go. If the dialog closes any other way the dailog defaults to having a negative exit status, meaning the callback will not be executed.

The code in the package contains a lot of comments to explain how it works. The basic code is as follows:

(function ($) {
    var Defaults = function () {};
    $.extend(Defaults.prototype, {
        yes: "Yes",
        no: "No",
        title: "Confirmation Required",
        msg: "Do you wish to proceed?"
    });
    
    $.confirmDialog = function (options, callback) {
        // Pass the options and a callback to execute if affirmative user
        // response.
        var opts = new Defaults();
        $.extend(opts, options);
        
        var exit_status = 0;
    
        var yesFunc = function () {
            exit_status = 1;
            $(this).dialog('close');
        }
        var noFunc = function () {
            exit_status = 0;
            $(this).dialog('close');
        }
        
        var dlg = $('body').append("<div></div>").find('div:last')
                .dialog({
                    autoOpen: false,
                    modal: true,
                    close: function () {
                        // Clean up
                        dlg.dialog('destroy').remove();
                    }
            });
        
        // Set options, open, and bind callback
        var buttons = new Object();
        buttons[opts.yes] = yesFunc;
        buttons[opts.no] = noFunc;
        dlg.dialog('option', 'title', opts.title);
        dlg.dialog('option', 'buttons', buttons);
        dlg.text(opts.msg ? opts.msg : "&nbsp;").dialog('open')
            .one('dialogclose', function () {
                if (exit_status) {
                    callback();
                }
        });
    }
    
    $.confirmDialog.defaults = function (options) {
        $.extend(Defaults.prototype, options);
    }
    
})(jQuery);

The main thing I find interesting about this code is that it does not exactly follow the jQuery recommended plugin pattern when it comes to the options. jQuery authors recommend that you create a default options object and use jQuery.extend to override it with options passed in by the user.

The problem with this is that every call changes the defaults themselves. If you plan to call the plugin more than once, you have to account for changes to the default options caused by previous calls.

My plugin avoids this by having immutable defaults that are overridden on each call but only for that call. However, the defaults themselves can be changed with jQuery.confirmDialog.defaults().

I accomplished this by making Defaults a blank object (really an empty function) instead of the usual anonymous object (really a hash). Then I extended the prototype of my Defaults object with the initial default options. The confirmDialog function which opens the dialog creates a new object from this prototype and extends this new object, leaving the original prototype untouched, and achieving the desired functionality. The confirmDialog.defaults function extends the original prototype with the passed in options, thus changing the defaults for all future calls.

Remembering that jQuery "dialogs" can be modal in the sense of stopping interaction with the rest of the page but not modal in the sense of stopping program flow and waiting for a response before proceding, you have to place all code that is contingent on the user response in the callback argument to confirmDialog.

Here's a basic example of usage. In this example, the programmer first chooses to use confirmDialog.defaults to change the default options for all future calls. Then he opens the dialog overriding one of the new defaults for that call. Note that the call to $.confirmDialog.defaults is optional if you like the defaults the plugin is packaged with.

$.confirmDialog.defaults({
    yes: "Oui",
    no: "Non",
    title: "Confirmation exigé",
    msg: "Est-ce que vous voulez continuer?"
});

$.confirmDialog({msg: "Voulez-vous dire «hi»?"}, function () {
    alert("Hi");
});

Getting variable text for the affirmative and negative buttons was a little tricky too...

Comments:

There are 4 comments on this item.

On November 24th, 2009 Ashley Link wrote:

The link for download provided serves an invalid arhive file (well according to my Fedora based archive manager anyway.) It's corrupt in some way: invalid headers.

On November 24th, 2009 Stephen DeGrace Link wrote:

Thanks for pointing that out! It's a weird problem and I still haven't figured it out, but I did correct it. Here's the deal... I used Ubuntu to make a tar.gz of the single file and SFTPed it to my web server, where it gets served by a separate Apache instance as a static file. Now when I download the file back to my computer by SFTP, it opens normally. But when I download it by HTTP I see the same problem as you, the file seems to be corrupt.

The solution is to put the file into a directory and compress the directory. You can then download by HTTP and open normally. For some reason if the archive contains only the one file at the root level, it doesn't work. Weird. I have no idea why it's doing that, but at least I was able to fix it.

On November 25th, 2009 Stephen DeGrace Link wrote:

I added some commentary at the top to explain more about the usage and link to a post that ended up leading to the confirmDialog plugin. The confirmDialog is a replacement for window.confirm but it doesn't escape from the fundamental limitations of this style of dialog, and it takes a major mental shift to begin using it. At least, it took me a long time to grasp!

On November 26th, 2009 Ashley Kitson Link wrote:

Stephen - thanks for your help in getting this working as a replacement for the jscript confirm() dialog. I've sent you feedback by email which hopefully will get into your next release.

Post a Comment

* :
* :
:
:

* Required field, your email will not be posted.