Trials with Javascript dialogs
Posted in Django
on October 13th, 2009 by
Stephen DeGrace
The built-in Javascript dialogs, alert, confirm and prompt, give very limited possibilities for customization and they don't look pretty. This encourages increasing numbers of developers to use more feature-rich alternatives where the dialog is actually a piece of HTML document which given dialog-like behaviour using Javascript.
Practically every Javascript library out there has a dialog widget that makes working with feature-rich and graphically-pleasing dialogs practically easy. I've been working with the jQuery UI dialogs. However, I have been discovering that these types of dynamic widgets do not behave like you would expect if you have done any GUI programming in other languages, and certainly not like the humble Javascript functions they might be used to replace.
The critical difference that it takes a little while to appreciate is that unlike alert and friends, which are synchronous, jQuery dialogs are in a sense asynchronous (i.e.: non-blocking) from the point of view of your script. Synchronous means that when the script comes to the line in the code with say a prompt function, it creates the prompt dialog and waits for the user to click a button and the dialog to return. It can then do something with the return value.
In contrast, when you open a jQuery dialog like so:
$('#my_dialog').dialog('open');
it simply changes the state of some elements on the page and moves on to the next line in the script. I.e., it will not wait for the dialog to close before doing the next thing in the queue! What's more, you can't return something from a dialog; the very idea is meaningless.
jQuery dialogs interact in a different way: you bind callback functions to events such as open or close, or to buttons. You can specify an arbitrary amount of buttons for the dialog to create and bind functions to all of them.
That covers a lot of possible cases, but there are some interesting gaps involving reuse. If every dialog has a very specific function and context and should always behave the same way every time then just binding the callbacks works nicely. But let's say you wanted to create a jQuery version of confirm that asks for a yes or no answer and then does something with the response. If you want to create a separate dialog for every conceivable case of where you might use it then tying callbacks to its buttons is great. But since you can't wait on a dialog (making it modal doesn't help - it just keeps the user from interacting with the rest of the page, it doesn't block script execution) and you can't see a return value, it is not trivial to devise a general solution to such simple problems.
The jQuery dialog call actually is synchronous - what the jQuery function is doing is manipulating the element on the page that you have picked out to be your dialog. It completes the manipulation, setting up the dialog, opening it, whatever, and then moves on. Since the dialog is an act of DOM manipulation, not an act of GUI programming widgetry per se, the apparent asynchronous behaviour is actually exactly what you should expect if you don't go too far into the trap of thinking you're working with a "real" dialog instead of what amounts to a cunning simulated dialog.
Here is one possible workaround. The markup you place somewhere on your page is like so:
<div id="confirm_dialog"></div>
And here is the Javascript in jQuery (Note 1: I created a jQuery plugin for this. I'll get around to posting it and making it available some time soon, and I'll link to it from here. SAD 2009-10-28. Note 2: Here is the post where I unveil my jQuery implementation of a general confirm dialog, note there are some important differences from what I wrote below. SAD 2009-11-25.):
var exit_status = 0;
$('#confirm_dialog').dialog({
autoOpen: false,
modal: true,
title: "Confirmation Required",
buttons: {
"Yes": function () {
exit_status = 1;
$(this).dialog('close');
},
"No": function (){
exit_status = 0;
$(this).dialog('close');
}
},
open: function () {
// Reset the exit_status
exit_status = 0;
},
close: function () {
// Reset title and message
$(this).dialog('option', 'title',
'Confirmation Required').text('');
}
});
function confirm_dialog(mesg, callback, title) {
// Pass a message, a callback, and optionally a
// dialog title. The callback will be called if "Yes"
// was clicked.
var dlg = $('#confirm_dialog');
if (title){
dlg.dialog('option', 'title', title);
}
dlg.text(mesg ? mesg : " ").dialog('open')
.one('dialogclose', function () {
if (exit_status) {
callback();
}
});
}
// Example of use
confirm_dialog("Do you want to say hi?",
function () {
alert("hi");
});
Some things to note about this solution. One, it's not truly synchronous. What you have to do is place all the subsequent code to be executed if the user selects Yes in the callback. Two, from a functional programming standpoint it is truly awful because it depends on a side effect, twiddling a variable in the common scope of the button callback and the dialogclose callback, for communication, to give the appearance that the dialog returned a value that the script subsequently checks and acts upon.
However, a solution like this can do the job, and arguably the current state of affairs may be an architectural necessity. Yes, Mozilla does give you a modal dialog function, but it is not widely supported, and I gather it is not all that terribly nice in any case.
Comments:
There are 0 comments on this item. Be the first to comment.
Post a Comment
* Required field, your email will not be posted.