I recently learned that the listener passed to addEventListener
can be an object with a handleEvent
function, instead of just a callback function. (https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#listener)
However, this doesn’t seem to work when handleEvent
is a class method:
class Foo {
static handleEvent() {
console.log(`clicked`);
}
}
document.body.addEventListener(`click`, Foo);
This throws:
Uncaught TypeError: Class constructor Foo cannot be invoked without ‘new’
This is trivial to work around — I can simply pass in Foo.handleEvent
instead. But for my own education, I’m wondering why static handleEvent
doesn’t work?
5
Answers
It hit me!
addEventListener
can accept either a function or an object with ahandleEvent
method.Foo
is an object --Foo instanceof Object
istrue
-- but it's also a constructor, which is a function.So,
addEventListener
"sees" thatFoo
is some kind of function and treats it like any other function. That is: it tries to callFoo()
.This fails, since
Foo
is a constructor function and so cannot be called withoutnew
.addEventListener
doesn't first check whetherFoo
is also an object that happens to have ahandleEvent
method.Have you actually tried to run your code ?
Classes are treated differently the object by the
addEventListener
method.You have to instantiate an instance of the class. This would work:
Without the
new
keyword, calling a function that is intended to be used as a constructor will simply execute the function without creating a new instance.Basically, the ecmascript interpreter doesn’t know how to properly handle instantiation of a class without the
new
keyword. It’s just a feature of the language, and not specific to TypeScript or EventTarget/addEventListener.Since
Foo
is a constructor,addEventListener
just sees that it is a function and will attempt to call it. It doesn’t know that it’s a class, so it doesn’t attempt to call the statichandleEvent
method.Check out this playground, there are "click" logs being fired left and right with or without adding the
abstract
keyword before the class definitionif you don’t want to handle instantiating a new class via the
new
keyword (such as your error indicates) then you’d turn the targeted class into an abstract class which allows you to simply key-into the class without instantiating a new instanceSince your method is
static
however, it should work regardless by keying into the "handleEvent" methodif you were to change the method from
static
topublic
for example, you’d simply have to key-intoprototype
followingFoo
as followsSection 1.3.1 of the W3C Document Object Model Events specification descibes an "IDL interface" of the listener:
The Web IDL Standard specification, section 2.5.7, says:
It therefore looks to me like the implementation of the language binding, which supplies the event to a Javascript listener, decided not to allow calls to "static operations".
For example, there is an open bug report here asking for Mozilla’s implementation to be capable of calling static methods here, which is mentioned here