A Plugin Development Pattern
read 110 commentsI'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:
- Claim only a single name in the jQuery namespace
- Accept an options argument to control plugin behavior
- Provide public access to default plugin settings
- Provide public access to secondary functions (as applicable)
- Keep private functions private
- 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".
- // plugin definition
- $.fn.hilight = function() {
- // Our plugin implementation code goes here.
- };
And our plugin can be invoked like this:
- $('#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:
- // plugin definition
- $.fn.hilight = function(options) {
- var defaults = {
- foreground: 'red',
- background: 'yellow'
- };
- // Extend our default options with those provided.
- // Our plugin implementation code goes here.
- };
Now our plugin can be invoked like this:
- $('#myDiv').hilight({
- foreground: 'blue'
- });
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.
- // plugin definition
- $.fn.hilight = function(options) {
- // Extend our default options with those provided.
- // Note that the first arg to extend is an empty object -
- // this is to keep from overriding our "defaults" object.
- // Our plugin implementation code goes here.
- };
- // plugin defaults - added as a property on our plugin function
- $.fn.hilight.defaults = {
- foreground: 'red',
- background: 'yellow'
- };
Now users can include a line like this in their scripts:
- // this need only be called once and does not
- // have to be called from within a 'ready' block
- $.fn.hilight.defaults.foreground = 'blue';
And now we can call the plugin method like this and it will use a blue foreground color:
- $('#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:
- // override plugin default foreground color
- $.fn.hilight.defaults.foreground = 'blue';
- // ...
- // invoke plugin using new defaults
- $('.hilightDiv').hilight();
- // ...
- // override default by passing options to plugin method
- $('#green').hilight({
- foreground: 'green'
- });
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.
- // plugin definition
- $.fn.hilight = function(options) {
- // iterate and reformat each matched element
- var $this = $(this);
- // ...
- // call our format function
- markup = $.fn.hilight.format(markup);
- });
- };
- // define our format function
- $.fn.hilight.format = function(txt) {'
- return '<strong>' + txt + '</strong>';
- };
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:
- $.fn.cycle.transitions = {
- // ...
- };
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).
- // create closure
- (function($) {
- // plugin definition
- $.fn.hilight = function(options) {
- debug(this);
- // ...
- };
- // private function for debugging
- function debug($obj) {
- if (window.console && window.console.log)
- };
- // ...
- // end of closure
- })(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.
- // plugin definition
- $.fn.hilight = function(options) {
- // ...
- // build main options before element iteration
- var $this = $(this);
- // build element specific options
- //...
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:
- $('.hilight').hilight();
Putting it All Together
Below is the completed code for our example:
- //
- // create closure
- //
- (function($) {
- //
- // plugin definition
- //
- $.fn.hilight = function(options) {
- debug(this);
- // build main options before element iteration
- // iterate and reformat each matched element
- $this = $(this);
- // build element specific options
- // update element styles
- backgroundColor: o.background,
- color: o.foreground
- });
- // call our format function
- markup = $.fn.hilight.format(markup);
- });
- };
- //
- // private function for debugging
- //
- function debug($obj) {
- if (window.console && window.console.log)
- };
- //
- // define and expose our format function
- //
- $.fn.hilight.format = function(txt) {
- return '<strong>' + txt + '</strong>';
- };
- //
- // plugin defaults
- //
- $.fn.hilight.defaults = {
- foreground: 'red',
- background: 'yellow'
- };
- //
- // end of closure
- //
- })(jQuery);
This design pattern has enabled me to create powerful, consistently crafted plugins. I hope it helps you to do the same.
















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.
Great article! Thanks for posting it. And thank you for all the work you've contributed to the jQuery community.
Pax,
- Stan
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 :-).
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.
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...
Feedback?
That's a fine optimization, Guy. Thanks!
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 ;¬)
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 :)
The if statement in the debug function seems to have the && chimped...
Great article !!
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:
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.
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
After update of metadata plugin consider using
var o = $.metadata ? $.extend({}, opts, $this.metadata()) : opts;
code instead in line 18
It DOES help a lot! Thank you for an excellent tutorial.
Is there a sensible way to set variables or run functions within the already running plugin?
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
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:
I saw a lot of plugins that uses object notation, how should behave this way?
$.extend({
somefunction: function(test) {
//somecode goes here
}
});
Why might I need to wrap my calling statement like the following:
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.
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.
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
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.
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:
$.namespaceinstead of:
$.fn.namespaceThis 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.
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');
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.
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
$.fnextension 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.
Thanks for the post. I just developed a plugin and I will implement the patterns.
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
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????
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!
I looked at different plugins code and this is definitely the best plugin pattern I found. Many thanks for that great tutorial!!
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 )
I almost always enjoy what I read on this site. :)
Wow.. this a great article.. I'm really novice in Jquery world. Just to leant to write my first plugin :) Thanks for the tutorial..
i love this tutorial, very thanks it's helpfull!
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 + '';
};
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?
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
Excellent tutorial !!! Very helpful i created my first plug-in today.
Will post it very soon on my blog.
G
jQuery very good!
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!
Hi Dave,
Great idea! I've added the plugin snippet (with a few modifications) to my jQuery bundle on github.com.
Thanks.
This is really cool! Thanks for sharing it.
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:
instead of:
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.
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!
@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);Hi Dave,
I'd like to also contribute an in-depth article on how to build jQuery plugins:
jQuery plugin tutorial
This was a very helpful written article!
Thank you for posting it!
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
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)
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?
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!
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
bindfunction within theeachfunction.)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
});
+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.
Actually lane you could even write:
var $this = $(this).click(function(){});Hi,
I really would like to hear explanation of scope of variables "opts" and "o", can they be accessible from public/private function?
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?
Excellent tutorial for plugin development.
I learnt lots of from this .
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.
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.
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.
great tutorial! public access to defaults is what i needed to get around (avoiding) using globals.
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 !.
putting the default setting at the end is not recommended. Beside that, everything is great
Now, I am studying about 'How to create jQuery plugins'.
It's great! Thank you for sharing!
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.
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
Great article, you really got me started and now I have some knowledge of basic requirements for plugins too:) Tnx!
Hi, I just read this article and it is awesome. It re-enforced some of my thoughts and taught me others. However, just food for thought; I wonder, what do you think about this:
$.fnreferences the prototype of jquery. This means that adding global data ($.fn.hiligt.defaults) will be copied across each instance of the objects with hiligt (I think). A better approach would be to apply those directly to the jquery object:$.hiligt.defaults = {...};This way, we can ensure that there is only one copy of the defaults which is shared between all instances of the hiligt and if it changes, all instances are affected and not just the new ones.
I guess this only really applies to plugins that do more than one function... that may have an API that allow it to do different things at different times.
Thanks,
Dipun
After reading the original post as well as all the comments, I have merged this with my own ideas and have created the
jquery.PluginPattern.js
and put it up on the jQuery website. Have a look and let me know what you think. I am going to use this in my upcoming jQuery class at LISA'09.tobi oetiker
Hi
Creating a plugin from scratch is great but is there a why to extend existing plugin.
For example jquery ui plugins.
$(selector).dialog( 'do_something_special' )
I would like to add the do something functionality.
Regards
I found it very helpful.
thanks a lot~
stay cool
That is very very very helpful. When ever somebody ask a question regarding how to extend the defaults or creating a jQuery plugin on irc, I just point them to this article.
Great article !!
This article has change the way I implement jQuery. Yeah, using plugin development pattern is really sweet. Thanks for a great share....
Great post and help much to make my own plugins.
Thanks for sharing to the world, mike :)
That is really a nice tutorial...
Thanks a lot guy for this.. please keep working...
Regards,
Rajkot
Very nice and useful tutorials to web designers,
Thanks for posting.
I have a problem with my plugin. I am assigning my function (called metadata) to $.fn.metadata as is said in this post, but I get an error "Uncaught TypeError: Object 0 has no method 'metadata'".
I have poked around in the console and it seems that $.fn.metadata exists and is my function, but $.metadata doesn't. Do I need to assign to both?
Sorry, its fixed now. I hadn't read the documentation for $.each(). 'Object 0' is the argument to the callback in $.each() which I had assumed to be the subitem, not the index.
Sorry if I missed this but, how should I handle multiple plugins (of the same type) on the page...
It appears that in this model, this object gets overwritten and the last one is the only one used.
Scenario:
Plugin that performs AJAX calls in order to behave as an auto-complete control
Each object on the page has its own behaviour based on options: url, clicked event, etc.
Everything works great when only 1 is on the page, but not with multiples...
Please help!
Great approach. The only aspect I don't like about it, but haven't found a viable alternative for yet, is the way private functions are designed. Check out my post at StackOverflow: http://stackoverflow.com/questions/2061501/jquery-plugin-design-pattern-common-practice-for-dealing-with-private-function
thanks for the post, it prove to be very useful for me.
I read the comments so closely and 3 or 4 people asked about either how to access options from one of the functions, or how options are handled for multiple plug-in instances, but no answer yet from what I can see.
I'd like to second these questions :
- How to access options from public and private methods
- How to handle multiple instances of your plug-in
Thank you for anyone who has an interest to answer and thank you for the article.
Concerning how to handle multiple instances of your plugin, a solution that looks appropriate is to use the DOM element itself to store instance-specific data. Frink describes this in some of the previous comments as context variables. For example
The problem is that since I'm new at jQuery, I'm not confident this is the best solution :) Maybe someone can suggest a better solution?
Great, well structured article, many thoughtful responses. Thanks everybody.
The article is especially good for one reason: It introduces Douglas Crockford's rather old Module design pattern to the huge jQuery community.
There's a slight hope that the pattern described will lead the one who adopts it to be a better JavaScript programmer.
There are many responses to the article from which you can tell that the guys who left them don't know the JavaScript language, such as:
- suggestions about cluttering the jQuery object for convenience instead of proper encapsulation
- questions about member scope & access, about instance & static members
- questions about problems caused by implied globals (missing variable declaration).
A proposal TWIMC: If you want to write a plugin for jQuery, consider LEARNING THE JAVASCRIPT LANGUAGE. Don't repeat the mistake of many others (me included) and try just to "figure out" how JavaScript works. Give it the respect it deserves as a full featured programming language. You wouldn't try to "figure out" C++, right?
First grade resources are in my view:
Douglas Crockford's http://javascript.crockford.com/
and his videos on http://developer.yahoo.com/yui/theater/ (recommended: The JavaScript Programming Language & Advanced JavaScript).
Else Chuck Norris will come a-roundhouse kick you ;)
Great tut ! Thanks
i think its better to use
$.extend(true, {}, $.fn.hilight.defaults, options);instead of
$.extend({}, $.fn.hilight.defaults, options);cause defaults like
{ first: { option: 1, option: 2}, second: {option:1, option: 2}wont be written by the extend() Method, except you pass all options in the invoke method.
Thanks. This is likely the most comprehensive jQuery plugin guide around, and is helping to define best practices. This is really helpful for beginning/intermediate jQuery users, esp. those without a firm grasp on the inner workings of the beast. Thanks again.
thanks for the article, i will try it
Hi Mike,
Great tutorial thanks a lot for this, just
need to change line no 16, by below line
var o = $.metadata ? $.extend({}, opts, $this.metadata()) : opts;
Thanks,
Amit.
Hi Mike,
Thank you very much for this great tutorial,
the final code worked for when I changed line no 16 to below
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;Thanks,
Amit.
When I tried to apply this plugin development pattern Visual Studio Intellisense for jQuery stopped working. But I found a solution: Getting Visual studio jQuery Intellisense working again within jQuery plugin pattern http://www.bnomad.com/2010/07/getting-visual-studio-jquery.html
@author: It would be appropriate to flag this tutorial as outdated somewhere at the top, as all included guidelines are nowadays easily achieved using jQuery widget factory.
Plugin factory is a utility from jquery ui. If you are doing a plugin for jquery this is the best way. INHO.
Thanks for that great article, I have been writing jQuery for a while now but only just got into writing plugins for it, this was a very helpful guide. Not used the meta plugin yet but I'll be giving that a go later
Nice work. Quite an advanced post from others I've seen, will definitely give this a go.
Here's an alternative, more basic jQuery plugin template.
Thanks
I have a plugin pattern that I've just blogged where I link to your plugin pattern as an alternative to my readers.
You can find my pattern at http://milan.adamovsky.com/2010/09/jquery-plugin-pattern-20.html
Great work!
I like what I've read in this post, but I'm unclear as to how I can keep track of variables for each element in the original selection. For example:
markup:
<div class="slideshow" id="slideshow1">...</div>
<div class="slideshow" id="slideshow2">...</div>
Code:
$('.slideshow').slideshow (); // creates a slideshow for each "slideshow" class (2 total)
... then for a specific instance:
$('#slideshow1').slideshow.next (); // I need to somehow store the current frame
Can someone recommend a method to do this?
Using this design pattern, how would I get access to the value of the variable settings outside the plugin?
For example, I am setting a bunch of values on the settings variable in my code. I need to get access to these properties.
In my plugin:
In my page I have:
var myObject = $('#some-element').myPlugin();What I want to do is access the settings property, so something like:
var curPosition = myObject.settings.imagePosition;Or I tried among other ways:
$.fn.myPlugin.attributes.imagePosition;Thanks for any help.
- Charles
$("select",obj).bind("click.mydownlist", {sel_index:$(this).attr("sel_index")},defaults.onclick);
I have bind data in jquery plugin , but how to get it in client's callback function?
> Our "debug" method cannot be accessed from outside of the closure
> and thus is private to our implementation.
No it's private implementation; Debug function is available all over your code
Hmmm, ok, strange:
(function($){ function debug() { alert("debug ok"); } debug2 = function() { alert("debug2 ok"); } [...]Debug function is private
Debug2 isn't private.
That's because debug2 is a function expression, and you didn't declare the variable (using
var), which makes it available globally.I'm sorry if I missed something in the comments that might already address this concern, but is there a reason why you're adding $this to the global namespace?
Shouldn't
$this = $(this);be
var $this = $(this);?I was wondering why you use the implied global variable $this during your iterations.
Shouldn't the declaration of $this=$(this) be a variable declaration to keep it local to the each iteration...in other words, wouldn't it be better to say: var $this=$(this); ?
I'm sure I am missing something, but any advice would be greatly appreciated. Thanks for the great pattern/tutorial. I'm excited to write my first plugin.
Great article! Thanks for posting it. And thank you for all the work you've contributed to the jQuery community.
why do you pass jquery and window sometimes together?