Reputation: 430
How do I add an event handler inside a class with a class-method as the callback?
<div id="test">move over here</div>
<script>
oClass = new CClass();
function CClass()
{
this.m_s = "hello :-/";
this.OnEvent = OnEvent;
with(this)
{
var r = document.getElementById("test");
r.addEventListener('mouseover', this.OnEvent); // this does NOT work :-/
}
function OnEvent()
{
alert(this); // this will be the HTML div-element
alert(this.m_s); // will be undefined :-()
}
}
</script>
Yes I know some quirks to make it work but what would be the intended way when these event handlers were introduced ??? I again have the bitter feeling, that no-one truly lives OOP :-(
Here for you to play: https://jsfiddle.net/sepfsvyo/1/
Upvotes: 34
Views: 37802
Reputation: 16281
The answer from @ibrahimmahrir does the job, but I wanted to consolidate a few points.
As many JavaScript developers struggle to understand, the this
keyword is a moving target. In traditional OOP languages, an object method is exclusive to the object, so this
is defined as the object to which it is attached.
JavaScript functions are more promiscuous, and can be attached to multiple objects. In JavaScript, this
refers to the object which is currently invoking the function, not necessarily the one to which it was originally attached.
For an Event Handler function, the invoking object is the element to which it is attached, not the original object; thus this
refers to the element. The usual safe method is to store a reference to the original object in a different variable, often called that
:
oClass = new CClass();
function CClass() {
var that = this; // a reference to the original object
this.m_s = "hello :-/";
this.OnEvent = OnEvent;
var r = document.getElementById("test");
r.addEventListener('click', this.OnEvent);
function OnEvent() {
alert(that); // this is now the object
alert(that.m_s); // this works
}
}
The comments above are my updated comments. I have also removed the with
statement which wasn’t contributing much and which is seriously discouraged.
Oh, and I have changed the event to click
to make it easier to test.
While we’re on the confusion with this
, it is not necessarily the element which started things off. Suppose we now include a span
:
<div id="test">click <span>over</span> here</div>
Clicking on the span
will trigger the event listener, even though the you didn’t actually click on the div
to which it is attached. In this case the event is bubbled from the span to the div.
Here this
refers only to the div
element with the event listener. If you want to reference the span
, you will need event.target
:
function OnEvent(event) { // include event parameter
alert(this); // the element attached
alert(event.target); // the element clicked
alert(that); // this is now the object
alert(that.m_s); // this works
}
Here is an updated fiddle: https://jsfiddle.net/osk083xv/
Upvotes: 2
Reputation: 31682
The this
inside the event listener callback will be the element that fired the event. If you want the this
to be the instance of your class, then either:
Bind the function to the class instance:
Using Function.prototype.bind
, will create a new function that its this
value will always be what you specify it to be (the class instance):
r.addEventListener('mouseover', this.OnEvent.bind(this));
// ^^^^^^^^^^^
Wrap the function inside an anonymous function:
var that = this;
r.addEventListener('mouseover', function(ev) { that.OnEvent(ev); });
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
or use an arrow function (so no need for that
):
r.addEventListener('mouseover', ev => this.OnEvent(ev));
// ^^^^^^^^^^^^^^^^^^^^^^
Note: As mentioned in a comment bellow, both of the above methods pass a different function to addEventListener
(the one with bind
create a new function, and the anounimous function is obviously !== this.OnEvent
). If you are going to remove the event listener later, you'll have to store a reference to the function:
var reference;
r.addEventListener('mouseover', reference = this.OnEvent.bind(this));
// ^^^^^^^^^^^^
or:
var reference;
var that = this;
r.addEventListener('mouseover', reference = function(ev) { that.OnEvent(ev); });
// ^^^^^^^^^^^^
then you can remove the event listener like:
r.removeEventListener('mouseover', reference);
Upvotes: 64
Reputation: 508
You can actually return the object as an EventListener callback, this way JS will search for an handleEvent
method in the class and execute accordingly :
var myInstance = new myClass;
myInstance.addEventListener("mousedown",myInstance);
// To remove the event you can follow the same pattern
myInstance.removeEventListener("mousedown",myInstance);
You have to construct your class this way :
class myClass {
constructor(){
// Whatever this is supposed to do.
// You can also add events listener within the class this way :
this.addEventListener("mousedown",this);
}
mouseDownEvent(e)(){
// Some action related to the mouse down event (e)
console.log(e.target);
}
mouseMoveEvent(e)(){
// Some action related to the mouse move event (e)
}
mouseUpEvent(e)(){
// Some action related to the mouse up event (e)
}
handleEvent(e) {
switch(e.type) {
case "mousedown":
this.mouseDownEvent(e);
break;
case "mousemove":
this.mouseMoveEvent(e);
break;
case "mouseup":
this.mouseUpEvent(e);
break;
}
}
}
Sources :
https://www.thecssninja.com/javascript/handleevent
https://metafizzy.co/blog/this-in-event-listeners/
I find this method clearer, also while declaring events inside the class this
is pretty explicit.
Hope I helped someone.
Upvotes: 15