I managed to solve my problem and get all my events to sequence properly. It took some clever use of Deferreds and lots of trial and error before I got the hand of what I was actually doing. Anyway, in case anyone needs this solution for anything they are working on...
The Problem:
I wanted a message box pop-up on my page to essentially replace the browser's alert window for my purposes. I wanted to chain some fade and slidedown animations to show the message box and then its contents. I also wanted a loading image to appear in the message box when it first appeared, while an asynchronous request was sent to the server. The the message box would show the response. Anyway, this is how I set it up...
In the body of my html, I included the message box container:
<div id="msg-box">
<div id="msg-box-container">
<div id="msg-box-headbar">
<div id="msg-box-title"></div>
<div id="msg-box-clButton"></div>
</div>
<div id="msg-box-content"></div>
<div id="msg-box-footbar"></div>
</div>
</div>
<div id="msg-box-backdrop"></div>
As you see, my message box also has a backdrop (which I set up as a semi-transparent div that resides outside of the message box's container). The backdrop will be shown and animated simultaneously but independently of the message box itself. This is another complication, since the sequence and timing of events has to be executed correctly, otherwise we'd get strange effects... like the backdrop doing one thing while the message box is doing another.
The Script:
To make my code a bit cleaner that in my first attempt, I created an object literal for the message box with a set of functions to manipulate the message box. It took a lot of research, but the solution was ultimately to use a combination of chaining and Deferreds. Utimately, my code looks like this:
var msgBox = {
status: 0,
self: $('#msg-box'),
title: $('#msg-box-title'),
cl_button: $('#msg-box-clButton'),
content: $('#msg-box-content'),
backdrop: $('#msg-box-backdrop'),
hideBox: function() {
var b = this;
if (b.status == 1) {
var dfd = $.Deferred();
b.self.fadeOut(500);
b.backdrop.fadeOut(500, dfd.resolve);
b.status = 0;
return dfd.promise();
}
},
showBox: function(ttl, msg) {
var b = this;
if (b.status == 1) {
$.when(b.hideBox()).then(function() {
b.doShow();
b.loadCont(ttl, msg);
});
}
else {
if(b.backdrop.queue().length > 0) {
$.when(b.backdrop.queue()).done( function() {
b.doShow();
b.loadCont(ttl, msg);
});
}
else {
b.doShow();
b.loadCont(ttl, msg);
}
}
},
doShow: function() {
var b = this;
b.title.text('Loading...');
b.content.html('<div id="msg-box-loader"><img id="loading-img" src="/graphics/loader-16.gif" /></div>');
b.backdrop.height(b.self.height());
b.self.centerBox($(window).width());
b.backdrop.centerBox($(window).width());
b.self.fadeIn('fast');
b.backdrop.corner('round 6px').fadeTo('fast', 0.6);
},
loadCont: function(ttl, msg) {
var b = this;
b.content.delay(1500).queue(function(next) {
b.title.text(ttl);
b.content.text(msg).slideDown('fast');
b.backdrop.height(b.self.height()).slideDown('fast');
b.status = 1;
next();
});
}
};
As you can see, at several points in the hide / show sequence, I use Deferreds to allow certain events to complete before launching new ones. For reference, these are my bindings:
// events related to message box
$(document).keydown( function(e) {
if (e.keyCode == 27) {
e.preventDefault();
if(msgBox.status == 1)
msgBox.hideBox();
}
});
msgBox.cl_button.hover( function() { $(this).toggleClass('no-hover').toggleClass('hover'); });
msgBox.self.on('click', msgBox.cl_button.selector, function() { msgBox.hideBox(); });
$(window).resize(function() {
if (msgBox.status == 1) {
msgBox.self.centerBox($(window).width());
msgBox.backdrop.centerBox($(window).width());
}
});
// login form submitted
$('#login-form').submit( function(e) {
e.preventDefault();
var postData = 'async=login&user='+$('#login-user').val()+'&pass='+hex_md5($('#login-pass').val());
$.ajax({ type: 'POST', url: "index.php", data: postData,
success: function(response) {
if(response==0) msgBox.showBox('Login Failed', 'Invalid Credentials');
else if(response==1) msgBox.showBox('Success', response);
else msgBox.showBox('Internal Error', 'There was an internal error. Please contact the administrator.<br><br>'+response);
},
error: function() {
msgBox.showBox('Internal Error', 'There was an internal error. Please contact the administrator.');
}
});
});