Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

removeEventlistener not working as expected with arrow function and parameter [duplicate]

I've got a page which can hold multiple editable contents. I want to fire some kind of check event whenever the content is edited.

My code to achieve this looks like this:

// Find all editable elements.
let allEditableElements = document.querySelectorAll('[contenteditable="true"]');

for(let i = 0; i < allEditableElements.length; i++){

  //Remove eventListener to prevent duplicate events.
  allEditableElements[i].removeEventListener('input', (event) => {myClass.myEventMethod(event);}, false);

  // Add event.
  allEditableElements[i].addEventListener('input', (event) => {myClass.myEventMethod(event);}, false);
} 

Everything works fine so far. But as I said users can edit the content, which includes adding new editable contents to the page itself. At that point the events will be set again, which is why I'm trying to remove the event beforehand.

My question is why would the removeEventListener function not work as expected? And isn't there a way to name given events like so:

// With eventNameGivenByUser an event could be removed just by its name.
addEventListener('eventTriggerName', 'eventNameGivenByUser', function(), [, options]);

Of course I did some research and found out that the code itself would work like this:

// Find all editable elements.

let allEditableElements = document.querySelectorAll('[contenteditable="true"]');

for(let i = 0; i < allEditableElements.length; i++){

  //Remove eventListener to prevent duplicate events.
  allEditableElements[i].removeEventListener('input', myClass.myEventMethod, false);

  // Add event.
  allEditableElements[i].addEventListener('input', myClass.myEventMethod, false);
} 

However this is without passing parameters, which is mandatory in such a dynamic setup...

Hope someone will tell me that in 2017 there is a nice and decent way without using libraries.


Edit 08.02.2017:

Just for the curious ones: The Solution is to not pass any parameter to the listener:

// as you can see there is no (event) and no arrow function either.
addEventListener('input', myClass.myEventMethod, false);

All there is to do now is to call prepare the method like this:

// The Parameter will be passed through anyway!
myEventMethod(event) {

      /**
      * Do stuff with event parameter.
      */

};

After that the listener can be removed like so:

removeEventListener('input', myClass.myEventMethod, false);

Sidenote:

I'm using electron and do not need cross browser support. It just has to be compatible with Chromium: 56.0.2924.87.

Regards, Megajin

like image 813
Megajin Avatar asked Feb 08 '17 11:02

Megajin


1 Answers

The removeEventListener method takes at least two arguments: the event name and the listener function to remove.

In your first example:

  allEditableElements[i].removeEventListener('input', (event) => {myClass.myEventMethod(event);}, false);

you are defining a new function which wasn't previously attached as event listener, so it can not be removed.

I don't know what's wrong with your second attempt:

allEditableElements[i].removeEventListener('input', myClass.myEventMethod, false);

this one should work fine. However you could combine both approaches: wrap your class method in a function, and attach the wrapped version as an listener. You just need to have a reference to make it possible to remove later:

const inputListener = (event) => { myClass.myEventMethod(event); };
let allEditableElements = document.querySelectorAll('[contenteditable="true"]');


for(let i = 0; i < allEditableElements.length; i++){

  //Remove eventListener to prevent duplicate events.
  allEditableElements[i].removeEventListener('input', inputListener, false);

  // Add event.
  allEditableElements[i].addEventListener('input', inputListener, false);
} 

All that said I'd advise to just use event delegation. Attach the listener on an element higher up in the DOM and don't worry about clearing and re-adding event listeners when new elements appear:

const handleContentEditable = e => {
    if( e.target.isContentEditable ){
        console.log( 'editing ', e.target );
    }
};

document.body.addEventListener('input', handleContentEditable );

https://jsfiddle.net/rd3y9gh9/1/

like image 184
pawel Avatar answered Sep 30 '22 12:09

pawel