Background
I’ve recently been seeking to implement dialog boxes in the online learning application I am building (perhaps for launch by March ’09 – watch this space). Having learned some hard lessons about getting these seemingly straightforward bits of display to render correctly while working for the start up BView, I was determined to harness the collective intelligence embedded in one of the great UI frameworks of the enlightened times we live in. A fortnight later, after much pain, frustration and incandescent programming-induced rage, I’ve settled for a slightly ‘hacked’ implementation of jQuery UI’s Dialog.
In the past 6 months I’ve morphed from a Java/Spring MVC/Hibernate web application developer into a Google App Engine/Python developer with sole responsibility for the design and implementation of my application’s user interface. So, I am a bit novice when it comes to more advanced UI-coding and best practices, but I do have a good appreciation for JavaScript APIs, such as jQuery, and I have sought to take advantage of robust UI APIs in building my app. With the understanding that there are gargantuan gaps in my knowledge and skill set, I’d like to share with readers my experiences with YUI’s and jQuery’s Dialogs. On the one hand, I might get some guidance from more capable readers, and on the other I might help other noobs avoid some of the intense pain I suffered especially as regards the freshly born (and slightly flakey) jQuery Dialog.
I built my initial UI using YUI Grids and LayoutManager. I found this process to be pretty straightforward and have been really impressed with these products’ reliability and performance cross-browser (resizing is weird in Chrome, but that seems to be a more general issue). When it came time to add buttons to the site, I naturally opted for YUI Button, a basic implementation of which I was able to get running super fast. However, customising the look and feel seemed to require more effort than I was willing to put in at this early stage of development, and I left that to one side. The complexity of YUI and the steepness of the learning curve really hit home, however, when I sought to implement YUI Dialog.
The Use Case
I’ve got a list of items. Next to each item is a cross icon the user may click to delete. The goal is to display a Yes/No dialog to confirm a user’s intent to delete a given item before initiating an AJAX call to delete the item.
Attempt 1: YUI Dialog
Having had a reasonably carefree relationship with YUI up to this point, I initially sought to use YUI’s Dialog. I was able to get a basic example from YUI’s site running in short order:
YAHOO.namespace(“example.container”);
function init() {
YAHOO.example.container.simpledialog1 = new YAHOO.widget.SimpleDialog(“simpledialog1”,{
width: “20em”,
fixedcenter: false,
visible: false,
draggable:false,
close: true,
text: “Delete this”,
icon: YAHOO.widget.SimpleDialog.ICON_WARN,
constraintoviewport: true,
buttons: [ { text:”Yes”, handler:handleYes, isDefault:true },
{ text:”No”, handler:handleNo } ],
context: [“show”, “bl”, “bl”]
} );
YAHOO.example.container.simpledialog1.setHeader(“Are you sure?”);
YAHOO.example.container.simpledialog1.render(“container”);
YAHOO.util.Event.addListener(“show”, “click”,
YAHOO.example.container.simpledialog1.show,
YAHOO.example.container.simpledialog1, true);
YAHOO.util.Event.addListener(“hide”, “click”,
YAHOO.example.container.simpledialog1.hide,
YAHOO.example.container.simpledialog1, true);
}
YAHOO.util.Event.addListener(window, “load”, init);
In my markup I added:
div class=”yui-skin-sam”
div id=”container”
button id=”show”Show simpledialog1/button
button id=”hide”Hide simpledialog1/button
/div
/div
This worked like a dream. Problems came thick and fast, however, when I sought to extend this simple use case to fit the needs of my app. Essentially I could not figure out how to dynamically instantiate multiple dialogs, each one associated with a particular delete cross. For instance, where I retrieved all elements with a particular class (e.g. all elements with .delete-cross) I was unable to loop the returned array and create a new YAHOO.widget.SimpleDialog with a variable name and etc dynamically generated from the element’s id attribute. Ultimately I was only able to create one YAHOO.widget.SimpleDialog which was associated with the last element in the collection. It was around this time that Twitter lit up with news of jQuery’s 1.3 release and I thought, ‘I wonder if my beloved jQuery’s UI API is as kick ass as its JavaScript API?’
Attempt 2 : jQuery Dialog
I went to the jQuery UI site and hit the Gallery / Themeroller, which had me very excited as it looked like I could get something much more inline with my app’s look and feel ‘out of the box’ than with YUI and perhaps roll it out for my buttons and etc. I got the resources downloaded and into my project, no drama. I got the basic use case running in a snap, i.e. (“#dialog”).dialog(“open”). I was feeling full of hope and optimism and generally love for my old pal jQuery. These rosy feelings soured as, again, going beyond the basic use case was exceptionally problematic.
Once I started to customise the dialog that was instantiated in the JavaScript, things went haywire. In particular setting autoOpen to false (it defaults to true) caused me no end of hassle. The documentation claims: “When autoOpen is true the dialog will open automatically when dialog is called. If false it will stay hidden until .dialog(“open”) is called on it.” Indeed, setting autoOpen did not hide the dialog div on page load. I attempted to hide the dialog div on page load by: a) setting style=”display: none”; b) adding .ui-helper-hidden to the markup. Both of these approaches did hide the dialog div on page load, but when .dialog(“open”)was invoked, the dialog did not render correctly; the content area of the div was obscured because it was not clearing between the title and content areas of the dialog.
OK, back to the jQuery UI theming API where I discovered some utility classes, but things got especially hairy when, if I added .ui-helper-clearfix, the dialog div was displayed on page load, but when opened the dialog’s content was rendered incorrectly. Calgon – take me away! I resolved these issues, but am not satisfied with my hack-around and I would like to be able to use the API in a more pure way.
I added an empty dialog div in the markup:
div id=”dialog” title=”Are you sure?” class=”ui-helper-clearfix”
p
span id=”icon”/span span id=”message”/span
/p
/div
I then dynamically generate the spans’ content in the JavaScript:
$(“#message”).html(“Delete this?”);
$(“#icon”).addClass(“ui-icon”).addClass(“ui-icon-alert”);
I then instantiate and open the dialog:
$(“#dialog”).dialog({
autoOpen: false,
bgiframe: true,
buttons: {
“Yes”: function() {
delete();
},
“No”: function() {
$(this).dialog(“close”);
}
},
minHeight: 0,
modal: true,
overlay: {
backgroundColor: ‘#000000’,
opacity: 0.5
},
resizable: false
});
$(“#dialog”).dialog(“open”);
At this point, I thought I was good to go and could go in peace that’d I’d gotten this running in an acceptable, if less than optimal way. Oh, silly, silly me.
While I found tweaking the CSS used in the dialog to get it just right for my app much easier than with YUI, overriding the minHeight default of 47px by setting the minHeight option in the constructor caused IE7 to yelp in pain. So, bring on the browser sniffing:
if($.browser.msie){
$(“#dialog”).dialog({
autoOpen: false,
bgiframe: true,
buttons: {
“Yes”:function() {
delete();
},
“No”: function() {
$(this).dialog(“close”);
}
},
modal: true,
resizable: false
});
$(“#dialog”).dialog(“open”);
} else {
$(“#dialog”).dialog({
autoOpen: false,
bgiframe: true,
buttons: {
“Yes”: function() {
delete();
},
“No”: function() {
$(this).dialog(“close”);
}
},
minHeight: 0,
modal: true,
overlay: {
backgroundColor: ‘#000000’,
opacity: 0.5
},
resizable: false
});
$(“#dialog”).dialog(“open”);
}
This solution is still being reworked as I hope to get overlay to play nice in IE7, but this is pretty much how I got where I needed to go. It is much less elegant than I’d like, and while it is acceptable in FF3 and IE7 it starts scrolling the content area in Chrome (but is anyone using it?).
Conclusion
While I believe that YUI might provide a more robust solution, I find building up the complexity of an implementation too difficult considering pressures in terms of releasing my app this quarter (already a quarter behind schedule, but then I made major tech shift to GAE in November so can live with that). If it was more clear how to dynamically instantiate the YUI widget, I might’ve stuck with it and struggled with customising the skin, but as the jQuery option allowed much faster take-up and customisation, it is much more practical for me at this time. I am hopeful that as the jQuery UI project matures it will become as resilient and reliable as its JavaScript API.