A simple object-oriented template for creating jQuery plugins. It allows you to maintain a DRY workflow and minimizes the need to write boilerplate code. The template respects the official jQuery plugin authoring guide.
Before diving in you should consider picking an approriate name for your plugin.
Set the NAMESPACE
constant according your choice. It will be referenced
throughout the template making it really easy to update your plugin name
should you ever feel the need to do so.
The SELECTOR
constant is used as a part of the plugin data API.
/**
* Plugin NAMESPACE and SELECTOR
* @type {String}
* @api private
*/
var NAMESPACE = 'walkthrough',
SELECTOR = '[data-' + NAMESPACE + ']';
The constructor provides access to the DOM element and the options via the
instance. Both the options, extended with the defaults, and a jQuery version of
the DOM element are mapped to the plugin instance: this.options
and
this.$element
. The original text of the DOM element is also stored as an
instance property.
/**
* Plugin constructor
* @param {Node} element
* @param {Object} [options]
* @api public
*/
function Plugin (element, options) {
this.options = $.extend(true, $.fn[NAMESPACE].defaults, options);
this.$element = $(element);
this.originalText = this.$element.text();
}
All public facing methods and properties, including the plugin version, are
bound to the plugin prototype. Overwriting the prototype necessitates restoring
the constructor and prototype relationship: constructor: Plugin
.
/**
* Plugin prototype
* @type {Object}
* @api public
*/
Plugin.prototype = {
constructor: Plugin,
version: '1.0.0',
init: function () {
this.$element.html(this.options.text);
},
restore: function () {
this.$element.html(this.originalText);
}
};
The plugin definition acts as a wrapper around the constructor and is bound to
the jQuery object. This prevents against multiple plugin instantiations and
allows us to access the plugin functionality as a jQuery function. By default
the init
method is called. This can be easily overruled by passing a method,
e.g. restore
.
/**
* jQuery plugin definition
* @param {String} [method]
* @param {Object} [options]
* @return {Object}
* @api public
*/
$.fn[NAMESPACE] = function (method, options) {
return this.each(function () {
var $this = $(this),
data = $this.data('fn.' + NAMESPACE);
options = (typeof method === 'object') ? method : options;
if (!data) {
$this.data('fn.' + NAMESPACE, (data = new Plugin(this, options)));
}
data[(typeof method === 'string') ? method : 'init']();
});
};
The default plugin settings are exposed making it very easy for users to step in and override the defaults with minimal code.
/**
* jQuery plugin defaults
* @type {Object}
* @api public
*/
$.fn[NAMESPACE].defaults = {
text: 'Walkthrough'
};
Set data-walkthrough
on a DOM element to activate the plugin without writing
JavaScript. Event handlers are namespaced for reliable binding and unbinding.
/**
* jQuery plugin data api
* @api public
*/
$(document).on('click.' + NAMESPACE, SELECTOR, function (event) {
$(this)[NAMESPACE]();
event.preventDefault();
});
Putting our code inside a closure allows us to keep private variables private and have complete control over what's publicly exposed without polluting the global namespace. The function is invoked immediately (IFFE) leaving no reference whatsoever.
;(function(window, document, $, undefined) {
...
}(this, this.document, this.jQuery));
Activate the plugin by using the data API. Clicking the <div>
will trigger the
plugin and replace the element's text with 'Walktrough'.
<div data-walkthrough>API</div>
We could just as easily activate our plugin with JavaScript.
$('.api').walkthrough()
will render the same result as using our data API
would.
<div class="api">API</div>
<script>$('.api').walkthrough();</script>
Restoring the original text is as easy as passing the correct method:
$('.api').walkthrough('restore')
.