I'm trying to write my first plugin using jQuery Boilerplate. The issue I'm having is that I lose access to the this
scope as soon as I try to handle an event, due, I assume, to variable shadowing. It seems like would be a common use case for jQuery Boilerplate, so I'm guessing that I'm going about it incorrectly.
I found two similar questions here on SO:
- jQuery plugin object: attached an event handler via .on() and now have a scope issue of this. (the main plugin object) - Has no answer
- Javascript scoping Issue using JQuery Boilerplate - Doesn't answer my question
I've created a minimal sample to demonstrate the issue.
HTML
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
PLUGIN
(function($, window, document, undefined) {
'use strict';
var pluginName = 'elementValueLog',
defaults = {};
function Plugin(element, options) {
this.element = element;
this.$element = $(element);
this.settings = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}
$.extend(Plugin.prototype, {
init: function() {
this.$element.on('click', this.doLog);
},
doLog: function() {
// HERE: I can't figure out how to access the Plugin "this" scope
// I want to be able to use "element", "$element", "settings", etc.
console.log(this.$element.val().trim());
}
});
$.fn[pluginName] = function(options) {
this.each(function() {
if (!$.data(this, "plugin_" + pluginName)) {
$.data(this, "plugin_" + pluginName, new Plugin(this, options));
}
});
return this;
};
}(jQuery, window, document, undefined));
USE PLUGIN
$(document).ready(function() {
$('li').elementValueLog();
});
SOLUTION
I would have preferred to add this as an answer, but being marked as "duplicate" is preventing that. After trying several methods shown in the answers on the other post, I found a solution. I personally think my question is specific enough to stand on its own, because the "canonical" answer on the other post is quite broad.
For browsers that support bind
, the init
function can be changed like this:
init: function() {
this.$element.on('click', this.doLog.bind(this));
},
Because I need to support IE 8, I will be using jQuery.proxy:
init: function() {
this.$element.on('click', $.proxy(this.doLog, this));
},
The doLog
function can then reference this.element
, this.$element
, this.settings
, etc.
Based on the answer from Jeff Watkins:
init: function() {
var plugin = this;
var doLog = function() {
console.log(plugin.$element.val().trim());
};
this.$element.on('click', doLog);
},
This solution has the benefit of preserving the this
context while giving access to the Plugin's this
context.