Update: Jamie Talbot has written an excellent article that improves this method and is definitely worth a read.
So you've been using jQuery as
your Javascript framework and now you need to write a plugin. If you
come from an Object-Oriented background like me, you may feel that
jQuery's plugins leave a lot to be desired.
The basic formula to create a jQuery plugin is to extend the plugin namespace with a single method:
3 |
jQuery.fn.myplugin = function () |
While that seems all fine and dandy for simple plugins, you may need to
create more robust plugins that do many things, often in a non-linear
fashion.
Some plugins get around this by adding tons of methods to jQuery's plugin namespace.
2 |
$( '#test' ).pluginAdd( 'stuff' ); |
3 |
$( '#test' ).pluginRemove( 'other stuff' ); |
4 |
$( '#test' ).pluginDoSomethingCool(); |
I personally don't like that approach because it pollutes the jQuery
plugin namespace with lots of methods. I personally like to stick to
just one plugin method per plugin.
Other plugins use the first parameter of the plugin to call methods:
2 |
$( '#test' ).plugin( 'add' , 'stuff' ); |
3 |
$( '#test' ).plugin( 'remove' , 'other stuff' ); |
4 |
$( '#test' ).plugin( 'doSomethingCool' ); |
I think this approach is a little awkward, especially if the plugin
accepts an options object the first time it is created. This approachs
means you would have to either write a switch of all the methods you
want to expose, or blindly accept any string as a method name.
To get around these hurdles, I've created a basic template for jQuery
plugins that provides access to an Object-Oriented interface if needed
while still maintaining jQuery's simplicity of a single method in the
plugin namespace.
The first thing you need to do is wrap all your plugin code in an
anonymous function. This will help keep things nice and tidy without
creating global variables.
Next, create your plugin as a class, where the first parameter is a single DOM element.
04 |
var MyPlugin = function (element) |
06 |
var elem = $(element); |
10 |
this .publicMethod = function () |
12 |
console.log( 'publicMethod() called!' ); |
To make your new object-oriented class available as a jQuery plugin, write a simple wrapper function in the plugin namespace:
04 |
var MyPlugin = function (element) |
06 |
var elem = $(element); |
10 |
this .publicMethod = function () |
12 |
console.log( 'publicMethod() called!' ); |
16 |
$.fn.myplugin = function () |
18 |
return this .each( function () |
20 |
var myplugin = new MyPlugin( this ); |
Now, when you call $(element).myplugin(), the jQuery plugin
instantiates an instance of MyPlugin, passing the element as the first
argument.
But now there's a problem of how to get the object "myplugin" once it's
been created. For this, I usually store the object in the elements
data. This provides easy access to the object while allowing you to
prevent accidental double instantiation in the event that the plugin was
called again on the same element.
04 |
var MyPlugin = function (element) |
06 |
var elem = $(element); |
10 |
this .publicMethod = function () |
12 |
console.log( 'publicMethod() called!' ); |
16 |
$.fn.myplugin = function () |
18 |
return this .each( function () |
20 |
var element = $( this ); |
23 |
if (element.data( 'myplugin' )) return ; |
25 |
var myplugin = new MyPlugin( this ); |
28 |
element.data( 'myplugin' , myplugin); |
Now you have easy access to the object should you need to run methods on it.
2 |
var myplugin = $( '#test' ).data( 'myplugin' ); |
3 |
myplugin.publicMethod(); |
If you need to get fancy and add options parameter or other required
parameters, just pass them from the jQuery plugin to your plugin's
constructor:
04 |
var MyPlugin = function (element, options) |
06 |
var elem = $(element); |
10 |
var settings = $.extend({ |
15 |
this .publicMethod = function () |
17 |
console.log( 'publicMethod() called!' ); |
21 |
$.fn.myplugin = function (options) |
23 |
return this .each( function () |
25 |
var element = $( this ); |
28 |
if (element.data( 'myplugin' )) return ; |
31 |
var myplugin = new MyPlugin( this , options); |
34 |
element.data( 'myplugin' , myplugin); |
You may also want to expose some of your object's methods while keeping
others private. To make a private method, create a local function
within your object using the var
keyword:
04 |
var MyPlugin = function (element, options) |
06 |
var elem = $(element); |
08 |
var settings = $.extend({ |
13 |
this .publicMethod = function () |
15 |
console.log( 'public method called!' ); |
19 |
var privateMethod = function () |
21 |
console.log( 'private method called!' ); |
25 |
$.fn.myplugin = function (options) |
27 |
return this .each( function () |
29 |
var element = $( this ); |
32 |
if (element.data( 'myplugin' )) return ; |
35 |
var myplugin = new MyPlugin( this , options); |
38 |
element.data( 'myplugin' , myplugin); |
To see an example of a plugin I wrote that uses this template, check out my Tagger plugin.