This post is the continuation of the series Things I learned creating a jQuery Plugin.
In the first part we have seen how the structure of a jQuery plugin must be, the plugin entry point (so called wrapper function) and how we can control the behavior of a method as a getter or setter.
Your plugin more probably will accept different set of options to allow some configuration. For these reason it is important to define a set of default options which will be applied in cases where no options are specified by the user. Place it within the jQuery wrapper function is a good practice:
// // Default options // $.fn[pluginName].defaults = { opt_A: "" };
A good practice is to encapsulate the logic of our plugin within a function, this way our plugin's entry point function can easily initialize or call the right method.
For example, in a really simple wrapper function, that simply initializes a plugin's instance on each selected element, we could write something like:
$.fn[pluginName] = function(options) { return this.each(function() { new Plugin(this, options); }); }
The main part of your plugin is the constructor function. Usually this function is responsible to initialize the plugin, store a reference to the selected element or merge the passed options with the default ones:
function Plugin(element, options) { // Store references to the selected element this.el = element; this.$el = $(element);// Merge passes options with defaults this.options = $.extend({}, $.fn[pluginName].defaults, options); // ...other code here... // Initialize the plugin instance this.init();
}
Once the Plugin
function is defined we can modify its prototype adding all the desired methods we want for our plugin.
There are a couple of methods are a good practice to implement:
init
method, which initializes each plugins instance: creating new DOM elements, registering listeners, etcdestroy
method, responsible to free any resource used by the plugin: extra elements, unregister listeners, etc.Other methods can be created within your plugin's prototype but remember the convention: Use method names starting with underscore for those methods we want to be private.
If you remember the first part of this series, what really happens is when you call a plugin's method, the wrapper function of our plugin checks if the method's name starts with underscore and if so then avoids the call.
// // Plugin prototype // Plugin.prototype = {// // Initialize the plugin instance // init: function() { ... }, // // Free resources // destroy: function() { ... }, // // Public method // publicMethod: function() { ... }, // // Private method (it starts with an underscore) // _privateMethod: function() { ... }
}
destroy
methodAs we have commented, the destroy
method must free any resource used by the plugin instance, like extra created elements, unregister listeners, etc
If you remember the first article, you will notice that the plugin's instance is stored within the selected DOM element where the plugin is applied:
$.fn[pluginName] = function(options) { var args = arguments;if (options === undefined || typeof options === 'object') { // Creates a new plugin instance, for each selected element, and // stores a reference withint the element's data return this.each(function() { if (!$.data(this, 'plugin_' + pluginName)) { $.data(this, 'plugin_' + pluginName, new Plugin(this, options)); } }); } ...
};
That occurs in the line:
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
So, the last action in your destroy
method must be always remove the plugin's instance reference from the element's data. This can easily done using the reference to the DOM element stored in the plugin instance:
// // Free resources // destroy: function() {// Remove elements, unregister listerners, etc // Remove data this.$el.removeData();
}
It is common jQuery plugins allows to register callback functions to be called when an event or action is generated by the plugins. For example, in the tagger plugin the user can be notified when a new tag is added, removed, clicked, etc.
Next lines shows the initialization of the tagger plugin setting the parameter fieldSeparator
to a value different from the default options value and registering a callback function for the onTagAdded
event:
$('#inputID').tagger({ fieldSeparator: '|' onTagsAdded: function(tags) { console.log('Added new tag: '+tags+'\n'); } });
To achieve this we need to make to main steps:
Continuing with the sample of the tagger plugin, its default options looks like:
// // Default options // $.fn[pluginName].defaults = { fieldSeparator: ",", readOnly: false, // Callback invoked when user calls the 'tags' method onTagsAdded: function() { }, // Callback invoked when user calls the 'remove' method onTagsRemoved: function() { }, // Callback invoked when user calls the 'clear' method. // Note: Internally the 'clear' method uses the 'remove'. onClear: function() { }, // Callback invoked when the user click a tag label onClick: function() { } };
Later, in the method responsible to add new tags to the tag list, a call is made to the onTagsAdded
function:
// Adds one or more tags // ... // tags: function(tags) { ... // Call the callback this.options.onTagsAdded.call(this, tags); ... } },
Note how we have forced to set the
this
object and passed the value of the new added tag to the callback function.
Ok, this is the end. A short series of two articles to introduce the main concepts to creating jQuery plugins isn't a bad thing when you are looking for help starting with jQuery and custom plugin development.
Let's try to summarize the main points we have seen in this couple of posts:
$.fn
object and is responsible to (or can) control: plugin initialization, call to setter or getter methods, simulate private methods, etc.destroy
method responsible to free all the resources used by your pluginThe web is plenty of great information:
http://docs.jquery.com/Plugins/Authoring
http://www.websanova.com/tutorials/jquery/the-ultimate-guide-to-writing-jquery-plugins
https://github.com/zenorocha/jquery-plugin-patterns
http://coding.smashingmagazine.com/2011/10/11/essential-jquery-plugin-patterns/
http://addyosmani.com/resources/essentialjsdesignpatterns/book/#jquerypluginpatterns