jQuery UI

jquery

http://jqueryui.com/home

Other jQuery UI Libraries

http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/
http://bililite.com/blog/extending-jquery-ui-widgets/
http://bililite.com/blog/2008/08/05/namespaces-in-jquery/
http://css-tricks.com/jquery-ui-position-function/ - jQuery UI’s Position Function (plus, MagicZoom from scratch)
http://www.erichynds.com/jquery/jquery-create-event/
http://www.erichynds.com/jquery/tips-for-developing-jquery-ui-widgets/
http://benalman.com/news/2010/03/jquery-special-events/

The pattern for using the widget factory is simple. 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.

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.

Next we can create our initialisation function _create. 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 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.

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; }

Coding your First jQuery UI Plugin

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License