1

I'm using jQuery's event system to allow external code to drive my plugin. In my event handlers 'this' is set to the element that the events are bound to, so what's the best way for me to access the plugin's methods themselves?

;(function($, window, document, undefined){
    var pluginName = "book";

    // Standard constructor
    function Plugin(element, options){
        this.element = element;
        this.options = $.extend({}, defaults, options);

        this.init();
    }

    // Simple init
    Plugin.prototype.init = function(){
        this.setBindings();
    }

    // Tie local methods to event channels
    // so that external code can drive the plugin. 
    Plugin.prototype.setBindings = function(){
        var events = {
            'book-open'      : this.open,
            'book-next-page' : this.toNext,
            'book-prev-page' : this.toPrev,
            'book-cover'     : this.toFront,
            'book-back'      : this.toBack
        }

        for(event in events){
            var fn = events[event];
            console.log(event);
            this.$element.on(event, fn);
        }
    };

    // Event Handlers
    Plugin.prototype.open = function(){
        // when called externally 'this' refers
        // to the element the plugin was intialized on.
        // I want to be able to call the plugin's 'private'
        // methods, like someMethod() below.
    };

    /* .... other event handlers ...  */

    // 'Private' plugin methods
    Plugin.prototype.someMethod = function(){
        // do something
    }

    // Wrap and return technique from @ajpiano & @addyosmani
    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if ( !$.data(this, "plugin_" + pluginName )) {
                $.data( this, "plugin_" + pluginName,
                    new Plugin( this, options ));
            }
        });
    }

 })(jQuery, window, document);
Thomas
  • 5,736
  • 8
  • 44
  • 67

1 Answers1

2

You can, instead of passing the function itself, call a function that will return the function you want to execute, which a closure around the plugin.

var createBookOpenFunction = function () {
    var self = this; //since you execute this function on the plugin, "this" will be the plugin
    return function () {
        self.open();
    }
};

then, instead of calling...

this.$element.on(event, fn);

you instead call

this.$element.on(event, this.createBookOpenFunction());

so now, when the function is called on the $element, the actual execution is done on the plugin object, because it's closed on "self".
and you can just feed the parameters (if there are any) through the returned function, into the call "self.open()".

Also, this thread may be of help: Controlling the value of 'this' in a jQuery event

(I don't use jQuery directly, so I am unfamiliar with what all is available in the API, but some posts on here seem to have alternate solutions to your problem)

Community
  • 1
  • 1
Caleb
  • 1,088
  • 7
  • 7
  • 1
    That thread you linked to had the answer. Using $.proxy allows you to define an arbitrary scope on any function. So the code should read this.$element.on(event, $.proxy(fn, this)); – Thomas Oct 12 '12 at 19:57
  • Can you explain why it is necessary to wrap the function inside of an anonymous function? This works for me but I can not realize why it is necessary – asumaran Jul 15 '13 at 05:37
  • 2
    in javascript, "this" means the object that is calling the function. if I attach a function pointer to a different object, the "this" inside of that function will be different at execution time. So by wrapping the function I want to call, I can create a reference to the object "self" and call the method on the "self" reference. That way I have control over what "this" means in the function I am calling. – Caleb Jul 15 '13 at 19:29
  • that last comment gave me a eureka moment – daviestar Jul 20 '14 at 23:25