jQuery plugin patterns: something more object-oriented?
I'm working on a jQuery plugin, following the pattern detailed in the Authoring guide. Basically:
(function($) {
// Private
var doSomething = function($element, settings) { ... }
var doSomethingElse = function($element, settings) { ... }
// Public
var methods = {
init: function(options) { ... },
show: function() { ... },
hide: function() { ... },
update: function(content) { ... }
};
$.fn.myPlugin = function(method) {
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 {
$.er开发者_Go百科ror('Method ' + method + ' does not exist on jQuery.myPlugin');
}
};
})(jQuery);
Here's my dislike: I have to pass the same "instance" variables to all of the private functions. I'm still working on becoming a JS pro — so pardon my incorrect term usage — but if I were doing this same thing in Ruby or PHP, I'd create a class with all of these public and private members and methods, and each instance of the class would correspond to an $element
. Then I could do something like this (in JS):
var firstElement = new MyPlugin($element, settings);
firstElement.doSomething();
Rather than passing $element
and settings
to doSomething()
, it already has access to those via this.$element
and this.settings
.
Get where I'm going with this? I'm looking for a more object-oriented approach, I guess. Now, I totally understand that JS doesn't have classes like Ruby or PHP. But between constructor functions, the module pattern, and regular object notation (like methods
above), I'm not sure which is the best option for a jQuery plugin.
Can someone help point me in the right direction? Maybe some examples of existing jQuery plugins that do this well? Thanks!
The jQuery UI Widget Factory might be a good solution. It's useful for creating any kind of stateful jQuery plugins and can be used entirely separate from the rest of the jQuery UI suit.
Some useful links:
- http://bililite.com/blog/understanding-jquery-ui-widgets-a-tutorial/
- http://wiki.jqueryui.com/w/page/12138135/Widget-factory
- http://ajpiano.com/widgetfactory/ (presentation)
If you want a more bare bone solution I'd go with either a regular Constructor + prototype setup to do things "properly" or use the Revealing Module Pattern to create a function that takes the element and any options as arguments and returns the public methods.
An example using the Revealing Module Pattern:
function myPlugin (element, options) {
var privateVar;
function privateFunc () {}
function publicMethod () {}
return {
publicMethodName: publicMethod
};
}
This pattern is a bit more tidy than a traditional prototypal set up, but does not take advantage of the prototype chain.
Edit: To clarify, when using any of these patterns you are supposed to create a new instance for each element/use.
It isn't necessarily a good idea to store any kind of stateful information in the plugin itself since it would be shared by all instances. One option is to store that data elsewhere, outside of the plugin.
The Plugins/Authoring page has a Data section which describes how to store information for use by your plugin on a per-element basis using the data() function.
Using data helps you keep track of variables and state across method calls from your plugin. Namespacing your data into one object literal makes it easy to access all of your plugin's properties from one central location, as well as reducing the data namespace which allows for easy removal if need be.
The example provided on the page uses the plugin pattern described in your post, but allows "instance" variables to be stored with the element they're associated with.
One key thing to remember when doing this is:
Always namespace your methods, events and data.
Edit:
It should be noted too, that in your example some of your functions expect $element
as a parameter, but this isn't necessary since this
will refer to the right thing when those functions are called through the plugin (because apply()
is being called and setting the context to the correct this
).
精彩评论