4

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 {
      $.error('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!

3
  • What is your intention? Are you trying to maintain a separate state every time you call your plugin? Commented Jul 13, 2011 at 16:42
  • In your example above, do you consider settings an instance variable, or are you referring to something else? Commented Jul 13, 2011 at 16:44
  • A separate state for every element, yes. $element and settings would both be instance variables. Commented Jul 13, 2011 at 17:14

2 Answers 2

5

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:


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.

Sign up to request clarification or add additional context in comments.

2 Comments

Reading those links about the Widget Factory, it sounds pretty great. I think I'll give that try. Thanks!
+1 I agree that this approach is probably what the OP wants to do rather than implementing the functionality as a jQuery plugin.
2

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).

2 Comments

Yes, I'm already using data() to store stateful information, but I don't like that I have to retrieve that data in every function to gain access. this does in fact refer to the DOM element in the public methods, but I don't think it does when a call a private method (unless I use apply() to call those methods). Anyway, your advice is definitely good stuff, but I don't think it helps me with finding a more object-oriented approach. Thanks though!
Ah yes! My mistake with regards to your example's private methods. I think that implementing your additional functionality as a jQuery plugin may not be what you really want then, since you're extending jQuery in the end. I would suggest either using the widget approach, or not implementing your functionality as a jQuery plugin since it sounds like that model doesn't fit your requirements.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.