Class Methods as Event Handlers in JavaScript

Class methods as event handlers in JavaScript?

ClickCounter = function(buttonId) {
this._clickCount = 0;
var that = this;
document.getElementById(buttonId).onclick = function(){ that.buttonClicked() };
}

ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}

EDIT almost 10 years later, with ES6, arrow functions and class properties

class ClickCounter  {
count = 0;
constructor( buttonId ){
document.getElementById(buttonId)
.addEventListener( "click", this.buttonClicked );
}
buttonClicked = e => {
this.count += 1;
console.log(`clicked ${this.count} times`);
}
}

https://codepen.io/anon/pen/zaYvqq

How adding event handler inside a class with a class-method as the callback?

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);

How does one access a class instance's `this` context from within an event handler without using `bind`?

In case of an instance method (own or prototypal) being used as event handler without explicit this binding one has to implement such a handler as arrow function in order to directly access the instantiation time's (enclosed) this context.

class A_Test {
constructor(a, b) {
this.selector = '.foam-types';
}
registerFoamTypeRadios() {
$(this.selector).on('click', '[name="foam-type"]', this.selectFoamType);
// mouseenter: this.showFoamTypeExample,
// mouseleave: this.hideFoamTypeExample,
}
selectFoamType = (evt) => {
const elm = evt.currentTarget;
const srcElm = evt.target;
console.log({ this: this, className: this.constructor.name, elm, srcElm });
}
showFoamTypeExample = (evt) => {}
hideFoamTypeExample = (evt) => {}
}
var theTest = new A_Test();

theTest.registerFoamTypeRadios();
* { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<fieldset class="foam-types">
<legend>Foam Types</legend>

<label class="foam-type-option">
<input type="radio" name="foam-type" value="expanding"/>
<span>Expanding Foam</span>
</label>
<label class="foam-type-option">
<input type="radio" name="foam-type" value="shaving"/>
<span>Shaving Foam</span>
</label>
</fieldset>

DOM event handling with ES6 class methods

ES6 itself doesn't have the solution you'd like, but you can use ES7 Class Properties:

class foo {
constructor() {
document.body.addEventListener('click', this.clickHandler, false );
}

clickHandler = event => {
// do stuff
document.body.removeEventListener('click', this.clickHandler);
}
}

clickHandler, being a fat-arrow function, has 'this' context bound to the class instance, which is exactly what you want

ES6 Classes/Object oriented and Event Handlers

JavaScript events are bound to the document object model (DOM) and aren't bound to any arbitrary object you might make.

https://developer.mozilla.org/en-US/docs/Web/API/Event

The Event interface represents any event which takes place in the DOM; some are user-generated (such as mouse or keyboard events), while others are generated by APIs (such as events that indicate an animation has finished running, a video has been paused, and so forth).

So knowing that, we'd want to have some sort of DOM element in your class. It makes sense to connect your class which represents a physical object to the actual element - the class is just another presentation of the physical state of the DOM. Yes, you are correct that any code placed in the constructor will be executed - including any addition of event handlers.

If you wanted to create listeners as methods on the class you'd do something like:

class {
constructor() {
this.domElement.addEventListener('click', this.handler.bind(this));
}

handler() {
this.hello();
}

hello() {
}
}

It's crucial that you remember that the this scope has to be fixed via passing in a bound function call since it loses all context when passed in as a listener. The binding is not necessary if in the above code you were to not use any methods of the class, and it just becomes this.domElement.addEventListener('click', this.handler);.

This is obviously not the only way to add the handler but seems to me the more sane way when dealing with classes that represent the DOM.

How to set the class method as event handler?

You may try with camelCase on naming autoComplete.

    let autoComplete = new Autocomplete('/autocomplete', 'query');
<input oninput="autoComplete.requestSuggestions();" placeholder="Product search" required type="search" value="">

It seems autocomplete this word is conflicting with the HTML element on the form there.

Furthermore, camelCase is more a standard naming convention in javascript also.

How to define event handler as elements class method?

ES7+ way

class CustomElement extends HTMLElement {
onclick = () => {...}
}



Related Topics



Leave a reply



Submit