A Plugin Development Pattern

read 66 comments

I've been developing jQuery plugins for quite a while now, and I've become rather comfortable with a particular style of plugin development for my scripts. This article is meant to share the pattern that I've found especially useful for plugin authoring. It assumes you already have an understanding of plugin development for jQuery; if you're a novice plugin author, please review the jQuery Authoring Guidelines first.

There are a few requirements that I feel this pattern handles nicely:

  1. Claim only a single name in the jQuery namespace
  2. Accept an options argument to control plugin behavior
  3. Provide public access to default plugin settings
  4. Provide public access to secondary functions (as applicable)
  5. Keep private functions private
  6. Support the Metadata Plugin

I'll cover these requirements one by one, and as we work through them we'll build a simple plugin which highlights text.

Claim only a single name in the jQuery namespace

This implies a single-plugin script. If your script contains multiple plugins, or complementary plugins (like $.fn.doSomething() and $.fn.undoSomething()) then you'll claim multiple names are required. But in general when authoring a plugin, strive to use only a single name to hold all of its implementation details.

In our example plugin we will claim the name "hilight".

JavaScript:
  1. // plugin definition
  2. $.fn.hilight = function() {
  3.   // Our plugin implementation code goes here.
  4. };

And our plugin can be invoked like this:

JavaScript:
  1. $('#myDiv').hilight();

But what if we need to break up our implementation into more than one function? There are many reasons to do so: the design may require it; it may result in a simpler or more readable implementation; and it may yield better OO semantics.

It's really quite trivial to break up the implementation into multiple functions without adding noise to the namespace. We do this by recognizing, and taking advantage of, the fact that functions are first-class objects in JavaScript. Like any other object, functions can be assigned properties. Since we have already claimed the "hilight" name in the jQuery prototype object, any other properties or functions that we need to expose can be declared as properties on our "hilight" function. More on this later.

Accept an options argument to control plugin behavior

Let's add support to our hilight plugin for specifying the foreground and background colors to use. We should allow options like these to be passed as an options object to the plugin function. For example:

JavaScript:
  1. // plugin definition
  2. $.fn.hilight = function(options) {
  3.   var defaults = {
  4.     foreground: 'red',
  5.     background: 'yellow'
  6.   };
  7.   // Extend our default options with those provided.
  8.   var opts = $.extend(defaults, options);
  9.   // Our plugin implementation code goes here.
  10. };

Now our plugin can be invoked like this:

JavaScript:
  1. $('#myDiv').hilight({
  2.   foreground: 'blue'
  3. });

Provide public access to default plugin settings

An improvement we can, and should, make to the code above is to expose the default plugin settings. This is important because it makes it very easy for plugin users to override/customize the plugin with minimal code. And this is where we begin to take advantage of the function object.

JavaScript:
  1. // plugin definition
  2. $.fn.hilight = function(options) {
  3.   // Extend our default options with those provided.
  4.   // Note that the first arg to extend is an empty object -
  5.   // this is to keep from overriding our "defaults" object.
  6.   var opts = $.extend({}, $.fn.hilight.defaults, options);
  7.   // Our plugin implementation code goes here.
  8. };
  9. // plugin defaults - added as a property on our plugin function
  10. $.fn.hilight.defaults = {
  11.   foreground: 'red',
  12.   background: 'yellow'
  13. };

Now users can include a line like this in their scripts:

JavaScript:
  1. // this need only be called once and does not
  2. // have to be called from within a 'ready' block
  3. $.fn.hilight.defaults.foreground = 'blue';

And now we can call the plugin method like this and it will use a blue foreground color:

JavaScript:
  1. $('#myDiv').hilight();

As you can see, we've allowed the user to write a single line of code to alter the default foreground color of the plugin. And users can still selectively override this new default value when they want:

JavaScript:
  1. // override plugin default foreground color
  2. $.fn.hilight.defaults.foreground = 'blue';
  3. // ...
  4. // invoke plugin using new defaults
  5. $('.hilightDiv').hilight();
  6. // ...
  7. // override default by passing options to plugin method
  8. $('#green').hilight({
  9.   foreground: 'green'
  10. });

Provide public access to secondary functions as applicable

This item goes hand-in-hand with the previous item and is an interesting way to extend your plugin (and to let others extend your plugin). For example, the implementation of our plugin may define a function called "format" which formats the hilight text. Our plugin may now look like this, with the default implementation of the format method defined below the hilight function.

JavaScript:
  1. // plugin definition
  2. $.fn.hilight = function(options) {
  3.   // iterate and reformat each matched element
  4.   return this.each(function() {
  5.     var $this = $(this);
  6.     // ...
  7.     var markup = $this.html();
  8.     // call our format function
  9.     markup = $.fn.hilight.format(markup);
  10.     $this.html(markup);
  11.   });
  12. };
  13. // define our format function
  14. $.fn.hilight.format = function(txt) {'
  15.  return '<strong>' + txt + '</strong>';
  16. };

We could have just as easily supported another property on the options object that allowed a callback function to be provided to override the default formatting. That's another excellent way to support customization of your plugin. The technique shown here takes this a step further by actually exposing the format function so that it can be redefined. With this technique it would be possible for others to ship their own custom overrides of your plugin נin other words, it means others can write plugins for your plugin.

Considering the trivial example plugin we're building in this article, you may be wondering when this would ever be useful. One real-world example is the Cycle Plugin. The Cycle Plugin is a slideshow plugin which supports a number of built-in transition effects נscroll, slide, fade, etc. But realistically, there is no way to define every single type of effect that one might wish to apply to a slide transition. And that's where this type of extensibility is useful. The Cycle Plugin exposes a "transitions" object to which users can add their own custom transition definitions. It's defined in the plugin like this:

JavaScript:
  1. $.fn.cycle.transitions = {
  2.  // ...
  3. };

This technique makes it possible for others to define and ship transition definitions that plug-in to the Cycle Plugin.

Keep private functions private

The technique of exposing part of your plugin to be overridden can be very powerful. But you need to think carefully about what parts of your implementation to expose. Once it's exposed, you need to keep in mind that any changes to the calling arguments or semantics may break backward compatibility. As a general rule, if you're not sure whether to expose a particular function, then you probably shouldn't.

So how then do we define more functions without cluttering the namespace and without exposing the implementation? This is a job for closures. To demonstrate, we'll add another function to our plugin called "debug". The debug function will log the number of selected elements to the Firebug console. To create a closure, we wrap the entire plugin definition in a function (as detailed in the jQuery Authoring Guidelines).

JavaScript:
  1. // create closure
  2. (function($) {
  3.   // plugin definition
  4.   $.fn.hilight = function(options) {
  5.     debug(this);
  6.     // ...
  7.   };
  8.   // private function for debugging
  9.   function debug($obj) {
  10.     if (window.console && window.console.log)
  11.       window.console.log('hilight selection count: ' + $obj.size());
  12.   };
  13.  //  ...
  14. // end of closure
  15. })(jQuery);

Our "debug" method cannot be accessed from outside of the closure and thus is private to our implementation.

Support the Metadata Plugin

Depending on the type of plugin you're writing, adding support for the Metadata Plugin can make it even more powerful. Personally, I love the Metadata Plugin because it lets you use unobtrusive markup to override plugin options (which is particularly useful when creating demos and examples). And supporting it is very simple! Update: This bit was optimized per suggestion in the comments.

JavaScript:
  1. // plugin definition
  2. $.fn.hilight = function(options) {
  3.   // ...
  4.   // build main options before element iteration
  5.   var opts = $.extend({}, $.fn.hilight.defaults, options);
  6.   return this.each(function() {
  7.     var $this = $(this);
  8.     // build element specific options
  9.     var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
  10.     //...

This changed line does a couple of things:

  • it tests to see if the Metadata Plugin is installed
  • if it is installed, it extends our options object with the extracted metadata.

This line is added as the last argument to jQuery.extend so it will override any other option settings. Now we can drive behavior from the markup if we choose:

<!--  markup  -->
<div class="hilight { background: 'red', foreground: 'white' }">
  Have a nice day!
</div>
<div class="hilight { foreground: 'orange' }">
  Have a nice day!
</div>
<div class="hilight { background: 'green' }">
  Have a nice day!
</div>

And now we can hilight each of these divs uniquely using a single line of script:

JavaScript:
  1. $('.hilight').hilight();

Putting it All Together

Below is the completed code for our example:

JavaScript:
  1. //
  2. // create closure
  3. //
  4. (function($) {
  5.   //
  6.   // plugin definition
  7.   //
  8.   $.fn.hilight = function(options) {
  9.     debug(this);
  10.     // build main options before element iteration
  11.     var opts = $.extend({}, $.fn.hilight.defaults, options);
  12.     // iterate and reformat each matched element
  13.     return this.each(function() {
  14.       $this = $(this);
  15.       // build element specific options
  16.       var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
  17.       // update element styles
  18.       $this.css({
  19.         backgroundColor: o.background,
  20.         color: o.foreground
  21.       });
  22.       var markup = $this.html();
  23.       // call our format function
  24.       markup = $.fn.hilight.format(markup);
  25.       $this.html(markup);
  26.     });
  27.   };
  28.   //
  29.   // private function for debugging
  30.   //
  31.   function debug($obj) {
  32.     if (window.console &amp;&amp; window.console.log)
  33.       window.console.log('hilight selection count: ' + $obj.size());
  34.   };
  35.   //
  36.   // define and expose our format function
  37.   //
  38.   $.fn.hilight.format = function(txt) {
  39.     return '<strong>' + txt + '</strong>';
  40.   };
  41.   //
  42.   // plugin defaults
  43.   //
  44.   $.fn.hilight.defaults = {
  45.     foreground: 'red',
  46.     background: 'yellow'
  47.   };
  48. //
  49. // end of closure
  50. //
  51. })(jQuery);

This design pattern has enabled me to create powerful, consistently crafted plugins. I hope it helps you to do the same.

comment feed

66 comments

  1. Great tutorial. Thanks, I was hoping to delve into jQuery plugins someday and this will be a great help. These were indeed the issues I would run into.

  2. Stan

    Great article! Thanks for posting it. And thank you for all the work you've contributed to the jQuery community.

    Pax,
    - Stan

  3. Byron

    Nice pattern & article Mike, I especially like the last two pointers (private functions and support metadata) as they are something i felt i was lacking an understanding of. thanks for clearing that up :-).

  4. I love the way you implemented the metadata plugin, I've been using it for a while, but never thought about implementing it with extend!

    Thanks for such a great guide.

  5. First of all, superb tutorial!

    However, the following line of code seems to be getting run excessively:

    var opts = $.extend({}, $.fn.hilight.defaults, options, $.meta ? $this.data() : {});

    That's a lot of object iterating to do on an element-by-element basis IMHO, especially if the metadata plugin isn't installed.

    What if you changed it slightly...

    
    $.fn.hilight = function(options) {
        debug(this);
    
        // work out main settings (without metadata) - only do this work once per plugin call
        var main_opts = $.extend({}, $.fn.hilight.defaults, options);
    
        // iterate and reformat each matched element
        return this.each(function() {
          $this = $(this);
    
          // if metadata is present, extend main_opts, otherwise just use main_opts
          var opts = $.meta ? $.extend({}, main_opts, $this.data()) : main_opts;
    
          // update element styles
    
          ... etc
    

    Feedback?

  6. That's a fine optimization, Guy. Thanks!

  7. Wow a fantastic tutorial on how to write jQuery plugins. That's exactly how every one should do it. Glad I've been doing it right all this time ;¬)

  8. I've been doing most of what you are suggesting already, but there are a few things (like making default options public and using the meta plugin) that I was missing, I'll be upgrading my plugins to include these :)

  9. The if statement in the debug function seems to have the && chimped...

  10. Just found another minor optimisation with respect to /packer/ - you can prefix variables to reduce the length of their names:

    http://dean.edwards.name/packer/2/usage/#special-chars

    This is probably overkill for the tutorial, however as lots of people will be looking at this page it's worth increasing knowledge of this feature of the packer.

    So, some tweaks for extra packing goodness:

    
          //
          // create closure
          //
          (function($) {
            //
            // plugin definition
            //
            $.fn.hilight = function($$options) {
              ;;; _debug(this);
    
              // build main options before element iteration
              var $settings = $.extend({}, $.fn.hilight.defaults, $$options);
    
              // iterate and reformat each matched element
              return this.each(function() {
                $this = $(this);
                // build element specific options
                var o = $.meta ? $.extend({}, $settings, $this.data()) : $settings;
                // update element styles
                $this.css({
                  backgroundColor: o.background,
                  color: o.foreground
                });
                var $markup = $this.html();
                // call our format function
                $markup = $.fn.hilight.format($markup);
                $this.html($markup);
              });
            };
    
            //
            // private function for debugging
            //
            function _debug($obj) {
              if (window.console && window.console.log)
                window.console.log('hilight selection count: ' + $obj.size());
            };
    
            //
            // define and expose our format function
            //
            $.fn.hilight.format = function($txt) {
              return '' + $txt + '';
            };
    
            //
            // plugin defaults
            //
            $.fn.hilight.defaults = {
              foreground: 'red',
              background: 'yellow'
            };
          //
          // end of closure
          //
          })(jQuery);
    

    You do have to be super-careful with $ prefixes though. For example, $options and $opts are both packed to "o" (which was already in use lower down the code). To get round that I've used two $$ for the options parameter (packed to "op") and renamed opts to $settings (packed to "s").

    I've renamed debug to _debug (packed to _0 - that's a zero, not an "o") and also put three semicolons (;;;) before the call to debug(this) - the packed version would omit that line completely.

    It's a shame that Dean Edwards' packer isn't based on Rhino - that way he'd be able to work out automatically with very high accuracy which vars are local or private. I believe Jack Slocum's ExtJS packer is based on Rhino and does this, however I haven't tried it to see if it works.

  11. Hi Mike.
    Great job !!!! ( Sorry for my english)
    and, a couple of questions:
    * how I can implement Statics Methods/Attributes ?
    * how I can implement Inheritance?
    for example:
    Person is superclass for employee.
    Thank's :)
    Mario Garcia

  12. After update of metadata plugin consider using
    var o = $.metadata ? $.extend({}, opts, $this.metadata()) : opts;
    code instead in line 18

  13. It DOES help a lot! Thank you for an excellent tutorial.

  14. Is there a sensible way to set variables or run functions within the already running plugin?

  15. Hi Mike and thanks for the tutorial, yet I have a single comment: using $ instead of jQuery inside your plugin isn't recommended, IMO, since it will mean users trying to rename "$" to anything else will not be able to do that from a single location.
    Read: http://docs.jquery.com/Plugins/Authoring#Custom_Alias

  16. Hi Omar,
    If you look at Mike's last code example (in "Putting it all together"), you'll see that he does exactly what the author of your link recommends:

    (function($) {
    
      // plugin code
    
    })(jQuery);
  17. I saw a lot of plugins that uses object notation, how should behave this way?

    $.extend({

    somefunction: function(test) {

    //somecode goes here

    }

    });

  18. Adam T.

    Why might I need to wrap my calling statement like the following:

    
    $(function () {
      $('#myDiv').myMethod();
    });

    Ideally I'd like to get away from having a wrapper in my code and instead just call

    $('#myDiv').myMethod();

    directly. Unfortunately, it seems as though my code doesn't work unless I wrap it in this way.

  19. Josh Kline

    Adam T,

    $(function () { ... }); is a shortcut for $(document).ready(function () { ... });

    That is roughly equivalent to <body onload="...">. The purpose is to run your jQuery code after the entire page has loaded, included jQuery itself, and the dom is ready for manipulation.

    There are plenty of tutorials on the basics of jQuery, but this post is not one of them. You might want to check out docs.jquery.com or some other posts on this site.

  20. Adrien

    Bravo!

    This is a great tutorial, usefull for beginner to confirmed jQuery developpers...

    Explanation are crystal clear... you rocks!

    Just a explanation for those who start jQuery:
    In
    var o = $.metadata ? $.extend({}, opts, $this.metadata()) : opts;

    "$.extend({}, opts, $this.metadata())" returns a new object extending opts with $this.metadata() without modifying opts

    as the first argument of $.extends is like passed by reference, using {} will return a brand new object.

    using "$.extend(opts, $this.metadata())" will modify the general options witch is not wanted...

    my two coins

  21. christian

    GREAT TUTORIAL ... this really got me motivated to play around with jquery plugins.
    One point i am curious about. What do you think of exposing the format-function not via $.fn.highlight.format but as another attribute of the options. So the format function can be changed globally in the defaults or locally for one jquery call, as the other primitive types in the options.

  22. Frink

    I propose a slight alteration to the namespace convention suggested above. It is already in widespread use among plugin developers. In creating long chains for complicated processes in jQuery the pater is:

    $('selector').method().method().method() etc...

    If we use the above recommendation it becomes:

    $('selector').namespace.method().namespace.method().namespace.method() etc...

    This is over-prototyping and bad coding practice. It would be better to use a namespace-prefix instead. consider:

    $('selector').namespaceMethod().namespaceMethod().namespaceMethod() etc...

    In the case of re-render function where manipulates or changes the HTML two functions should be provided. You should provide a function to reverse any destructive operation you do. For re-rendering use (/un)namespaceMethod(). For manipulation use (add/remove)Namspace().

    $('selector').namespaceMethod();
    $('selector').unnamespaceMethod();
    $('selector').addNamespaceMethod();
    $('selector').removeNamespaceMethod()

    This naming convention is used all over jQuery already. These re-render and manipulation functions should always return a jQuery object so that chaning may continue. If you need to use a return code consider setting a context persistent variable in the DOM.

    You should provide a global object rather than extending the prototype object. Consider:

    $.namespace

    instead of:

    $.fn.namespace

    This allows a more accurate representation of data. The global default settings are stored as part of the global default object. The manipulation and render functions are part of the prototyped jQuery select. Also the defaults should not be prototyped. It is better to render the options as a child of the global namespace.

    Finally, initialization was not discussed in this article. Often plugins may wish to initialize on document ready. This should be put after the prototype section of the plugin.

    Altogether a pulgin template should look like this:


    (function($) {
    // Private Variables and Functions
    var privateVariable = {};
    function privateFunction() {
    };
    // Public Variables and Methods
    $.namespace = {
    options: {},
    publicVariable: [];
    publicMethod: function() {}
    };
    // Prototype Methods
    $.fn.extend({
    usernameMethod: function() {
    return this.each(function() {
    // Persistent Context Variables
    this.contextVariable = 'foo';
    }
    }.
    unusernameMethod: function() {
    return this.each(function() {
    delete this.contextVariable;
    }
    }
    });
    //Initialization Code
    $(function() {
    });
    })(jQuery);

    Prototype variables should variables should be avoided. If a context specific variable is required you should use the each() construct and set the variable to this.namespaceVariable. In this way you are using the native DOM construct for variable settings.

    Finally, it is generally considered good etiquette to put a usage information in a comment aat the head of your script file along with author attribute, copyright, version and source repository information. That way developers can easily find where to submit bug reports and change ideas.

  23. Frink

    There are errors in my code above. I was typing fast and my connection froze when I tried to go back and edit it. The skeleton template should have been rendered:


    (function($) {

      // Private Variables and Functions
      var privateVariable = {};
      function privateFunction() {
        // Do Something Privately
      };

      // Public Variables and Methods
      $.namespace = {
        options: {},
        publicVariable: [],
        publicMethod: function() {
          // Do Something Publicly
        }
      };

      // Prototype Methods
      $.fn.extend({
        namespaceMethod: function() {
          return this.each(function() {
            // Example Setting Persistent DOM Variables
            this.contextVariable = 'foo';
          });
        },
        unnamespaceMethod: function() {
          return this.each(function() {
            // Example Removing Persistent DOM Variables
            delete this.contextVariable;
          });
        }
      });

      //Initialization Code
      $(function() {
        $.namespace.publicMethod();
      });

    })(jQuery);

    One final note. Although this isn't standard practice, you can greatly clarify your code by using a private object as opposed to using free-floating private functions and variable. The the private section would therefore look like this:


      // Private Variables and Functions
      var private = {
        privateVariable: {},
        function privateFunction() {
          // Do Something Privately
        }
      };

    This way when you access private variables and methods it will be apparent in your code:


    private.privateVariable = 'foo';
    private.privateMethod('bar');

  24. Frink

    One more oops. Low blood sugar. Wasn't thinking. You should not use "private" as a variable name. instead you can use the namespace you choose.


    // Private Variables and Methods
    var namespace = {
    };
    // Public Variables and Methods
    $.namespace {
    }

    Sorry for the confusion. Argh! I hate not thinking straight.

  25. Frink,

    I'm most definitely not suggesting anyone using namespacing to do this:

    $('selector').namespace.method().namespace.method().namespace.method()

    As was mentioned, this article focuses on a single-plugin script and the suggestion is to use a single namespace for a single plugin. If your script has multiple plugins then you certainly need multiple $.fn extension points (for example, $.fn.doSomething() and $.fn.undoSomething()).

    I believe that using the plugin function for default settings and for exposing public functions is a better implementation than extending the jQuery object itself. I suppose it's a matter of taste, but I prefer to have the entire plugin implementation packaged in the same namespace.

  26. Thanks for the post. I just developed a plugin and I will implement the patterns.

  27. Hi Mike,
    thanks for this most excellent and referential tutorial!

    Could you update it to explain how to implement callbacks? Some concise background info about callbacks would be great as well.

    Thank you so so much for all you do for the jquery community!

    Alexandre

  28. Im working in a big project of ajax and I would like to use a plugin
    pattern thats not working in pratice...

    All javascript in the project I would like to transform in a jQuery
    plugin. But to achieve this I need to separate in categories in
    libraries for this site Im working

    So, I wanna to make this:

    $("seletor").project.ajax.remote();

    $("seletor").helpers.options();

    But when Im creating a plugin like this:

    $.fn.project.ajax.remote = function() {
    //code

    }

    Its not working...

    Using extend I reach it, but extending methods of jQuery objects I
    cant do that. I need this because I create a ajax library just for the
    site and a lot of thing, so we need this pattern...

    How I can accomplish this????

  29. Edilson de Souza

    Hails! Mike, I've been writing a plugin and I wanna know about the flexibility of writing in a OO manner, like so:
    (function($){
        $.myClassName = {

            calcSomething: function(arg){
                //do something
            },

            main: function(){

               return this.each(function(){
                    //do something more
                    $.myClassName.calcSomething(param){

               }
            }
        }
        $.fn.myMethod = $.myClassName.main;
    })(jQuery);

    I'd like to know your opinion about that OO pattern in jQuery plugins.
    Thanks for your article!

  30. michael

    I looked at different plugins code and this is definitely the best plugin pattern I found. Many thanks for that great tutorial!!

  31. Brian

    Hi! That was a great article, but I still have a question:
    If you extend the options this way and the user doesn't set all the new defaults then some options may be missing that can easily cause problem (as they will be undefined). If each properties are set it doesn't matter but only if a new object is given as defaults, which is more convenient in most cases.
    So, wouldn't be better:

    //defaults inside the plugin:
    var defaults = {
    foreground: 'red',
    background: 'yellow'
    };

    $.extend( defaults, $.fn.pluginName.defaults, options )

  32. I almost always enjoy what I read on this site. :)

  33. Wow.. this a great article.. I'm really novice in Jquery world. Just to leant to write my first plugin :) Thanks for the tutorial..

  34. ze

    i love this tutorial, very thanks it's helpfull!

  35. I had to var the $this on line 16 of the putting it all together list to get it to work right when setting up a binding on an element.

    Also, I seem to not have access to opts in from within the getformat function on line 42. Is there a trick to accessing it?

    $.fn.hilight.format = function(txt) {
    alert(opts) //fails
    return '' + txt + '';
    };

  36. mark meves

    One thing I don't get here, near all of the examples with a line like

      return this.each(function() {
        var $this = $(this); ...
    

    After that last line, $this is now an array, isn't it? In the examples, it isn't used as an array. What am I missing?

  37. mark meves

    Ok disregard my above comment -- i shouldn't have spoken too soon. I have since learned that this is lesson 101 of jquery -- we frequently deal with lists of elements

  38. Excellent tutorial !!! Very helpful i created my first plug-in today.
    Will post it very soon on my blog.

    G

  39. Dave Giunta

    Hey all...

    Just wrapped up a little textmate snippet that encompasses this plugin design pattern, with tabstops and all the trimmings. Copy / Paste the following code bit into a new snippet inside the jquery bundle (or your own if you so choose), set a tab trigger (I set mine to "plugin") and the scope selector to: source.js.jquery and you should be good to go. Enjoy!

    
    (function(\$) {
      ${2:// ${3:What does the $1 plugin do? In a nutshell, please.}}
      \$.fn.${1:pluginName} = function(options) {
        var opts = \$.extend({}, \$.fn.$1.defaults, options);
    
        return this.each(function() {
          var \$this = \$(this);
    
          ${5:${6:// This line sets up support for the Metadata Plugin.}
          var o = \$.meta ? \$.extend(\{\}, opts, $this.data()) : opts;}
    
          $0
        });
    
        // private function for debugging
        function debug(\$obj) {
          if (window.console && window.console.log)
            window.console.log(\$obj);
        };
    
      };
    
      \$.fn.$1.defaults = {
        ${4:defaultOne:true,
        defaultTwo:false,
        defaultThree:'yay!'}
      };
    
    })(jQuery);
    
    // This plugin design pattern taken from: http://www.learningjquery.com/2007/10/a-plugin-development-pattern
    
  40. Hi Dave,

    Great idea! I've added the plugin snippet (with a few modifications) to my jQuery bundle on github.com.

    Thanks.

  41. This is really cool! Thanks for sharing it.

  42. Terry Schmidt

    First of all, I loved the article. I am new to jQuery (coming form Proto and Scripty) and loved the examples you used in this article. The way you have described building a plugin is clear and easily understandable.

    I did have a question though, is there a reason that you wrote:

    ...
    var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
    
    // update element styles
    $this.css({
        backgroundColor: o.background,
        color: o.foreground
    });
    ...
    

    instead of:

    ...
    var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
    
    // update element styles
    $this.css(o);
    ...
    

    Again, I very much appreciated you taking the time to write this article. It is a great resource and I can't wait to read more articles.

  43. heribert

    Hi!

    I have a question. Is it possble to extend objects with sub-obobjects ?

    this doesn't work:

    var settings = jQuery.extend( { a: "hello", b: { c: "something", d: "something else" }}, options)

    The b will completely removed by the options b and not extended.

    Thank you very much!
    Sorry for my bad english!

  44. @Terry, Mike probably chose his way rather than yours because the options map didn't match exactly the css style attributes. He has "foreground" and "background" rather than "color" and "backgroundColor".

    @heribert, to recursively extend, you can set the first argument to a Boolean, like so:
    $.extend(true, objectA, objectB);

  45. Hi Dave,

    I'd like to also contribute an in-depth article on how to build jQuery plugins:
    jQuery plugin tutorial

  46. Dimo

    This was a very helpful written article!
    Thank you for posting it!

  47. Frank

    Thanks for the great code examples. People like you make the Internet such a wonderful place! I'm still a bit of a newbie, but I love jQuery. I just found this awesome jQuery Cheat Sheet for the iPhone today. It's really great. I've only had it for a day and it's already making my life a lot easier.

    http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=302090867&mt=8

    Looks like there's also a CSS Cheat Sheet from the same guys.

    http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=301093674&mt=8

  48. rahul

    Exposing $.fn.hilight.format as a public api , I believe, allows one to customize the plugin which is nice!
    What if I want more than one version of the format function?
    I would like to use one Highltier in some situations and a different one in other (based on the format function override)

    • rahul

      Ok, answering my own question -
      I guess I will modify the plugin to accept the 'format' function as a configuration option instead of supporting it as a public function as shown in the article above?

  49. This was not the place to gloss-over the implementation of callbacks.

    If you ever update this article, Mike, consider talking about callbacks in all its guises.

    Cheers!

  50. Excellent article. And a HUGE thanks to Andre (post #36). I spent HOURS working on a plugin, finally reducing it down 14 lines (and 4 of those were debug) which, even if it worked right, wouldn't actually be achieving my desired results -- but there was STILL some "mystery" error!

    Let it be known to the entire world that you have to var your $this if you want to bind it, if you know what I mean. (I realize now that a local variable scope doesn't extend to the bind function within the each function.)

    UNPREDICTABLE RESULTS:
    return each.this.each(function(){
    $this = $(this);
    $this.click(function(){
    // code here
    });

    CORRECT RESULTS:
    return each.this.each(function(){
    var $this = $(this);
    $this.click(function(){
    // code here
    });

    • James Scott

      +1

      Just figured this out myself. Wish I'd read the rest of the comments before hand. Cheers.

      Many thanks for the original post, it's cleared up a lot for me.

  51. Hi,
    I really would like to hear explanation of scope of variables "opts" and "o", can they be accessible from public/private function?

  52. It worked only after i turned this line

    var o = \$.meta ? \$.extend(\{\}, opts, $this.data()) : opts;}

    into

    var o = \$.metadata ? \$.extend(\{\}, opts, $this.metadata()) : opts;}

    new version?

  53. Umesh Kamat

    Excellent tutorial for plugin development.
    I learnt lots of from this .

  54. Hugo

    Hi, first of all thank you for this article, its very well written.

    I am somehow confused with scope in jQuery plugins though. Lets say I need the options to be available in both the debug function and the format function, how should I invoke them? If anyone can explain it or point me in the right direction of how scope works in a plugin pattern as this one I would deeply appreciate it.

    Thanks.

  55. Hugo

    Hi, first of all thank you for this article, its very well written.

    I am somehow confused with scope in jQuery plugins though. Lets say I need the options to be available in both the debug function and the format function, how should I invoke them? If anyone can explain it or point me in the right direction of how scope works in a plugin pattern as this one I would deeply appreciate it.

    Thanks.

  56. Joe

    Total new here, my question is, and I have been doing research, do I need to include jQuery somehow in my plugin or what ?. Help, I feel so dumb as I am thinking I should know this.
    Thanks, this article is a big help to me in my learning.

  57. Sam

    great tutorial! public access to defaults is what i needed to get around (avoiding) using globals.

  58. Joe

    Joe
    RE: Joe April 30, 2009 at 6:23 pm:

    I have found so much here, (answer to my question) as I said Iam new but have and continue to find the information on jQuery here such a great help to me.

    Thanks !.

  59. lion

    putting the default setting at the end is not recommended. Beside that, everything is great

  60. Now, I am studying about 'How to create jQuery plugins'.
    It's great! Thank you for sharing!

  61. Karl Oakes

    Thanks Mike, I have been thinking about creating a jQuery plugin, your article has helped me to clarify a few points such as public methods and properties.

  62. Hi!
    I´m developing my first jQuery plugin and i don´t know how to put some movement efects, like this:
    hover(function(){
    $this.stop().animate({ left: 20 }, 'fast');}, function() {
    $this.stop().animate({ left: 0 }, 'fast');}
    );

    I hope you can help me..
    Thanks!!!!
    Martin

37 Pings

  1. Learning jQuery » A Plugin Development Pattern...

    Learning jQuery » A Plugin Development Pattern...

  2. Un patrón de desarrollo de plugins para jQuery...

    Mike Alsup, autor de jQuery form plugin y otras delicias, nos explica cómo crear un plugin para jQuery que cumpla con las condiciones de: no contaminar el espacio de nombres, acepte opciones (y las extienda), mantenga los límites adecuados entre lo p...

  3. [...] This is the most basic style of jQuery authoring, I got a helping hand here from learning jQuery. [...]

  4. [...] Codex: Writing a Plugin; A (JQuery) Plug-in Development Pattern; Expression Engine documentation; Expression Engine plugin development; Developing Movable Type [...]

  5. [...] And the plugin handles the rest.  The only tricky part has been getting the callback to behave itself, so I’m working on getting that stable.  jQuery plugin generation is actually a pretty pleasant process.  A good primer can be found directly from jQuery’s documentation, and once you’ve gone through that, an excellent practical demonstration can be found here. [...]

  6. [...] 翻译旧文:JQuery插件开发 Oct.30, 2008 in jquery应用与研究 近期要准备一个web前端技术交流会的内容,所以陆续会有一些整理的资料发布,JQuery目前在组内日常开发中占据了重要地位,但各自为战的情况很明显,要做到重用和通用,形成插件是不错的办法,特别是基于JQuery的插件,具有使用简单,可参数化配置等优点。这篇文章就介绍了如何开发JQuery的插件。原文地址是:http://www.learningjquery.com/2007/10/a-plugin-development-pattern [...]

  7. [...] using the jQuery plug-in development pattern it’s fully extendable so it you want to show more than the default number of list items you [...]

  8. [...] Text box hints Auto-populate multiple select boxes jQuery: » Expandable Sidebar Menu Screencast Learning jQuery » A Plugin Development Pattern Evaluation of Javascript Libraries John Resig - Building Interactive Prototypes with jQuery Dr. [...]

  9. [...] article here is a very good read at understanding the basics of creating a plug-in. Tags: Ajax, [...]

  10. [...] a minified production version? Assuming the former, how well is the code factored? Does it follow the standard plugin development pattern? Does it add just one new overloaded method to the jQuery object, or does it litter the namespace [...]

  11. [...] Learning jQuery website offers fantastic advice for packaging your behaviors into plugins: A Plugin Development Pattern. Take a look and give it a [...]

  12. [...] hält das Markup sauber! Folgender Link zeigt sehr schön, wie man so etwas anstellt. Die Kernfunktionalität bildet folgender Schnipsel: [...]

  13. [...] A Plugin Development Pattern- This article is meant to share a useful pattern for plugin authoring. [...]

  14. [...] a quick way to create a plugin in jQuery I found A Plugin Development Pattern (Learning jQuery), by Mike Alsup, via Improving upon the jQuery Plugin Template (Google Groups). Later, much later, I [...]

  15. [...] post, A Plugin Development Pattern (for jQuery), from the Learning jQuery blog is really [...]

  16. [...] >> plugin Favorite WordPress Plugin #28/08/2008 First saved by GetMoneyTV | 11 days ago Comment on A Plugin Development Pattern by mark meves First saved by brettish | 11 days ago How do I remove the wordpress logo from the login and [...]

  17. [...] Apprendre à développer avec un modèle de plugin [...]

  18. [...] A Plugin Development Pattern » Learning jQuery - Tips, Techniques, Tutorials - [...]

  19. [...] A Plugin Development Pattern- This article is meant to share a useful pattern for plugin authoring. [...]

  20. [...] A Plugin Development Pattern- This article is meant to share a useful pattern for plugin authoring. [...]

  21. [...] A Plugin Development Pattern- This article is meant to share a useful pattern for plugin authoring. [...]

  22. [...] plugin patterns are great. The recommended pattern allows for an options object to pass into the methods during invocation with a bunch of defaults to [...]

  23. [...] A4 cheat sheet. 95. How to Build jQuery Plugins – One can try any of the following tutorials: A Plugin Development Pattern; Developing a jQuery Plugin; and Building your First jQuery Plugin. 96. jQuery Pop-up Menu Tutorial [...]

  24. [...] Learning Query: A Plugin Development Pattern (GREAT ONE!) [...]

  25. [...] mais sobre o desenvolvimento de plugins para jQuery, leia o guia oficial de desenvolvimento ou esse excelente artigo de Mike Alsup. Espero ter inspirado [...]

  26. [...] you haven’t seen it yet, you need to checkout a post on LearningJquery.com by Mike Alsup. He came up with a good base for a design pattern on writing jQuery plugins: There are a few [...]

  27. [...] A Plugin Development Pattern » Learning jQuery - Tips, Techniques, Tutorials (tags: jquery plugin) [...]

  28. [...] that bad. I used jQuery’s site a lot and I have to thank Mike Alsup for his excellent great plugin development pattern. You can download it [...]

  29. [...] A Plugin Development Pattern » Learning jQuery - Tips, Techniques, Tutorials (tags: jquery plugins) [...]

  30. [...] making it relatively straightforward to add functionality; b) bring the plugin inline with what’s considered to be the done thing. (This in fact ended up breaking the plug-in completely when an instance is applied to a jQuery [...]

Leave a Comment

IMPORTANT:

  • If you wish to post code examples, please wrap them in <code> tags.
  • Multi-line code should be wrapped in <pre><code> </code></pre>
  • Use &lt; instead of < and &gt; instead of > in the examples themselves. Otherwise, you could lose part of the comment when it's submitted.