jQuery plugin object: attached an event handler vi

2019-08-12 06:22发布

问题:

I am attempting to move away from spaghetti code in my jQuery plugins and work in a more structured manner.

I am doing so by using the basic boilerplate template found here: https://github.com/jquery-boilerplate/jquery-patterns/blob/master/patterns/jquery.basic.plugin-boilerplate.js

I also found the ToDo MVC plain jQuery code a source of inspiration because it doesn't chain many functions and keeps things nicely separated (I am not allowed to post more than two links due to my lack of status, so you will have to Google that yourself).

All in all I find a combination of the two above quite good to work with, but there seems to be no way to work like this without referencing this (the root plugin object) quite often. This in itself is not an issue, but sometimes this becomes out of scope. I haven't really found an elegant way around it, especially when binding event handlers via jQuery on.(). Please see my JSFiddle for an illustration of what I mean: http://jsfiddle.net/hendrikdegraaf/wzp3ok4h/18/

I find that passing the plugin object in the event.data object as event.data.base a bit awkward (it's just very verbose and harms readability of my code). Is there not a better way to reference the main plugin object?

I've had issues with variable scope before when using this way of structuring my plugin, so any general advice on how to structure my plugins in a sensible way would be appreciated as well.

Thanks,
Hendrik

EDIT: Please see my code below

;(function ( $, window, document, undefined ) {

    // Create the defaults once
    var pluginName = "myplugin",
        defaults = {
            settingA : 'someValue',
            settingB : 'someValue'
        };

    // The actual plugin constructor
    function Plugin(params) { 
        params.options = $.extend( {}, defaults, params.options);
        this.params = params;
        this._defaults = defaults;
        this._name = pluginName;
        this.init();
    }

    Plugin.prototype = {
        init: function () {
            var base = this; // cache the context so we can still reference it when entering new context
            this.cacheElements();
            this.bindEvents(base);
        },
        cacheElements : function (base) { //cache elements
            this.$el = $('div.doSomething');
        },
        bindEvents : function (base) { // bind some touch functions to touch events, and modal window hidden event
            this.$el.on('click', {base: base}, this.myFunction);
        },
        myFunction : function () {
            console.log(this); //this now refers to $el

        }
    };
    // A really lightweight plugin wrapper around the constructor,
    // preventing against multiple instantiations
    $.fn[pluginName] = function ( params ) {
        return this.each(function () {
            if (!$.data(this, "plugin_" + pluginName)) {
                $.data(this, "plugin_" + pluginName,
                new Plugin(params));
            }
        });
    };

})( jQuery, window, document );

回答1:

I recently ran into this issue, and I found a few work-arounds.

JavaScript's bind works in modern browsers:

bindEvents : function (base) {
    this.$el.on('click', {base: base}, this.myFunction.bind(this));
},
myFunction : function () {
    console.log(this); // Plugin context
}

jQuery's proxy function works in older browsers:

bindEvents : function (base) {
    this.$el.on('click', {base: base}, $.proxy(this.myFunction, this));
},
myFunction : function () {
    console.log(this); // Plugin context
}

You can also create the event handler function inside your bindEvents function, and give yourself access to both contexts:

bindEvents : function (base) {
    var instance = this;
    var myFunction = function() {
        console.log(this); // element context
        console.log(instance); // Plugin context
    };
    this.$el.on('click', {base: base}, myFunction);
}