How to Structure a Custom jQuery Plugin
Structuring a custom jQuery plugin requires a pattern that prevents
namespace conflicts, supports chainability, and allows for customizable
configuration. By wrapping your logic inside an Immediately Invoked
Function Expression (IIFE) and extending jQuery’s prototype
($.fn), you create a clean, reusable component that
integrates seamlessly into any jQuery-powered application.
The Standard Plugin Architecture
The most widely accepted structure for a jQuery plugin uses four
primary components: an IIFE for scope isolation, a definition on
jQuery.fn, an options object merged with user settings via
$.extend(), and a return this.each() loop to
preserve chainability.
Here is the complete, production-ready boilerplate:
(function ($) {
'use strict';
// 1. Define default options
const defaults = {
property: 'value',
onComplete: null
};
// 2. Define the plugin on jQuery.fn
$.fn.myCustomPlugin = function (options) {
// Merge defaults with user-provided options
const settings = $.extend({}, defaults, options);
// 3. Maintain chainability by returning this.each()
return this.each(function () {
const $element = $(this);
// Avoid duplicate instantiations using jQuery data
if ($element.data('myCustomPlugin')) {
return;
}
// Private function specific to this instance
function init() {
// Plugin logic here
$element.addClass('initialized');
// Trigger a callback if provided
if (typeof settings.onComplete === 'function') {
settings.onComplete.call($element);
}
}
// Save plugin instance or state to the element
$element.data('myCustomPlugin', {
settings: settings
});
init();
});
};
})(jQuery);Key Structural Elements
1. The IIFE and Scoping
Wrapping the plugin in (function ($) { ... })(jQuery);
ensures that the $ alias refers strictly to
jQuery inside the plugin scope. This eliminates conflicts
with other libraries that might assign $ to a different
object (such as Prototype or MooTools).
2. Default Options and
$.extend()
By defining a private defaults object outside the
$.fn method, memory is conserved because the defaults are
not recreated on every invocation. The
$.extend({}, defaults, options) method creates a shallow
copy, combining default values with any overrides supplied by the
developer without mutating the original defaults object.
For nested configuration objects, use
$.extend(true, {}, defaults, options) for a deep copy.
3. Chainability via
this.each()
jQuery functions are expected to be chainable (e.g.,
$('selector').myPlugin().addClass('active')). To support
this, return this.each(...). Inside the initial
$.fn method, this refers to the jQuery
collection itself. Inside the .each() callback,
this refers to the individual raw DOM element, which can be
wrapped as $(this).
4. Preventing Multiple Initializations
Attaching the plugin instance or state to the DOM element via
$.data() (or $element.data()) prevents
accidental re-initialization. If the plugin is called on the same
element multiple times, checking
$element.data('pluginName') allows you to either return
early or direct subsequent calls to public plugin methods.