What is the reason `addEventListener` does not work with a class method?

Recently, I discovered that the listener passed to addEventListener can actually be an object with a handleEvent function instead of just a callback function (here).

However, I encountered an issue when trying to use handleEvent as a class method:

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

This resulted in the error message:

Uncaught TypeError: Class constructor Foo cannot be invoked without 'new'

To bypass this, I could simply pass in Foo.handleEvent instead. But out of curiosity and for educational purposes, I'm wondering why static handleEvent doesn't work?

Answer №1

A realization dawned on me!

Guess what? The addEventListener function has the ability to accept either a function or an object with a handleEvent method.

Here's the interesting part - Foo is indeed an object (Foo instanceof Object evaluates to true) but at the same time, it's a constructor, essentially a function.

When utilizing addEventListener, it sees Foo as some type of function and treats it like any other function by attempting to execute Foo().

However, this approach results in failure because Foo is actually a constructor function that requires the use of new for invocation. Unfortunately, addEventListener doesn't initially verify if Foo is also an object containing a handleEvent method.

Answer №2

Within the scope of Section 1.3.1 in the W3C Document Object Model Events specification, there is a reference to an "IDL interface" for the listener:

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

In the Web IDL Standard specification, section 2.5.7 articulates:

It varies based on language bindings whether calling a static operation or accessing a static attribute through an instance of the interface is allowed

Based on this information, it seems that the language binding implementation, responsible for passing events to a Javascript listener, opted not to support calls to "static operations."

As an illustration, there exists an unresolved bug report requesting Mozilla's implementation to enable the invocation of static methods here, which is also discussed here

Answer №3

To create an instance of the class, you must use the new keyword. Here is an example:

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

If you don't include the new keyword, a function that is meant to be used as a constructor will simply execute the function without creating a new instance.

The ecmascript interpreter requires the new keyword for proper instantiation of a class. This behavior is inherent to the language, and not specific to TypeScript or EventTarget/addEventListener.

When using addEventListener, if the function provided is recognized as a constructor (which Foo is), it will be invoked. However, since the interpreter sees it as a function and not specifically as a class, it won't call the static handleEvent method.

typeof Foo
'function'

Answer №4

Take a look at this playground, numerous "click" events are happening with or without using the abstract keyword before defining the class

If you prefer not to instantiate a new class using the new keyword (as indicated by your error), you can transform the targeted class into an abstract class which allows direct access without creating a new instance

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

Even though the method is static, it should still function properly by accessing the "handleEvent" method directly

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 instance, you would simply need to access prototype following Foo in the same manner

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Grunt: Executing tasks exclusively for updated files - the ultimate guide!

In my Gruntfile.js, I have the configuration set up as follows: module.exports = function(grunt) { require('jit-grunt')(grunt); grunt.initConfig({ uglify: { options: { manage: false }, my_target: { file ...

The width of the Ion-slide is dynamically determined by the styling

After transitioning my Ionic 2 project to Ionic 3, I encountered issues with ion-slides which are affecting my app's functionality. Upon app loading, specific widths are being defined in the style tags on the slides, disrupting the intended styling. ...

What is the best way to access my backend API on a web hosting platform?

To successfully send information from a contact form through an email, I need to access my backend API. My app is deployed on a webhost called Kinghost and I have two URLs provided: the first one is the generic mywebaddr.com:port-number, and the second one ...

How can I make TypeScript mimic the ability of JavaScript object wrappers to determine whether a primitive value has a particular "property"?

When using XMLValidator, the return value of .validate function can be either true or ValidationError, but this may not be entirely accurate (please refer to my update). The ValidationError object includes an err property. validate( xmlData: string, opti ...

Filtering an array of objects based on a specific condition in TypeScript

I am trying to filter the array object data where the count of Failed is greater than 0. Unfortunately, the code below is not working as expected. ngOnInit() { this.employeeService.getProducts().subscribe((data:any) => { console.log(data); this. ...

Setting up Jplayer as an audio player: A step-by-step guide

I am looking to incorporate a Jplayer audio player into my project, but I am struggling to find any documentation or resources that provide instructions on what components to include and how to set it up. If anyone has experience with using the Jplayer au ...

Is there a way to send the image object to the onclick function as it is being assigned?

I apologize if my question is a bit unclear, as I am currently teaching myself how to use javascript. I am working on generating image thumbnails dynamically and would like the ability for users to enlarge the image when they click on the thumbnails. The p ...

What is the best way to determine the height of a DIV element set to "auto"?

When setting a fixed height on a div using jQuery, such as $('div').height(200);, the value of $('div').height() will always be 200. This remains true even if the content within the div exceeds that height and overflow is hidden. Is th ...

What is the way to display the final list item when clicking in jQuery?

I am attempting to achieve a specific behavior where clicking on a button will trigger the content below to scroll in such a way that only the last item in the list is visible. I have been using jQuery for this functionality, but unfortunately, it is not ...

Looking for guidance on sending data from a JS file to HTML using Nodejs? Seeking advice on various modules to achieve this task effectively? Let's

Looking for advice on the most effective method to transfer data from a JS file (retrieved from an sqlite db) to an HTML file in order to showcase it in a searchable table. My platform of choice is NodeJS. As a beginner, I am willing to put in extra time a ...

Having trouble locating an element, Sammy?

I am encountering an issue while using Sammy for my SPA. The error message I receive is: [Sun Mar 29 2020 17:37:19 GMT+0300 (Eastern European Summer Time)] #main 404 Not Found get / Error: 404 Not Found get / at Sammy.Application.error (sammy-latest ...

Encounter an Unexpected Token Issue when using NextJS-auth0 in Jest

I am facing a problem with my Next.js app that is integrated with the nextjs-auth0 package. Whenever I attempt to test a particular file and include the following import: import { getSession } from '@auth0/nextjs-auth0'; An error occurs, stating ...

Is there a way for me to retrieve the widths of all child elements within an HTML document?

I have been working on a JavaScript (jQuery) function to calculate the maximum width of all child and children's-child elements within a specific div element. Here is the code I have written so far: function setBodyMinWidth(name){ var max = 0; $(nam ...

Discover specific element details using the id with Strapi and React

I am currently studying react with strapi and I have encountered an issue. I have successfully displayed all elements from a database, but I am facing a problem when trying to display specific information on clicking an element. Although I can retrieve t ...

Prevent the event listener from continuously triggering

I have a situation where every time I create an Angular component, an event listener is added. However, upon leaving the page and returning to it, a new event listener is added because the constructor is called again. The problem arises when this event is ...

Tips for transforming a UTF16 document into a UTF8 format using node.js

My dilemma involves an xml file that is encoded in UTF16, and I need to convert it to UTF8 for processing purposes. When I use the following command: iconv -f UTF-16 -t UTF-8 file.xml > converted_file.xml The conversion process goes smoothly with the ...

Extracting JSON data within ajax's success callback

I am currently working on parsing and displaying JSON data that is returned from a server. To accomplish this, I have set up an AJAX call that reads user input, sends it to a PHP page via POST method, and the PHP page var_dumps the array containing the JSO ...

Encountered issues loading JavaScript and received a pyppeteer error while trying to access a website through requests

I am facing a challenge when trying to scrape a webpage post login using BeautifulSoup and requests. Initially, I encountered a roadblock where the page requested JavaScript to be enabled to continue using the application. To work around this issue, I de ...

Guide on bringing in Typescript definition that exports a lone variable

The "@types/dd-trace" library defines a single variable called trace in the type definition for the "dd-trace" library. declare var trace: TraceProxy; export = trace; declare class TraceProxy extends Tracer { /** * Initializes the tracer. This s ...

Implement a timeout feature for npm clickhouse queries

Is there a way to set the timeout for query execution in the following code snippet? I attempted to use the timeout property, but it does not seem to be functioning as expected. const results = await clickhouse.query(query, { timeout: 50, }).toPromise(); ...