Advanced button theming on jQuery UI Dialog

Check out the update to this post!

There is a very annoying bugfeature that I have found when using jQueryUI. It’s not the themes, they work great, in fact, they are the best thing since sliced bread. But in a ‘Cupertino.esc’ way, they provide too much simplicity over the functionality that I require. This post aims to solve that in a progressive enhancement sorta way. I am using jQuery 1.4.2. with jQuery UI 1.8.2, it doesn’t matter what theme you use with this, but I prefer redmond.

Let’s take a look at the core issue. Here is what a typical dialog looks like in jQueryUI 1.8.2 with some nice buttons.

screen-capture-1-300x191

Here’s the simple code for this.

1
2
3
4
5
6
<article id="dialogs">
<a href="javascript: void(null);" id="dialogbutton">Open Dialog</a>
<div title="this is my test this is my test this is my test this is my test" id="dialogtest">
This is a div dialog test. This is a div dialog test. This is a div dialog test. This is a div dialog test. This is a div dialog test.
</div>
</article>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script type="text/javascript">
$(document).ready(function() {
/* Create a dialog */
$("#dialogtest").dialog({
title: "Alert!",
modal: true,
buttons: {
"Ok": function() {
$(this).dialog("close");
},
"Cancel": function() {
$(this).dialog("close");
}
}
});

/* Add Dialog Button Functionality */
$('#dialogbutton').click(function(){
$('#dialogtest').dialog('open');
return false;
});
});

</script>

That’s really great, I mean that code is so small and yet you can get such a great looking item out of it. So how can I have a problem with that? Well let me tell you how the buttons are made, you have a name and then you have that name point to a function. The name is the text of the button, the function is the action of the button. And that’s it. Period.

Now, I find it great that I can use these simplistic featuresets and get ‘get by’ but normally my “Cancel don’t ever touch me button” is the color red(ish) while my “Oh you need to hit me to move on” button is the color green(ish). But you cannot do that (easily) with this current setup. In fact it’s very sloppy to force this setup into doing that and it ends up using a great amount of code just to do one thing, add a class.

To solve this, I did what any self respecting JavaScript ninja would do, I said goodbye to jQuery UI I opened up the source code and took a peek, found out where I could attack (fix) the code and I did it.
So before I show you the code, I will show you how I decided buttons should/could be made. My process is 100% backward compatible, yet more difficult and easier to mess up (the original goal of jQueryUI was obviously to make it as simple as possible).

Buttons with my code changes can now be made like this

1
2
3
4
5
6
7
8
9
10
11
buttons: {
"Ok": function() {
$(this).dialog("close");
},
"Cancel": {
action : function() {
$(this).dialog("close");
},
type : "cancel"
}
}
1
2
/* This is poor CSS by design to be simple and readable */
div.ui-dialog-buttonpane button.cancel { background:none; border:0px none;color:red; }

and the old way of doing it will still work just fine as well.

I’ve gone ahead and added a few features, namely it is now name-points-to-object instead of the old name-points-to-function mentality. This object has two attributes: action and type. Action is the old function() that was pointed to and it will be called when this button is clicked. Type is the new feature on the block, it’s just a CSS class that is added to the function using jQuery.addClass. I’ve tried to keep is simple, and at the same time, make it robust and backward compatible. Here’s a simple change that is possible using this feature.

screen-capture-2-300x203

Now my dialogs can look like this when I want them to. This is a big usability thing (make the buttons descriptive, not just in text but in color/design and function).

So the secret sauce to all of this is actually an extension of the jQueryUI 1.8.2 Dialog code (more like rewrite of some of the code). This code can only be ran AFTER jQueryUI 1.8.2 has been created, otherwise you will get an error. I placed this code into the jQueryUI 1.8.2 custom.js file itself for easy keeping; just drop it at the bottom of the file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/* TorchUI dialog button fix */
(function($) {
var _createButtons = $.ui.dialog.prototype._createButtons;
$.ui.dialog.prototype._createButtons = function(buttons) {
var self = this,
hasButtons = false,
uiDialogButtonPane = $('<div></div>')
.addClass(
'ui-dialog-buttonpane ' +
'ui-widget-content ' +
'ui-helper-clearfix'
);
// if we already have a button pane, remove it
self.uiDialog.find('.ui-dialog-buttonpane').remove();

if (typeof buttons === 'object' && buttons !== null) {
$.each(buttons, function() {
return !(hasButtons = true);
});
}
if (hasButtons) {
$.each(buttons, function(name, fn) {
/* Begin the code change here */
// Check to see if fn is being passed in as a funciton, assume it's an object if not.
if(!$.isFunction(fn)) {
var buttonclass=fn.type;var func=fn.action;
} else {
var func=fn; var buttonclass="; }
var button = $('<button type="button"></button>')
.text(name)
/* Add a class to the button, if there is a class */
.addClass(buttonclass)
.click(function() { func.apply(self.element[0], arguments); })
.appendTo(uiDialogButtonPane);
}
if ($.fn.button) {
button.button();
}
});
uiDialogButtonPane.appendTo(self.uiDialog);
}
}
})(jQuery);

If you have questions, please respond, I promise not to take them personally :)