I have been dabbling in different styles of jQuery plugin architecture for a few months now. At first, I tried to find a definitive style to emulate, but the jQuery docs don’t express much more than how to leverage the basic API for plugins. The conflict I was having about approach is jQuery is a DOM-centric library, however, not all plugins solely manipulate the DOM. Choosing the best pattern has been a struggle. Below I outline some of the pros and cons of three different formats. I have been using the last pattern for the last few weeks and currently prefer it.
At the office, I began using the below basic skeleton.
//<![CDATA[ /* name : pluginname file : jquery.pluginname.js author : gregory tomlinson (c) Copyright 2009 AOL LLC /////////////////////////// /////////////////////////// dependencies : jQuery 1.3.2 /////////////////////////// /////////////////////////// */ (function($) { $.fn.pluginname = function( options ) { var defaults = { // declare all plugin defaults here }; var ClassName = { options : {}, ui : { bx : null }, init : function( obj, options ) { this.options = options; this.ui.bx = obj; } }, class_options = $.extend(true, defaults, options), j_obj = $(this); ClassName.init( j_obj, class_options ); return j_obj; } })(jQuery); //]]>
This approach definitely has its strengths. Coming from a background of developing MooTools classes in the past, it was very familiar to me. What I liked about it is it allows me to instantiate multiple instances of the plugin without collision and control the scope very easily.
As you can see, I cache the original value of this into my ui object. This allows me to retain access to the element or group of elements that are originally targeted via jQuery('<element>'); It also let me adjust the scope of the ‘this’ keyword to target my specific internal methods.
One of the nicest things about this approach is all the methods are private. But, it means you cannot call any additional methods, or maintain control over anything in the plugin once it’s been originally initialized. While they are definitely scenarios where having private methods is a huge plus, it’s not a one sized fits all solution.
Additionally, because I changed the scope of the this keyword, it’s difficult for other developers, familiar with jQuery, to quickly read and understand my pattern and code. One major downside is you junk up the code with tons of references to this.
This led me to begin thinking about alternatives to the above approach. I am currently trying out a new format with the FriendFeed plugin that I recently released. Here is the bare bones of that approach.
//<![CDATA[ /* name : friendfeed file : jquery.friendfeed.js author : gregory tomlinson site: http://gregorytomlinson.com/ /////////////////////////// /////////////////////////// dependencies : jQuery 1.3.2 /////////////////////////// /////////////////////////// */ (function($) { $.fn.friendfeed = function( user, options ) { /* declare INSTANCE specific variables and settings */ /* extend the defaults to include all user specified options */ var defaults = $.extend( true, $.friendfeed.defaults, options ); /* do anything I want here, keep a cached version of the override options for this instance. */ return this; /* jQuery default behavior, return container */ }; $.friendfeed = { log : function( str ) { if( !this.defaults.debug ) { return; } try { console.log( str ); } catch(e){} } } /* define defaults for override */ $.friendfeed.defaults = { /* this is publicly accessible */ }; })(jQuery);
Again, this approach has several strengths. Because I have moved the specific method I might need, in this case ‘log’ outside of the $.fn.method, I can retain access to it, aka it’s public, not private. The biggest drawback to this approach is that collision could occur. Also, options that may be specific to this instance aren’t always easily accessible internally, which can lead to default values that were intended to be overriden at runtime containing incorrect values.
The final pattern I have playing with most recently has mostly private methods, but the pattern doesn’t alter the scope of this, and the code isn’t junked up with tons of references to this
Here is a sample skeleton of the pattern
//<![CDATA[ /* name : pluginName file : jquery.pluginName.js author : gregory tomlinson site: http://gregorytomlinson.com/ /////////////////////////// /////////////////////////// dependencies : jQuery 1.3.2 /////////////////////////// /////////////////////////// */ (function($) { var defaults = { /* stuff I want to override */ }, el; function myMethod() { // do private method stuff } function eventMethod(e) { /* listen to my element */ } $.fn.pluginName = function( options ) { // do stuff with the method here el = this; $.extend( true, defaults, options ); el.bind( 'click', eventMethod ); // attach an event for kicks myMethod(); el.trigger('myPluginName'); // standard jQuery - return the jQuery Object return this; } })(jQuery);
It’s very simple to read and the code is fairly concise. It has some limitations, like the inability to directly alter the code once it’s been initialized, however, you can easily attach listeners that can handle that action for you.
I’m not particularly fond of any of these approaches, though I have found myself using the last pattern more often recently. There is definitely something to be said for the MooTools class pattern, though I do find it irritating that MooTools attempts to turn JavaScript into a classical language. jQuery doesn’t mask JavaScript’s prototypical nature and I find that more honest. I plan to keep exploring approaches and find a pattern that both fits my style and leverages the strengths of jQuery.