skip to Main Content

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


  1. Chosen as BEST ANSWER

    It hit me!

    addEventListener can accept either a function or an object with a handleEvent method.

    Foo is an object -- Foo instanceof Object is true -- but it's also a constructor, which is a function.

    So, addEventListener "sees" that Foo is some kind of function and treats it like any other function. That is: it tries to call Foo().

    This fails, since Foo is a constructor function and so cannot be called without new. addEventListener doesn't first check whether Foo is also an object that happens to have a handleEvent method.


  2. Have you actually tried to run your code ?

    class Foo {
        static handleEvent() {
            console.log(`clicked`);
        }
    }
    document.body.addEventListener(`click`, Foo);
    
    Uncaught TypeError: Class constructor Foo cannot be invoked without 'new'
    

    Classes are treated differently the object by the addEventListener method.

    Login or Signup to reply.
  3. You have to instantiate an instance of the class. This would work:

    class Foo {
        static handleEvent() {
            console.log(`clicked`);
        }
    }
    document.body.addEventListener(`click`, new Foo());
    

    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 static handleEvent method.

    typeof Foo
    'function'
    
    Login or Signup to reply.
  4. Check out this playground, there are "click" logs being fired left and right with or without adding the abstract keyword before the class definition

    if 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 instance

    abstract class Foo {
        static handleEvent() {
           return console.log(`clicked`);
        }
    }
    document.body.addEventListener(`click`, Foo.handleEvent);
    

    Since your method is static however, it should work regardless by keying into the "handleEvent" method

    class Foo {
        static handleEvent() {
           return console.log(`clicked`);
        }
    }
    document.body.addEventListener(`click`, Foo.handleEvent);
    

    if you were to change the method from static to public for example, you’d simply have to key-into prototype following Foo as follows

    class Foo {
        public handleEvent() {
           return console.log(`clicked`);
        }
    }
    document.body.addEventListener(`click`, Foo.prototype.handleEvent);
    
    Login or Signup to reply.
  5. Section 1.3.1 of the W3C Document Object Model Events specification descibes an "IDL interface" of the listener:

    // Introduced in DOM Level 2:
    interface EventListener {
      void               handleEvent(in Event evt);
    };
    

    The Web IDL Standard specification, section 2.5.7, says:

    It is language binding specific whether it is possible to invoke a
    static operation or get or set a static attribute through a reference
    to an instance of the interface

    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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search