Thoughtful jQuery Plugin Development

Why I wrote this issue

I was complaining on twitter, as usual. Then, two developers I respect very much (Chistoph Pojer and Rebecca Murphey) asked me to explain my woes. I believe in a lot of peculiar things--like green jello and casserole--but I don't believe in complaining about something without providing a solution. So, I've turned this into a series of articles on how to write extendable jQuery plugins, so check them out above. Or, continue reading some more whining below.

jQuery plugins are hard to use.

No, I don’t mean the syntax is hard, most of the time it’s as easy as $('#el').doStuff(). Rather, getting the the plugin to meet my requirements is impossible most of the time—unless I monkey patch the plugin’s source code. Why? 99% of them are immutable functions instead of mutable objects.

When I use somebody else’s code, I put a lot of effort in to leaving the source alone*. Unfortunately, I can’t always do that. Most of the plugins are mutable functions with event bindings unbindable, css styles dropped inline, and all sorts of things. This doesn’t have to be the case.

The issue at hand, from the jQuery docs

Here’s a code sample from the jQuery docs on authoring plugins:

(function( $ ){
  $.fn.myPlugin = function() {

    // Do your awesome plugin stuff here

  };
})( jQuery );

Right from the start, none of the “awesome plugin stuff” is mutable. It’s safely (har har) tucked away into an immediately-invoked function expression. I use IIFE’s regularly (that’s my textmate tab trigger) and believe I have respect for the global namespace—but you can’t hide your entire plugin like it’s your underpants and expect anybody but you to be able to use it.

Moving on through the docs we see this example:

(function( $ ){

  var methods = {
    init : function( options ) { // THIS },
    show : function( ) { // IS   },
    hide : function( ) { // GOOD },
    update : function( content ) { // !!! }
  };

  $.fn.tooltip = function( method ) {

    // Method calling logic
    if ( methods[method] ) {
      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    }    

  };

})( jQuery );

Look at the var methods object literal. What if my requirements for showing the tooltip are different than what the plugin author decided? Let’s consider the show method to be this:

show : function( ) { self.fadeIn() },

But I need this:

show: function( ) { self.slideDown() }

There’s nothing left for me to do other than hack the source and change the method. I have no access to the methods object literal. I’m not suggesting a plugin should allow me to change every line of code somehow—well, maybe I am—but there are a lot of things that I ought to be able to change.

There’s a lot of jQuery out there. Unfortunately, only a few have been flexible enough for me to use as I’ve gone looking. The rest I’ve had to hack the source to meet the requirements. If plugin developers can follow some of these tips, they’ll have far more useful code.

* If I find a bug or think of a feature that fits, I’ll fork it on github and send a pull request. Otherwise, I try to leave the source code alone.

Hi, I'm Ryan!

Location:
South Jordan, UT
Github:
rpflorence
Twitter:
ryanflorence
Freenode:
rpflo

About Me

I'm a front-end web developer from Salt Lake City, Utah and have been creating websites since the early 90's. I like making awesome user experiences and leaving behind maintainable code. I'm active in the JavaScript community writing plugins, contributing to popular JavaScript libraries, speaking at conferences & meet-ups, and writing about it on the web. I work as the JavaScript guy at Instructure.