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
bind
ing 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
What Is "Context" in Jquery Selector
How to Delete Document from Firestore Using Where Clause
Angular2:Render a Component Without Its Wrapping Tag
Save File JavaScript with File Name
Keys in JavaScript Objects Can Only Be Strings
Style.Display='None' Doesn't Work on Option Tags in Chrome, But It Does in Firefox
Make Vuejs and Jquery Play Nice
Why Is It Object.Defineproperty() Rather Than This.Defineproperty() (For Objects)
What Does Three Dots Do in Reactjs
Angular2/Spring Boot Allow Cross Origin on Put
How to Develop Desktop Apps Using HTML/Css/Javascript
Jquery - Sticky Header That Shrinks When Scrolling Down
Scrolltop Animation Without Jquery
Closing Websocket Correctly (Html5, JavaScript)
Remove Leading Zeros from a Number in JavaScript
How to Make <Input Type="Date"> Supported on All Browsers? Any Alternatives