I thought I would be able to dispatch the test event by doing window.dispatchEvent(test). In fact, if I change the event listener from test to click, it fires the alert.
What am I doing wrong?
var functionName = function () {
function functionName() {
// bind method
this.doSomething = this.doSomething.bind(this);
this.setupListeners();
}
functionName.prototype.setupListeners = function setupListeners() {
window.addEventListener('test', this.doSomething);
};
functionName.prototype.doSomething = function doSomething(event) {
alert('Hello!');
};
return functionName;
}();
You have to create the event object like this:
var event = new Event('test');
// Dispatch the event.
window.dispatchEvent(event);
See the working snippet:
var functionName = function() {
function functionName() {
// bind method
this.doSomething = this.doSomething.bind(this);
this.setupListeners();
}
functionName.prototype.setupListeners = function setupListeners() {
window.addEventListener('test', this.doSomething);
};
functionName.prototype.doSomething = function doSomething(event) {
alert('Hello!');
};
return functionName;
}();
var myfun = new functionName();
var event = new Event('test');
// Dispatch the event.
window.dispatchEvent(event);
Related
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();
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);
}
)
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();});
}
Basically, if you mock an object using Sinon.js which has a property that's passed as callback to jQuery.delegate your mock expectations will fail when you trigger the method being observed in jQuery.delegate. If you execute the method inside of the body of an anonymous function that you use as the callback to jQUery.delegate your expectations will pass.
test("FAILS: Handler is invoked as callback from jQuery delegate method directly...", function() {
var Controller,
View,
$el,
c,
v,
mock;
Controller = function() {
var self = {};
self.handler = function(e) {
console.log("got handled");
};
return self;
};
View = function($el, controller) {
var self = {};
self.render = function() {
$el.html("");
$el.delegate("#derp", "keyup", controller.handler);
};
return self;
};
$el = jQuery("");
c = new Controller();
v = new View($el, c);
mock = this.mock(c);
v.render();
mock.expects("handler").once();
$el.find("input").val("bar").trigger("keyup");
equal($el.find("input").val(), "bar"); // passes!
mock.verify();
});
test("WINS: Handler is invoked inside anonymous function...", function() {
var Controller,
View,
$el,
c,
v,
mock;
Controller = function() {
var self = {};
self.handler = function(e) {
console.log("got handled");
};
return self;
};
View = function($el, controller) {
var self = {};
self.render = function() {
$el.html("");
$el.delegate("#derp", "keyup", function(e) {
controller.handler(e);
});
};
return self;
};
$el = jQuery("");
c = new Controller();
v = new View($el, c);
mock = this.mock(c);
v.render();
mock.expects("handler").once();
$el.find("input").val("bar").trigger("keyup");
equal($el.find("input").val(), "bar"); // passes!
mock.verify();
});
Am I doing something wrong here?
Thanks,
Erin
This is a classic callback scope gotcha. Function arguments are out of scope, so controller does not have a handler method since it is not defined locally. The anonymous function gives it scope because functions control scope in JavaScript.
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();
};
},