Learning JavaScript is a continuous endeavor of mine. Building a strong foundation starts with understanding the core concepts of the JavaScript language. As with learning anything, there comes a point where you are left with a fragmented understanding of the subject — you know enough to know just how much you don’t know. That is exactly where I am, and it’s a frustrating place in the learning cycle.

I now understand many different pieces of the JavaScript puzzle, but knowing those pieces and knowing how they fit together are two very different things. While there is no shortage of learning resources for JavaScript, it’s difficult to apply concepts in a holistic way by reading a book or watching a tutorial. You just have to build stuff.

With that in mind, I put together a boilerplate for jQuery plugins that expands on the fantastic jQuery plugin boilerplate by Zeno Rocha. In an effort to understand exactly what is happening in every line of a basic jQuery plugin, I loaded up my boilerplate with comments. It’s a bit more opinionated than others because it is based on providing solutions to the problems I most frequently encounter. Hope you enjoy! …And if you are a JavaScript ninja, please feel free to correct or expand on anything.

See the Pen Ardln by John Dugan (@johndugan) on CodePen.16066

// ---------------------------------
// ---------- Plugin Name ----------
// ---------------------------------
// Brief plugin description
// ------------------------

    The semi-colon before the function invocation is a safety net against
    concatenated scripts and/or other plugins which may not be closed properly.

    "undefined" is used because the undefined global variable in ECMAScript 3
    is mutable (ie. it can be changed by someone else). Because we don't pass a
    value to undefined when the anonymyous function is invoked, we ensure that
    undefined is truly undefined. Note, in ECMAScript 5 undefined can no
    longer be modified.

    "window" and "document" are passed as local variables rather than global.
    This (slightly) quickens the resolution process.
;(function ( $, window, document, undefined ) {
        Store the name of the plugin in the "pluginName" variable. This
        variable is used in the "Plugin" constructor below, as well as the
        plugin wrapper to construct the key for the "$.data" method.

        More: http://api.jquery.com/jquery.data/
    var pluginName = 'myPluginName';

        The "Plugin" constructor, builds a new instance of the plugin for the
        DOM node(s) that the plugin is called on. For example,
        "$('h1').pluginName();" creates a new instance of pluginName for
        all h1's.
    // Create the plugin constructor
    function Plugin ( element, options ) {
            Provide local access to the DOM node(s) that called the plugin,
            as well local access to the plugin name and default options.
        this.element = element;
        this._name = pluginName;
        this._defaults = $.fn.myPluginName.defaults;
            The "$.extend" method merges the contents of two or more objects,
            and stores the result in the first object. The first object is
            empty so that we don't alter the default options for future
            instances of the plugin.

            More: http://api.jquery.com/jquery.extend/
        this.options = $.extend( {}, this._defaults, options );

            The "init" method is the starting point for all plugin logic.
            Calling the init method here in the "Plugin" constructor function
            allows us to store all methods (including the init method) in the
            plugin's prototype. Storing methods required by the plugin in its
            prototype lowers the memory footprint, as each instance of the
            plugin does not need to duplicate all of the same methods. Rather,
            each instance can inherit the methods from the constructor
            function's prototype.

    // Avoid Plugin.prototype conflicts
    $.extend(Plugin.prototype, {

        // Initialization logic
        init: function () {
                Create additional methods below and call them via
                "this.myFunction(arg1, arg2)", ie: "this.buildCache();".

                Note, you can cccess the DOM node(s), plugin name, default
                plugin options and custom plugin options for a each instance
                of the plugin by using the variables "this.element",
                "this._name", "this._defaults" and "this.options" created in
                the "Plugin" constructor function (as shown in the buildCache
                method below).

        // Remove plugin instance completely
        destroy: function() {
                The destroy method unbinds all events for the specific instance
                of the plugin, then removes all plugin data that was stored in
                the plugin instance using jQuery's .removeData method.

                Since we store data for each instance of the plugin in its
                instantiating element using the $.data method (as explained
                in the plugin wrapper below), we can call methods directly on
                the instance outside of the plugin initalization, ie:

                Consequently, the destroy method can be called using:

        // Cache DOM nodes for performance
        buildCache: function () {
                Create variable(s) that can be accessed by other plugin
                functions. For example, "this.$element = $(this.element);"
                will cache a jQuery reference to the elementthat initialized
                the plugin. Cached variables can then be used in other methods. 
            this.$element = $(this.element);

        // Bind events that trigger methods
        bindEvents: function() {
            var plugin = this;
                Bind event(s) to handlers that trigger other functions, ie:
                "plugin.$element.on('click', function() {});". Note the use of
                the cached variable we created in the buildCache method.

                All events are namespaced, ie:
                ".on('click'+'.'+this._name', function() {});".
                This allows us to unbind plugin-specific events using the
                unbindEvents method below.
            plugin.$element.on('click'+'.'+plugin._name, function() {
                    Use the "call" method so that inside of the method being
                    called, ie: "someOtherFunction", the "this" keyword refers
                    to the plugin instance, not the event handler.

                    More: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

        // Unbind events that trigger methods
        unbindEvents: function() {
                Unbind all events in our plugin's namespace that are attached
                to "this.$element".

            "someOtherFunction" is an example of a custom method in your
            plugin. Each method should perform a specific task. For example,
            the buildCache method exists only to create variables for other
            methods to access. The bindEvents method exists only to bind events
            to event handlers that trigger other methods. Creating custom
            plugin methods this way is less confusing (separation of concerns)
            and makes your code easier to test.
        // Create custom methods
        someOtherFunction: function() {
            alert('I promise to do something cool!');

        callback: function() {
            // Cache onComplete option
            var onComplete = this.options.onComplete;

            if ( typeof onComplete === 'function' ) {
                    Use the "call" method so that inside of the onComplete
                    callback function the "this" keyword refers to the
                    specific DOM node that called the plugin.

                    More: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call


        Create a lightweight plugin wrapper around the "Plugin" constructor,
        preventing against multiple instantiations.

        More: http://learn.jquery.com/plugins/basic-plugin-creation/
    $.fn.myPluginName = function ( options ) {
        this.each(function() {
            if ( !$.data( this, "plugin_" + pluginName ) ) {
                    Use "$.data" to save each instance of the plugin in case
                    the user wants to modify it. Using "$.data" in this way
                    ensures the data is removed when the DOM element(s) are
                    removed via jQuery methods, as well as when the userleaves
                    the page. It's a smart way to prevent memory leaks.

                    More: http://api.jquery.com/jquery.data/
                $.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
            "return this;" returns the original jQuery object. This allows
            additional jQuery methods to be chained.
        return this;

        Attach the default plugin options directly to the plugin object. This
        allows users to override default plugin options globally, instead of
        passing the same option(s) every time the plugin is initialized.

        For example, the user could set the "property" value once for all
        instances of the plugin with
        "$.fn.pluginName.defaults.property = 'myValue';". Then, every time
        plugin is initialized, "property" will be set to "myValue".

        More: http://learn.jquery.com/plugins/advanced-plugin-concepts/
    $.fn.myPluginName.defaults = {
        property: 'value',
        onComplete: null

})( jQuery, window, document );
Posted by: John Dugan


  • Pingback: Demystifying Debounce in JavaScript()

  • Tom

    This is just great!

  • Thx Tom 🙂

  • Damaso

    what is the Plugin.prototype conflicts?

  • Are you referring to jQuery’s `.extend` method? If so, https://api.jquery.com/jquery.extend/

  • Damaso

    // Avoid Plugin.prototype conflicts

    $.extend(Plugin.prototype, {

    // Initialization logic
    init: function () {

    Why this would cause a conflict?

  • Assigning an object literal to `Plugin.prototype` wipes out all properties. Using `jQuery.extend()` preserves them.

  • Damaso

    I get it, thank you very much for the explanation 🙂

  • Happy to help 🙂

  • Hey John – How would you handle creating private functions? It appears as if all of these are public.

  • Hey Dan,

    That’s correct – this jQuery plugin boilerplate uses public functions. It’s based off of the “combination contstructor/prototype pattern”.

    If you want private functions in your jQuery plugin, I’d advise you to use the “durable constructor pattern” (link below). Let me know if this is a sufficient answer, or if you need further explanation.


  • So looks like you would just include your private functions inside of the constructor function itself?

  • lookfortheroot

    Hi, could you also tell how to pass other callback after “onclick” event, for example? Point in idea of the multiple callbacks, in other words. It seems I have to repeat “callback” function? But it looks not DRY way… Maybe I’m wrong 🙂

  • Hi There –

    I’m not exactly sure what you are after. Keep in mind, this is a boilerplate – it’s meant as a starting point for you to build off of.

    That said, if you are wanting to invoke multiple functions in the `onComplete` method, one way you could do so is to bind multiple functions to a custom event. Then you could simply trigger that event in the `onCompete` callback. See screenshot below.

  • this._defaults = $.fn.myPluginName.defaults;

    Should this not be this._defaults = $.fn.PluginName.defaults; ??
    As myPluginName isn’t referenced anywhere

  • Hi Dan,

    …Missed this followup question. Apologies. Yes, private functions and variables can be declared either inside the constructor function or in the immediate outer scope that is wrapped up/protected from the global scope by the immediately invoked function expression.

    You’ll notice in the boilerplate above that I don’t have any private variables or functions defined within the `Plugin` constructor. However, I have declared a `pluginName` variable in the outer scope which will be private. That variable can be accessed by any of the descendant scopes. Because it is not defined as a property on the `Plugin` constructor, it will not be accessible from the object returned when a new plugin is instantiated (thus making it private).

    I’m sure by now you’ve resolved the issue. Hopefully this will help other folks who have the same question :-).

  • Daniel Chavolla

    Good work. Thanks for taking the time. Very informative and easy to understand.

  • Luiz Filipe Machado Barni

    Hello man! It’s an awesome post. I also read a boilerplate explanation in: https://github.com/acanimal/jQuery-Plugin-Boilerplate/blob/master/jquery-plugin.js. I noticed that both codes don’t make the use of the “use strict” mode. Is there any reason to don’t use it?

  • Hey Luiz – thx much. Regarding strict mode, there is no particular reason to not use it. The code will work either way just fine.

    Keep in mind that strict mode is function scoped. So if you decide to use it, you’ll probably want to do so within the immediately invoked function expression. That way you won’t bump in to potential problems with other code.

  • Luiz Filipe Machado Barni

    Sure, I’ll remember it! Thx!

  • Oliver

    Hello, thanks for sharing this. At the moment I struggle to add a public method to this structure that I could call like

    $(‘#mypluginelement’).MyPlugin(‘myfunction’, ‘myargument’);

    Could you give me a hint how to add this?

    Thanks in advance.

  • Hey Oliver!

    jQuery’s `data` method allows you to gain access to a plugin instance. So, using your example above you could do something like `$(‘#mypluginelement’).data.myCustomMethod();`

    Let me know if that is enough of an explanation, or if you’d like more clarification. 🙂

  • @garzasays

    Looks like I am late to the party, but this was an awesome post. Thanks for the great read.

  • Patrick Zeinert

    I too lacked context on the reasoning behind this change when looking at the more recent updates to the boilerplate. This is the kind of explanation I’ve been chasing after. Thanks again!

  • Arka_Bhowmik

    I have a document mousemove event and based on that I have appended some data to the instance of the plugin (like the position of the cursor). But the event is firing for all the instances. How do we go about adding the event inside bindEvents() such that only the instance we are dealing with fires ?

    And how do we save some data to the instance ?

  • Hi Arka,

    You need to ensure that you bind the event properly. I understand this is a broad statement, but it _is_ your problem. Unfortunately without knowing anything more that what you asked above, that’s the best I can do.

    With regard to saving arbitrary data for a particular invokation, that’s exactly what the `.data()` method does. I mentioned this above, but feel free to check out the official jQuery docs as well: https://api.jquery.com/jquery.data/.

    Best of luck!