1

I have a JavaScript module that I would like to create a jQuery plugin interface to. The module itself is like this:

var Foo  = (function () {
    "use strict";
    var self = {};


    self.add = function (selector, otherParam) 
    {

        // Does things unto selector.
        // Precisely what it does doesn't matter.
    };
    return self;
}());

and is used, with success, like this:

Foo.add(a);

Now, I would like to create a plugin that interfaces to this module, so I can use it somewhat like this:

$.fn.foo = Foo;
$.fn.foo.add = function (param) {
    var selector = this;
    Foo.add(selector, param);
}
$(elem).foo.add(a);

The problem I'm facing is that I can't get "this" working in .add(). The best way I managed to do it was to not have Foo be self-initializing, and use a syntax like:

$.fn.foo = Foo;
$(elem).foo().add(a);

It works, but I find it less aesthatically pleasing and less "clean".

Is there a way to do this? Am I on the wrong approach altogether?

Thankful for any input, and I apologize in advance if this has already been answered or is unfit in any other way. I did search for answers, but I'm not well-versed in plugin authoring nor an expert on jQuery itself.

TL;DR: I have a module like Foo above, and would like to access it's members like a jQuery plugin.

xiix
  • 162
  • 9
  • 1
    Have a look [here](https://stackoverflow.com/questions/15884096/organize-prototype-javascript-while-perserving-object-reference-and-inheritance) and [there](https://stackoverflow.com/questions/16502467/prototype-deep-scope-of-this-to-access-instances-scope) – Bergi Mar 03 '15 at 16:39
  • I'm no expert on jQuery plugins either, but does [this question](http://stackoverflow.com/questions/1117086/how-to-create-a-jquery-plugin-with-methods?rq=1) help? Looks like you could use the accepted answer, replacing the `methods` object with `Foo` – Timespace Mar 03 '15 at 16:45
  • Not hard to find lots of different jQuery plugin patterns in a web search. Suggest studying various approaches and then you can adopt one that is comfortable to you – charlietfl Mar 03 '15 at 16:52
  • @charlieftl true enough. finding things on the web is easy. But finding the right things aren't, especially not when not knowing what to search for nor how the solutions would apply to my module. Thankfully, I got advice and help with both :) – xiix Mar 03 '15 at 17:00

1 Answers1

1

Here is a simplified version of the pattern I normally use (error checking and extra features removed).

It uses a single class function and a plugin bridge extension method to allow attachment to multiple elements. Methods are called by using a string option value:

var Foo = (function () {
    "use strict";

    // Constructor
    function Foo($element, options){
        this.$element = $element;
        this.options = options
        this.fooVal = 0;
    }

    // Create method (called from bridge)
    Foo.prototype.onCreate = function(){
        this.fooVal = ~~this.$element.text()
    };

    // Add the specified val to the elements current value    
    Foo.prototype.add = function (val) {
        this.fooVal += val;
        // Update the element text with the new value
        this.$element.text(this.fooVal);
    };
    return Foo;
})();

// Create a bridge to each element that needs a Foo
$.fn.foo = function (options, args) {
    this.each(function () {
        var $element = $(this);
        // Try to get existing foo instance
        var foo = $element.data("Foo");
        // If the argument is a string, assume we call that function by name
        if (typeof options == "string") {
            foo[options](args);
        }
        else if (!foo) {
            // No instance. Create a new Foo and store the instance on the element
            foo = new Foo($element, options);
            $element.data("Foo", foo);
            // Record the connected element on the Foo instance
            foo.$element = $element;
            // Call the initial create method
            foo.onCreate();
        }
    });
}

// testing
console.clear();
$('#test').foo();
$('#button2').click(function () {
    $('#test').foo("add", 2);
});

$('#button10').click(function () {
    $('#test').foo("add", 10);
});

For your example Foo takes the initial value from the element text and subsequent "add" calls modify that value.

JSFiddle: http://jsfiddle.net/TrueBlueAussie/o2u7egfy/3/

Notes:

  • ~~ is just a fast short-cut for parseInt()
  • You could supply an initial value via the options parameter (ignored in first example). See following:

e.g.

// Create method (called from bridge)
Foo.prototype.onCreate = function(){
    this.fooVal = this.options.value || ~~this.$element.text()
    // Set initial value
    this.$element.text(this.fooVal);
};

and start with

$('#test').foo({value: 999});

JSFiddle: http://jsfiddle.net/TrueBlueAussie/o2u7egfy/4/

iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202