Authoring a Plugin ($.fn)
Extend jQuery with your own reusable, chainable method by adding to $.fn.
Every jQuery method you already love — .hide(), .addClass(), .on() — lives on $.fn, the prototype shared by every jQuery object. When you want a piece of behaviour you can reuse across a project, you write your own method the exact same way the library does.
The three habits of a well-behaved plugin
- Return
thisso callers can keep chaining. - Iterate the set with
this.each()— a selector can match many elements. - Wrap in an IIFE that receives
jQueryas$, so your code is safe even onnoConflictpages.
Here is the smallest plugin worth writing — it highlights each matched element:
(function ($) {
$.fn.highlight = function (color) {
return this.each(function () {
$(this).css("background", color || "#ffe58a");
});
};
})(jQuery);
$("p").highlight(); // works on every <p>
$("h1").highlight("#5BA3D0").addClass("done"); // still chainableAccepting options the jQuery way
Real plugins take an options object and merge it over sensible defaults with $.extend, so callers override only what they care about:
$.fn.badge = function (opts) {
var o = $.extend({ text: "NEW", bg: "#0769AD" }, opts);
return this.each(function () {
$(this).append($("<span>").text(o.text).css("background", o.bg));
});
};That $.extend({}, defaults, opts) pattern is the beating heart of nearly every jQuery plugin ever shipped.
Example
When to use it
- A UI library ships a .tooltip() plugin so developers can activate tooltips with a single $('.tip').tooltip() call.
- A form team packages shared validation as a $.fn.validate plugin, keeping reuse DRY across multiple page scripts.
- A charting toolkit adds a $.fn.sparkline plugin so any inline <span> can render a mini chart without extra markup.
More examples
Minimal chainable plugin
Returning this from the plugin function preserves the jQuery object so callers can continue chaining methods.
(function ($) {
$.fn.highlight = function (color) {
color = color || "yellow";
return this.css("background", color);
};
}(jQuery));
$(function () {
$("p.note").highlight("#b3f5b3").addClass("highlighted");
});Plugin with default options
$.extend() merges caller-supplied options over the defaults so all options are optional with sensible fallbacks.
(function ($) {
$.fn.badge = function (options) {
var opts = $.extend({ text: "New", color: "#e74c3c" }, options);
return this.each(function () {
var $badge = $("<span>")
.text(opts.text)
.css({ background: opts.color, color: "#fff",
padding: "2px 6px", borderRadius: "10px" });
$(this).append($badge);
});
};
}(jQuery));
$(function () {
$(".item").badge({ text: "Hot", color: "#e67e22" });
});Plugin with per-element state via data()
Storing state in .data() keeps each element's toggle state independent when the plugin is applied to many elements.
(function ($) {
$.fn.toggler = function () {
return this.each(function () {
var $el = $(this);
var open = $el.data("toggler-open") || false;
$el.on("click.toggler", function () {
open = !open;
$el.data("toggler-open", open)
.text(open ? "Close" : "Open");
});
});
};
}(jQuery));
$(function () { $(".panel-toggle").toggler(); });
Discussion