So I figure what better way to learn more about jQuery than to dig right in and create a plugin or two? At first I thought this sounded pretty intimidating, but after browsing through some of the existing jQuery plugins I figured it might be easy enough to try and ‘rearrange’ one of the Toolkit controls that I created. So that is what I did with my GlowButtonExtender control I created a few months back (and I plan on doing it with the ProgressBar one as well very soon). I kind of liked this because I thought creating the same UI widget using both the Toolkit and jQuery might give a little more insight into what the relative advantages are between the two frameworks.
In case you didn’t see my original post on creating the GlowButtonExtender control, here is how it works: When you mouse over the button, it glows as it comes into focus and of course when the mouse leaves the glow slowly fades away. These are just screen shots so you will have to check out the demo page to see it in action.
Live Demo (IE7, FF and Opera)* | Download
* IETester seems to be on the fritz – so I wasn’t able to test in IE6.




Footprint
Before I jump into the details, I just thought that I would mention that from now on, all of my posts (at least the ones that include a live demo) will include a section titled Footprint that has a brief discussion on the size of JavaScript and CSS resources my post is making use of. I really should have been doing this all along, but better late than never, right? So here it is for this post. I am using minified versions of jQuery and the metadata plugin (58 KB total combined) plus the color plugin (another 4 KB). And finally, the JavaScript for my glowbuttons plugin is a whopping 3 KB un-minified plus another 2 KB for the CSS.
JavaScript: Total 64 KB
CSS: Total 2 KB
Basic jQuery Plugin Template
So to get started creating my glowbutton plugin I first did some googling and came across a great resource that goes over some of the basics about creating jQuery plugins. I recommend reading (and understanding) the code samples. Below is the shell that I started with for my plugin. I would pay particular attention to the following …
- There is a decent amount of plumbing that really needs to be included in your plugin to make sure it can play together nicely with other jQuery plugins as well as other JavaScript libraries
- Notice the first and last lines. The plugin is wrapped in a self executing function that takes a single parameter ‘$’. And the last line of code invokes the function passing in the global jQuery object. If you follow this pattern you can safely use the $ shortcut to refer to the jQuery object without worrying about colliding with other bits of JavaScript code that might also be running on the client. If you don’t write your plugin using this technique I believe you should not be using the $ shortcut at all (or do at your own peril).
- Line 5 I am creating the entry point to my glowbuttons plugin and adding it to the $.fn object
- If you notice in line 5, my plugin accepts a single parameter called options. I am using lines 19 through 24 to define the options my plugin supports as well as the default values for each of the properties. Line 8 examines the options my plugin is provided along with the default option values and fills in any missing options with the defaults. If no options are supplied all of the default values will be used.
- Line 10 is where we actually start doing something. Here we iterate over each of the elements in the wrapped set and do something with them. Two things are important here. The first is that I am returning this which is a reference to the wrapped set. This allows users to chain calls together creating the nice compact, fluent interface that jQuery is know for. And the second is that where I have the comment ‘do something with $(this)’ is where the actual plug-in logic goes. We will see that soon.
(function($) {
// add our glowbuttons function. it accepts
// a single parameter that specifies any parameters
// our plugin supports
$.fn.glowbuttons = function(options) {
// build main options before element iteration
var opts = $.extend({}, $.fn.glowbuttons.defaults, options);
return this.each(function() {
// do something with $(this).
// this is where all of the core plug in code belongs
});
};
// default options - these are used of none others are specified
$.fn.glowbuttons.defaults = {
from: '#016bbd', '#b1ddff',
className: 'blue',
speed: 1000
};
// invoke the function we just created passing it
// the jQuery object
})(jQuery);
Adding the GlowButton Plugin Logic
So after creating the template, I went back to my existing GlowButtonsExtender logic and started moving it over into my plugin. My existing plugin did basically 3 things
- Injected 2 SPAN’s surrounding the button that I am using for styling (border and background)
- Injected some browser-specific style workarounds
- Setup 2 Toolkit animations for animating the background color as the mouse enters and leaves the button
And adding this logic into the plugin was really easy. Below I am just showing the part where I iterate over the wrapped set, but you can see that on line 4 I am wrapping the button in 2 spans using the wrap function. Then I navigate up to the immediate parent nodes and apply some browser hacks (I left them out of the code snippet here because they don’t add much value). And finally, I attach to the outer most SPAN’s hover events and run a simple color animation when the button is hovered over.
return this.each(function() {
var button = $(this);
// inject the parent nodes
button.wrap('');
// ** do some browser specific style workarounds
button.parent().each(function(){
var innerWrapper = $(this);
// ** do some browser specific style workarounds
innerWrapper.parent().each(function(){
// ** do some browser specific style workarounds
})
// add a class to the outer most node - this helps with theming
.addClass(o.className)
// finally attach to the hover events to run the animation
.hover(
function(){
$(this).stop();
$(this).animate({ backgroundColor: o.to }, o.speed);
},
function(){
$(this).stop();
$(this).animate({ backgroundColor: o.from }, o.speed);
}
);
});
});
And now I can do things like this …
$(document).ready(function(){
$('.glow').glowbuttons();
});
… and all of the INPUTs on my page with the glow CSS class will start glowing.
Overriding Options with the Metadata Plugin
And you could stop there if you want and have a pretty useful plugin. But, you can make your plugin even more flexible by adding support for the Metadata plugin. The Metadata plugin allows you to override the option values on a per element basis. So in the code snippet immediately above this I am applying the default options to all of the glow elements on the page. But that isn’t always what you want – and that is where the Metadata plugin becomes useful. With this plugin users can override the default options by specifying option values using the class attribute as follows …
And now we can still fire the same logic on document ready, but now because we are specifying different parameter values on a per element basis, we end up rendering the following buttons.
And best of all, to support this all we have to do us update our plugin and add a single line of code. In the snippet below I updated the template to check first check the current element (i.e. $(this)) to see if it has any metadata properties defined. If it does we will honor these values. And if the metadata plugin isn’t available, we just fall back and use what ever options are available.
(function($) {
// add our glowbuttons function. it accepts
// a single parameter that specifies any parameters
// our plugin supports
$.fn.glowbuttons = function(options) {
// build main options before element iteration
var opts = $.extend({}, $.fn.glowbuttons.defaults, options);
return this.each(function() {
// if the metadata plug-in is installed, use it to build the options
var o = $.metadata ? $.extend({}, opts, $(this).metadata()) : opts;
// ... plugin logic
});
};
// default options - these are used of none others are specified
$.fn.glowbuttons.defaults = {
from: '#016bbd',
'#b1ddff',
className: 'blue',
speed: 1000
};
// invoke the function we just created passing it
// the jQuery object
})(jQuery);
And finally, here is the complete source code for the plugin.
(function($) {
$.fn.glowbuttons = function(options) {
// build main options before element iteration
var opts = $.extend({}, $.fn.glowbuttons.defaults, options);
return this.each(function() {
var button = $(this);
// if the metadata plug-in is installed, use it to build the options
var o = $.metadata ? $.extend({}, opts, button.metadata()) : opts;
// inject the parent nodes
button.wrap('');
// ie display workaround
button.css('display', $.browser.msie ? 'inline-block' : 'block');
button.parent().each(function(){
var innerWrapper = $(this);
// more browser specific workarounds
innerWrapper.css('display', $.browser.msie ? 'inline-block' : 'block');
if($.browser.msie) {
innerWrapper.css({ 'position':'relative', 'left':'-1px' });
}
innerWrapper.parent().each(function(){
var outerWrapper = $(this);
outerWrapper.css('display', $.browser.mozilla ? '-moz-inline-box' : 'inline-block');
outerWrapper.css('backgroundColor', o.from);
// our glossy image is a transparent PNG so
// we have a special class that uses an image filter
if($.browser.msie && $.browser.version < 7) {
outerWrapper.addClass('ie6');
}
})
// add a class to the outer most node - this helps with theming
.addClass(o.className)
// finally attach to the hover events to run the animation
.hover(
function(){
$(this).stop();
$(this).animate({ backgroundColor: o.to }, o.speed);
},
function(){
$(this).stop();
$(this).animate({ backgroundColor: o.from }, o.speed);
}
);
});
});
};
$.fn.glowbuttons.defaults = {
from: '#016bbd',
'#b1ddff',
className: 'blue',
speed: 1000
};
})(jQuery);
What am I doing to learn more about jQuery?
So far I have found jQuery pretty easy to learn. But I have also only just started so I feel like I have a lot of catching up to do. Here is what I am doing to learn more about jQuery ...
- Take Scott Hanselman's advice and become a better dev by reading more code. The existing jQuery plugins are pretty readable. I think once you get past the seemingly goofy JavaScript syntax for controlling scoping the rest is pretty straight forward. Browse the plugins and see how they work. I think you might be surprised at how simple they are (I know I was)
- Subscribe to jQuery's discussion board over on Google Groups.
- Pick up a JavaScript reference book
- Check out learningjquery.com it has some great information. Plus I was over there today and stumbled onto a comment that Martin Fowler made (obviously I don't know that it was him for sure, but you never know).
- Both Dave Ward and Rick Strahl blog fairly regularly on using jQuery with ASP.NET. So if you haven't yet, subscribe to their feeds. (here and here)
That;s it. Enjoy!