This question already has answers here:
The value of "this" within the handler using addEventListener
(10 answers)
Closed 3 years ago.
I must be overlooking something as my eventlistener is not removed. I created a small reproduction. I am not using anynomous functions. The signature of the addEventListener is identical to the removeEventListener. Still my listener is still being triggered when I dispatch the event.
Even though the 3rd argument is not default in any modern browser, I still added it for debugging purposes but it didn't make any difference.
Can someone please help me out here. What am I missing?
function Foo(){
this.AddComponent();
}
Foo.prototype.AddComponent = function(){
var self = this;
window.addEventListener("OnAdd",self.OnAdd,false);
var ev = new CustomEvent('OnAdd', {
detail: {}
});
window.setTimeout(function(){
console.log('dispatched');
window.dispatchEvent(ev)
},1000);
}
Foo.prototype.OnAdd = function(event){
console.log('I was fired!');
var self = this;
window.removeEventListener("OnAdd",self.OnAdd,false);
// try to fire again, which in theory should not work
var ev = new CustomEvent('OnAdd', {
detail: {}
});
window.dispatchEvent(ev);
}
new Foo();
The problem is that this inside OnAdd is not bound to the instance.
function Foo(){
this.OnAdd = this.OnAdd.bind(this);
this.AddComponent();
}
Foo.prototype.AddComponent = function(){
var self = this;
window.addEventListener("OnAdd",self.OnAdd,false);
var ev = new CustomEvent('OnAdd', {
detail: {}
});
window.setTimeout(function(){
console.log('dispatched');
window.dispatchEvent(ev)
},1000);
}
Foo.prototype.OnAdd = function(event){
console.log('I was fired!');
var self = this;
window.removeEventListener("OnAdd",self.OnAdd,false);
// try to fire again, which in theory should not work
var ev = new CustomEvent('OnAdd', {
detail: {}
});
window.dispatchEvent(ev);
}
new Foo();
Same code, just as an ES6 class:
class Foo {
constructor() {
this.OnAdd = this.OnAdd.bind(this);
this.AddComponent();
}
AddComponent() {
var self = this;
window.addEventListener("OnAdd", self.OnAdd, false);
var ev = new CustomEvent('OnAdd', {
detail: {}
});
window.setTimeout(function() {
console.log('dispatched');
window.dispatchEvent(ev)
}, 1000);
}
OnAdd(event) {
console.log('I was fired!');
var self = this;
window.removeEventListener("OnAdd", self.OnAdd, false);
// try to fire again, which in theory should not work
var ev = new CustomEvent('OnAdd', {
detail: {}
});
window.dispatchEvent(ev);
}
}
new Foo();
Related
Let's assume we have the following code:
var MyClass = (function(){
var _this;
function MyClass(inputVal){
_this = this;
this.value = inputVal;
}
MyClass.prototype.getValue = function(){
return this.value;
}
MyClass.prototype.getValue2 = function(){
return _this.value;
}
return MyClass;
})();
Let's make two instances of the class:
var instance1 = new MyClass(10);
var instance2 = new MyClass(20);
Now if we console.log() the values we see that:
instance1.getValue(); // 10
instance1.getValue2(); // 20
var MyClass = (function(){
var _this;
function MyClass(inputVal){
_this = this;
this.value = inputVal;
}
MyClass.prototype.getValue = function(){
return this.value;
}
MyClass.prototype.getValue2 = function(){
return _this.value;
}
return MyClass;
})();
var instance1 = new MyClass(10);
var instance2 = new MyClass(20);
console.log(instance1.getValue());
console.log(instance1.getValue2());
Why is that happening? It looks obviously that the _this variable gets the latest created instance properties. How to fix that? I need to keep a copy of this. Thanks!
Edit:
Here's the real situation
var HoverEffects = (function(){
var _this;
function HoverEffects($nav){
_this = this;
this._$activeNav = $nav.siblings('.active_nav');
this._$hoverableLis = $nav.find('>li');
this._$activeLi = $nav.find('>li.active');
if(!$nav.length || !this._$hoverableLis.length || !this._$activeNav.length || !this._$activeLi.length) return;
if(this._$activeNav.hasClass('bottom')){
this._$activeNav.align = 'bottom';
this._$activeLi.cssDefault = {
left: this._$activeLi.position().left,
width: this._$activeLi.width()
};
}
else if(this._$activeNav.hasClass('left')){
this._$activeNav.align = 'left';
this._$activeLi.cssDefault = {
top: this._$activeLi.position().top,
height: this._$activeLi.height()
};
}
else{
return;
}
this._$hoverableLis.hover(
function(){
// How to set the correct this inside this function?
if(this._$activeNav.align === 'bottom'){
this._$activeNav.css({
left: $(this).position().left,
width: $(this).width()
});
}
else if(this._$activeNav.align === 'left'){
this._$activeNav.css({
top: $(this).position().top,
height: $(this).height()
});
}
},
function(){
// Same here, wrong this
this._$activeNav.css(this._$activeLi.cssDefault);
}
);
}
return HoverEffects;
})();
var sideNavHoverMagic = new HoverEffects($('#side-navigation'));
var primaryNavHoverMagic = new HoverEffects($('#primary-navigation'));
Why is that happening?
Every time you call new MyClass, _this = this gets run. The second time overrides the first time.
So _this refers to new MyClass(20), which means that when you call getValue2 from any MyClass instance, 20 will be returned because all MyClass instances are referring to the same _this value.
Based on commentary on the Question:
If you're attempting to pass a function bound to the appropriate context there are a variety of ways to make sure that this refers to the right object. Before continuing, please read "How does the 'this' keyword work?", because there's no reason for me to repeat all of it here.
If you're binding event callbacks such as in a constructor:
function Example(something) {
something.addEventListener(..event.., this.callback, false);
}
Example.prototype.callback = function () {
this.doStuff();
this.doMoreStuff();
};
The callback will have the wrong this value because it's not being called as this.callback, it's just being called as:
fn = this.callback;
fn(); //no reference to this
You can get around this in a number of ways.
Function.prototype.bind
You can bind the callback for every instance on their respective instance. This is very concise:
function Example(something) {
//generate a new callback function for each instance that will
//always use its respective instance
this.callback = this.callback.bind(this);
something.addEventListener(..event.., this.callback, false);
}
Example.prototype.callback = function () {
this.doStuff();
this.doMoreStuff();
};
that = this
You can create the callback (closure) within the constructor and reference a variable inside the constructor.
function Example(something) {
//every Example object has its own internal "that" object
var that = this;
this.callback = function () {
//this function closes over "that"
//every instance will have its own function rather than
//a shared prototype function.
that.doStuff();
that.doMoreStuff();
}
something.addEventListener(..event.., this.callback, false);
}
() => {} (Fat Arrow Syntax)
If you're using ES2015 you can use "fat arrow" syntax for creating lambdas that don't create a new context:
function Example(something) {
this.callback = () => {
//the callback function doesn't create a new "this" context
//so it referes to the "this" value from "Example"
//every instance will have its own function rather than
//a shared prototype function.
that.doStuff();
that.doMoreStuff();
}
something.addEventListener(..event.., this.callback, false);
}
I'm building a jQuery app using OOP principles and I'm trying to implement a externally added callback function which invokes a method from inside of my object.
function testObject() {
var self = this;
var functions = new Array();
this.updateObject = function() {
console.log('updated')
}
this.addFunction = function(func) {
functions.push(func)
}
this.callFunctions = function() {
$.each(functions, function(key, value) {
functions[key]()
})
}
}
var myobject = new testObject();
myobject.addFunction(
function() {
$(':text').on('change', function() {
return self.updateObject();
})
}
)
This is an overly simplified version of the plugin I'm building. The callback works fine, but I cannot use the self.updateObject(); inside of it, since it outputs Illegal Invocation.
How can I call a method from inside the callback properly?
The problem is self is out of scope of the callback function, because the function only has variables in the scope of where it was defined. The callback is defined outside of the testObject.
A solution is to bind the this context in the callback function to self using Function.prototype.call(self), when you call it in callFunctions(). Then in the callback, you can use this to refer to the testObject instance. In your callback example it contains a jQuery event so you will lose the this context. To rectify that you can create a local self that equals this before the jQuery change event.
function testObject() {
var self = this;
var functions = new Array();
this.updateObject = function() {
console.log('updated')
}
this.addFunction = function(func) {
functions.push(func)
}
this.callFunctions = function() {
$.each(functions, function(key, value) {
functions[key].call(self); // call it and bind the context to self
})
}
}
var myobject = new testObject();
myobject.addFunction(
function() {
var self = this; // needed because the change event will overwrite 'this'
$(':text').on('change', function() {
return self.updateObject(); // use self to access testObject
})
}
)
myobject.callFunctions();
Alternatively you can pass self as an argument to the callback. To do that, change the .call() line to:
functions[key].call(null, self);
and change the callback to accept an argument like so:
myobject.addFunction(
function(self) { // self as an argument
$(':text').on('change', function() {
return self.updateObject(); // use self to refer to testObject
})
}
)
function testObject() {
var self = this;
var functions = new Array();
this.updateObject = function() {
console.log('updated')
}
this.addFunction = function(func) {
functions.push(func.bind(self)) // Bind the context
}
this.callFunctions = function() {
$.each(functions, function(key, value) {
functions[key]()
})
}
}
var myobject = new testObject();
myobject.addFunction(
function() {
var self = this;
$(':text').on('change', function() {
return self.updateObject();
})
}
)
Or you can use this as well:
myobject.addFunction(
function() {
$(':text').on('change', this.updateObject);
}
)
This question already has answers here:
OOP. Calling methods from within methods
(4 answers)
Closed 8 years ago.
I'm just trying to figure out how I can call a javascript object method from within a method of the same object as below..
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003";
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
method2();
}
},
method2: function() {
this.method1();
}
}
Changed my question as I realise when using this.method2() it is refering to the WebSocker object.
There are a lot of answers in SO for problems like this, you should do a little research(on SO or on Google) before asking here.
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003",
self = this;
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
self.method2();
}
},
method2: function() {
this.method1(); //something like this would cause an infinite call stack, you should change this code
//this refers to the current object, so has properties method2 and method2
}
}
You need to reference to the current object using this, otherwise the JS Engine will look for a function named method1 in any of the higher scopes, all the way up to the global namespace. If such a function object (or such a name doesn't exist), method1 will be evaluated to undefined.
try this
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003";
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
testObject.method2();
}
},
method2: function() {
testObject.method1();
}
}
updated to match your current question: good part is you can add additional functions and call any one of them with this method;
var testObject = {
method1 : function() {
var connectionAddr = "ws://localhost:8003",
self = this;
socket = new WebSocket(connectionAddr);
socket.onmessage = function(event) {
self['method2']();
}
},
method2: function() {
this['method1']();
}
}
I have a problem with variable scope. I am setting event listeners (onclick), but the handler is method of an object and I need to refer to the object within the handler method.
Example:
var FOO = function () {
this.clicked = false
};
FOO.prototype.handler = function(e)
{
this.clicked = true;
}
FOO.prototype.setListeners = function()
{
$("#but").click(this.handler);
}
var oop = new FOO();
oop.setListeners();
Example works to the point this.clicked = true; where because this doesn't refer to the oop.
How do I pass a reference of the object to the handler function?
FOO.prototype.setListeners = function()
{
var that = this;
$("#but").click(function(){that.handler();});
}
In the SomeObj object, the onkeydown event handler this.doSomething is called in the wrong context (that of the textbox element) but it needs to be called in the context of this. How can this be done?
function SomeObj(elem1, elem2) {
this.textboxElem = elem1;
this.someElem = elem2;
this.registerEvent();
}
SomeObj.prototype = {
registerEvent: function() {
this.textboxElem.onkeydown = this.doSomething;
},
doSomething: function() {
// this must not be textboxElem
alert(this);
this.someElem.innerHTML = "123";
}
};
Copy the reference to a local variable, so that you can use it in a closure:
registerEvent: function() {
var t = this;
this.textboxElem.onkeydown = function() {
t.doSomething();
};
},