Coding your First jQuery UI Plugin

Coding your First jQuery UI Plugin

jQuery contains the fn.extend() method, which makes authoring jQuery plugins quite easy, allowing us to write code that is used in exactly the same way as other jQuery methods. jQuery UI also contains structures that make authoring custom jQuery UI plugins easy. So that’s what we’ll be looking at over the course of this tutorial. The methods used differ from that of standard jQuery plugins, and there are stricter conventions that should be followed, which is why I feel the topic is deserving of an article.


Over the course of this tutorial, I’ll show you the coding conventions and general guidelines that should be adhered to when authoring plugins for jQuery UI. We’ll be creating a simple plugin that just adds captions to images on the page. It’s purposely simple so that we can focus on what is needed to make a jQuery UI plugin without getting lost in the code. Anyone that’s written a jQuery plugin should have no problems. Knowledge of jQuery UI may help but shouldn’t be essential to complete this tutorial. Let’s get started.

Getting Started

We’ll need a copy of jQuery as well as a couple of files from jQuery UI, but it needs to be jQuery UI 1.8 (this can be found on the blog). Create a working directory somewhere on your machine called jqueryui-plugin, then inside this create a css folder, a js folder and an img folder (the images used in this tutorial can be found in the code download).

Download the library and unpack it somewhere accessible. We only need a few files from the archive, namely the jQuery source file which is in the root of the archive as jquery-1.4.1.js, and the jquery.ui.core.js and jquery.ui.widget.js files, which are both in the ui folder. Grab these and put them into the js folder in your working directory. We’ll be making light use of the CSS framework as well, so we’ll need one of the theme style sheets available with the current stable version of jQuery UI (I used ui-lightness in this example).

We’ll be making a captionator widget, so we’ll also need a page, with a bunch of images on it, to develop/test the plugin with. This example uses the following page:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<title>jQuery UI Captionator</title>
		<link rel="stylesheet" type="text/css" href="css/ui-lightness/jquery-ui-1.7.2.custom.css">
		<link rel="stylesheet" type="text/css" href="css/ui.captionator.css">
	</head>
	<body>
		<img src="img/1.jpg" alt="Royal Air Force Eurofighter Typhoon">
		<img src="img/2.jpg" alt="A British military GR-9 Harrier">
		<img src="img/3.jpg" alt="Two RAF Tornado GR-4s pull away from a KC-135 Stratotanker after refueling">
		<script type="text/javascript" src="js/jquery.js"></script>
		<script type="text/javascript" src="js/jquery.ui.core.js"></script>
		<script type="text/javascript" src="js/jquery.ui.widget.js"></script>
		<script type="text/javascript" src="js/jquery.ui.captionator.js"></script>
	</body>
</html>

We’ll keep things pretty simple for now; we’ve just three images on the page, followed by four script files; three link to the jQuery and jQuery UI source files, the fourth to our plugin’s source file which we’ll create shortly. The jquery.ui.core.js file is required by all jQuery UI widgets/plugins. The jquery.ui.widget.js file is the widget factory and allows for the creation of consistent widgets that share common API functionality. Most library components require this, and we’ll be using it to create our plugin.

Creating the Plugin File

Create a new JavaScript file and save it as jquery.ui.captionator.js in the js folder; we should keep to jQuery UI’s naming convention, which has just been updated in the 1.8 version of the library, and use jquery.ui.plugin_name.js. Add the following code to the new file:

(function($) {

})(jQuery);

All of the code that makes up our plugin should be encapsulated within a self-executing anonymous function. The jQuery object is passed into this function and is used inside the function via the $ alias; this is to ensure that the plugin is compatible with jQuery’s noConflict() method. This is a specified requirement and should always be adhered to.

Next we need to define the plugin; add the following code to our anonymous function:

$.widget("ui.captionator", {

});

The pattern for using the widget factory is simple to use, we just call the widget() method specifying the name of the plugin as the first argument, and an object literal containing the properties and methods that make the plugin function. This allows our plugin to be called (and created) using the commen jQuery syntax $(“element_caption_applied_to”).captionator(); like any other jQuery or jQuery UI method.

The widget factory provides a number of these properties and methods for us; for example, we can set the default options for the plugin using the options property, and add an initialisation function that is executed automatically by the widget factory as soon as an instance of the plugin is invoked. Within the object that appears as the second argument in the previous code add the following code:

options: {
  location: "bottom",
  color: "#fff",
  backgroundColor: "#000"
},

These are the only options we’ll use in our example plugin; users (and by users I mean implementers, not end users) of the plugin can specify the position of the caption to be either at the top of the image it is called on, or the bottom, they can specify the color of the text on the caption, or change the background-color of the caption. To change a configurable option of any jQuery UI widget prior to initialisation the implementing developer would just use something like this:

$(“element_caption_applied_to”).captionator({ location: “top” });

Next we can create our initialisation function, after the options object add the following method:

_create: function() {

	var self = this,
		o = self.options,
		el = self.element,
		cap = $("<span></span>").text(el.attr("alt")).addClass("ui-widget ui-caption").css({
			backgroundColor: o.backgroundColor,
			color: o.color,
			width: el.width()
		}).insertAfter(el),
		capWidth = el.width() - parseInt(cap.css("paddingLeft")) - parseInt(cap.css("paddingRight")),
		capHeight = cap.outerHeight() - parseInt(cap.css("paddingTop")) + parseInt(cap.css("paddingBottom"));

	cap.css({
		width: capWidth,
		top: (o.location === "top") ? el.offset().top : el.offset().top + el.height() - capHeight,
		left: el.offset().left,
		display: "block"
	});

	$(window).resize(function(){
		cap.css({
			top: (o.location === "top") ? el.offset().top : el.offset().top + el.height() - capHeight,
			left: el.offset().left
		});
	});
},

The method name should begin with an underscore as jQuery UI prevents any plugin method that begins with an underscore from being called from outside of the plugin, so this stops it being accidentally called from the HTML page. Any method that we begin with an underscore will be protected in this way.

The majority of our initialization method is a series of variables; within our function the keyword this refers to an object passed into the method which represents the instance of the plugin. The first variable caches a reference to the current instance of the plugin; the _create method is called for each element that the plugin method is called on, which could be a single element or several.

We can access the default options of the plugin (which are overridden automatically if the implementer configures any of them) using the options property of the object; we cache this in the second variable. The element that the plugin method (captionator()) was called on, which in this example would be an image, can be accessed using the element property of the object. We store this in the third variable.

We use the fourth variable to store a reference to the new caption element, which is built from a simple <span>; the <span> has its innerText set to the alt attribute of the current image, and several class names are added to it; we give it the ui-widget class name so that it can pick up some default styling from the current jQuery UI theme. We also give it a custom class name so that we can add some of our own styling.

Next we need to set some CSS properties; we’ll be using a separate style sheet for some styles, but certain things, such as the color and background-color styles are controllable via configurable options, so we need to set these using the plugin. The width of the caption needs to match the width of the image that it overlays, so we also need to determine this and set it programmatically. Finally the new <span> is injected into the page directly after the target image.

Once the caption has been inserted, it needs to be sized and positioned; the only way it can be sized accurately is if it already exists in the DOM and has CSS rules applied to it, such as the font-size. This is why we append the caption to the page, and then determine its exact dimensions, which are then stored in the variables capWidth and capHeight.

Once the caption has been appended to the page (and only then) we can work set the correct width, height and position of each caption, which we set using the css() method once again. The captions are actually completely separate from the images; they are inserted directly after each image and then positioned to appear to overlay the images, after all, we can’t append the <span> as a child of the <img>.

This is fine, until the browser is resized, at which point the images move but the captions don’t because they are absolutely positioned. To remedy this, we’ve used a basic resize handler attached to the window which simply repositions each caption to the new position of its image. This event handler is the last thing in our initialization method.

Another method that our plugin should expose is the destroy() method which is common to all jQuery UI plugins. We must provide an implementation of this method in order to clean up after our plugin. For our example plugin, the method can be as simple as this:

destroy: function() {
	this.element.next().remove();

	$(window).unbind("resize");
},

All we need to do is remove the captions and unbind our window resize handler. This method can be called by an implementer using the plugin so we shouldn’t begin this method name with an underscore. To call this method, the implementer would use $(“element_caption_attached_to”).captionator(“destroy”); which is how any of our public methods would be called.

We need to provide another method controlled/executed by the widget factory; we saw earlier how a developer could change a configurable option prior to initialisation, but what about after initialisation? This is done using the option method using the following syntax: $(“element_caption_attached_to”).captionator(“option”, “location”, “top”); so we need to add the built-in method _setOption to handle this:

_setOption: function(option, value) {
	$.Widget.prototype._setOption.apply( this, arguments );

	var el = this.element,
		cap = el.next(),
		capHeight = cap.outerHeight() - parseInt(cap.css("paddingTop")) + parseInt(cap.css("paddingBottom"));

	switch (option) {
		case "location":
			(value === "top") ? cap.css("top", el.offset().top) : cap.css("top", el.offset().top + el.height() - capHeight);
			break;
		case "color":
			el.next().css("color", value);
			break;
		case "backgroundColor":
			el.next().css("backgroundColor", value);
			break;
	}
}

We start this method with an underscore because the implementer uses option, not _setOption to actually change the options; we don’t need to worry about how this is handled, we just need to provide this method to deal with anything specific to our plugin. Because this method already exists in the widget factory we should call the original method, which we do first of all in our method using the prototype of the Widget object, specifying the method name (_setOption in this case but we could do it for other built-in methods as well) and use apply to call it. We can then proceed with the code specific to our plugin.

The function will automatically receive two arguments which are the option to change and the new value. We cache some commonly used elements, such as the image and the caption, and obtain the current height of each caption. We then use a simple switch-case statement to handle each of our three options being changed. Repositioning the captions is the most complex, but is still quite trivial and similar to how we positioned them initially.

Adding Events

It’s important to add events that developers using your plugin can add callbacks for so that they can react to different things happening when users interact with the widget in some way. The widget factory handles most of this task for us, all we need to do is trigger the event. This plugin doesn’t really do much, but we could still trigger an event after each caption is added to the page; to do this add the following code directly before the resize event handler:

self._trigger("added", null, cap);

That’s all we need to do! A single line of code and we have a custom event that can be reacted to. We call the _trigger() method of the plugin instance (which we stored in the variable self) and pass the method three arguments; the first is the name of the event, the second is for the event object (we don’t need to use this in our example plugin, hence the null value) and the third is a reference to the caption element. The widget factory will automatically pass the event object (if supplied) and the data we pass in the third parameter to a callback function that uses the added event. A developer could hook into this event using the following syntax: $(“element_caption_attached_to”).captionator({ added: function(e, ui){ //do stuff });

Styling the Plugin

We only need a very tiny style sheet for our plugin, literally we have just three styles. It’s almost not even worth creating a separate file for the styles! But we will, so create a new file called ui.captionator.css, which is the required format for plugin style sheets, and save it in the css directory. Add the following styles to it:

.ui-caption { display:none; position:absolute; padding:10px; }

That’s all there is to it. Our plugin is now functionally and visually complete. The captions should appear like this:

Final Product

Summary

Like jQuery’s plugin creation method fn.extend(), jQuery UI also has its own mechanism that allows developers to quickly and easily write robust and scalable plugins that meet the jQuery UI projects high standards, although in terms of what it actually does for us, it’s even better that jQuery. The widget factory has been created in such a way that pretty much all of the hard work is taken out of custom plugin creation.

It’s easy to work with the methods provided by the widget factory to add methods to our plugins that are common across UI widgets, such as the destroy and option methods, which implementing developers would expect to find in any plugin. We also saw just how easy it is to trigger custom events that developers can use to react to interactions or occurrences with the widget.

Add Comment

Discussion 44 Comments

  1. ShadowAssassin says:

    Thank you very much for this!

  2. Purple says:

    Thank you!

    Is it just me or does anyone else feel like nettuts is telepathically connected to your brain and publishes tutorials on exact subjects that you thought about a day or two ago?

    I love nettuts+!

  3. Peace says:

    War sucks…

  4. Avinash says:

    Another nice tut..

    Thanks !

  5. Jenna Molby says:

    Great tutorial, I’m going to have to try this.

  6. Wow

    That’s great !

    Thanks

  7. Fuat Akgün says:

    It seems wonderful in Firefox but in Chrome it isn’t rendered as in Firefox.

    Thanks for this good tutorial.

  8. Yoosuf says:

    @DAN its a nice job, this was the part you missed in jQuery UI 1.7, keep up good working, BTW expecting a book in jQuery UI 1.8

  9. Yoosuf says:

    @DAN its a nice job, this was the part you missed in jQuery UI 1.7, keep up good working, BTW expecting a book in jQuery UI 1.8

    any advance stuff than this?

  10. Yoosuf says:

    @DAN its a nice job, this was the part you missed in jQuery UI 1.7, keep up good working, BTW expecting a book in jQuery UI 1.8

    any advance stuff than this

  11. Natrium says:

    What’s the difference between a jQuery-plugin and a jQueryUI-plugin?

    • Dan Wellman says:
      Author

      Mostly the way that it’s structured, but also the methods that should be added (destroy, _setOption, etc) and the built-in methods that are available (_trigger, etc)…

  12. Very nice starting point to get me going in the plugin world :)

  13. Davidmoreen says:

    Jquery ui is the best. All of the assets that save you time when writing is the best part. Good tutorial, pretty easy to follow along.

  14. boris badenov says:

    Very nice tutorial. Jquery rules.

    Can the caption be made to be semi-transparent?

    • Dan Wellman says:
      Author

      I’m sure it can, rgba for example could easily be applied in certain browsers, but it meant resolving cross-browser issues with this that I didn’t want to get into for the purpose of this tutorial :)

  15. Agreed, that is a great tut to understand and implement. Thanks for your hard work.

  16. JimmyJames says:

    I get different results in Firefox, Chrome and IE8. Plus I don’t really think this tut shows off the jQuery UI at all. This can easily be done with just jQuery alone.

    • Dennis says:

      The main purpose of this tutorial is not to give you the code of a widget to use, but to show you the fundamentals of creating your own widget. Additionally, there are very useful things mentioned here and I think we have to appreciate this!

    • Dan Wellman says:
      Author

      Dennis is right (thanks!), the actual captions are just something vaguely interesting highlight the functionality of the widget factory and how plugins should be structured, etc. The example also had to be basic enough to not requie a ton of code, and something not already started/planned for the library…

  17. Dennis says:

    Thank you very much, this is a very useful tutorial :)

  18. bobbytuck says:

    Doesn’t work in Chrome.

  19. delta9 says:

    Thanks A lot for your paper. there is a lack of jQuery Ui Tut …

    You called your function _create, but in the jQuery.Ui Dev Guidelines, they speak about a _init function ? Does’nt matter ? Is it the same ?

  20. Athome says:

    Thanks a lot for this great tuts. There is a lack of jQueryUi how-to …

    You called your main function _create, but in the jQueryUi Dev Guidelines, they speak about an “_init” “constructor”. Doesn’t matter ?Is it new in jQueryUi 1.8 ?

    • Dan Wellman says:
      Author

      While both _create and _init are both fired automatically by the widget on page load, the source files for jQuery UI 1.8 all use _create so I thought it best to show the newest method in case there are plans to deprecate _init at some future point

  21. Roman says:

    In _setOption() function:
    el.next().css(“color”, value);
    Why not cap.css(“color”m value)?
    Why at all el.next()??? Put second plugin on the same DOM element that assume el.next() is it’s own generated element and have a nice day debugging this stuff.
    Much better solution is to remember cap inside element’s data (element.data(‘cap’, cal)) or put it in private member variable. Then write `var cap = element.data(‘cap’); cap.css(…);…`.
    Sorry for critics, in general nice tutorial.

    • Dan Wellman says:
      Author

      el is the element that the widget method (captionator()) is called on, this could be a single image or a collection of images but the way the _create() method works, which will always be executed *before* _setOption(), is to insert a single caption directly after the image. So the caption will always be the next element. There isn’t a second plugin on the same DOM element. el.next() is stored in a ‘private’ variable within the _setOption method.

      Using jQuery’s data() method to store information about the associated caption would be a good idea, but as already mentioned, the actual captions themselves aren’t really the focus of the tutorial. It’s more about creating a jQuery UI plugin in general.

      Critics is good, thanks for reading :D

  22. Keegan Watkins says:

    This is a very concise, well-written tutorial! I have a correction though:

    >> The method name should begin with an underscore as jQuery UI prevents any plugin method that begins with an underscore from being called from outside of the plugin, so this stops it being accidentally called from the HTML page. Any method that we begin with an underscore will be protected in this way.

    This isn’t really true. It IS true that when you attempt to call a private method of the widget using the plugin function, it will prevent calls to methods that are prefixed with an underscore.

    But, it is also important to note that the entire instance of the widget is stored using the data() method. This means that while you won’t be able to do:

    // Won’t work, as noted in the tut
    $(“#foo”).someWidget(“_somePrivateFn”)

    you can certainly still access those methods, using data() instead, for example:

    // Will work, as data() returns the widget instance
    $(“#foo”).data(“someWidget”)._somePrivateFn();

    This is one of my biggest complaints about the way jQuery UI structures it’s code (not to mention the use of passing-strings-as-method-names, but that’s a topic for another day). It is important for people to know not only how they *should* use the API, but also how those rules can be broken, either intentionally or otherwise.

    Nice work!

    • Dan Wellman says:
      Author

      Thanks for reading Keegan, and thanks for the info regarding accessing the ‘private’ (protected) methods – it shouldn’t crop up too often but nice to know how to do it when required :)

  23. Bob says:

    This was very helpful! Thank you for taking the time!

  24. Matthijs says:

    In the _create method, why do you store ‘this’ in a variable ‘self’? You also store the self.options and self.element properties in seperate variables. Is this just because you prefer to access those properties through those variables or is there some functional reason (You mention something about caching)?

    Thanks.

  25. B7 says:

    Great demo. It’s easy to understand. And awesome pictures!!!

  26. Ben says:

    $(“element_caption_applied_to”).captionator();

    Looking at the line above, is ‘captionator()’ not a plugin ( not a method, as it was named) ?

  27. jberryman says:

    I have to use jQuery UI 1.7.2 for now. What sorts of problems will I run into trying to follow this tutorial in 1.7.2 instead of 1.8?

  28. jberryman says:

    To answer my own question: it seems a lot of things are different, e.g. “_init” instead of “_create” and options are handled differently. Can’t find any docs, so I’m looking at some of the plugins from the 1.7.3 bundle; the simplest widget is `ui.progressbar.js`

  29. Griish Kumar Ms says:

    Great Tut… its helps to create an jquery plugin
    Thanks

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.