# Techniques for removing Event Listeners

Event listeners in Javascript are a powerful way for the code that we write to plug into user interactions, page events, and a whole host of useful events. Adding event listeners into our code is straightforward and can help us to do some really interesting things. However, removing event listeners can be a little more tricky. You can almost guarantee that we are all using web apps that are running lots of superfluous code just because some event listener hasn't been removed properly. Most of the time, neither the user nor the developer notices anything so the `Event Listener` will stay hidden forever.

In some scenarios, not removing event listeners can cause lots of unpredictable behaviour, making our code really difficult to debug. This is especially true as our code bases get bigger and we have lots of different classes and objects that might register their own event listeners.

In this article, we will explore a simple `Square` class that adds a red square to the DOM. When the red square is shown in the document, the class should also use the user's mouse actions to determine what color to set as the main `backgroundColor` of the app. If the user hovers over the app, the `backgroundColor` will be changed to yellow.

<iframe height="800" width="100%" src="https://codepen.io/anjalani/embed/bGJwvqp?default-tab=js%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/bGJwvqp">
  Techniques for removing Event Listeners: Part 1</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

*Moving the mouse in and out of the app before the red square is drawn to the screen should do nothing as there have not been any event listeners registered yet to handle this behaviour. Once the red square is available, the background color of the app should change as we move in and out of the app. Finally, once the red square has been removed from the document, we have completely destroyed this class and the reference to it. However, the event listeners are still there and have escaped garbage cleanup.*

### Trying with `removeEventListener()`

This is probably the most obvious approach to remove any listeners, but also the approach that is going to give you the most headaches.

Let's understand how the `EventTarget.removeEventListener()` method works. Simply put it removes an event listener from the target that was previously registered with `EventTarget.addEventListener()`. The event listener is identified using a combination of the event type, the event listener function and the optional options parameter.

```Javascript
removeEventListener(type, listener)
removeEventListener(type, listener, options)
removeEventListener(type, listener, useCapture)
```

For `EventTarget.removeEventListener()` to remove an event listener, the parameters passed to the method have to be exactly the same as the parameters that were passed to the original `EventTarget.addEventListener()`.

Here we have to be really careful. Looking at the modified code below, you can see that we have added two `removeEventListener()` method calls to the `destroy()` method.

<iframe height="300" width="100%" src="https://codepen.io/anjalani/embed/rNbMdmJ?default-tab=js%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/rNbMdmJ">
  Techniques for removing Event Listeners: Part 2</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

*Adding* `removeEventListener()` to the `destroy()` method has not removed the event listeners as we still get a yellow background each time we mouse over the app

At a first glance, it looks as though we have used exactly the same `type` and `event listener function` to add and remove the event. However, this is not really the case as the functions that are added in the `registerListeners()` method are in fact different functions from the functions that we are trying to remove in the `destroy()` method.

The way to get around this problem is by either using a class method or property as the event listener function.

```javascript
setYellow(event){
    appEl.style.setProperty('background color, 'yellow');
 }
```

or

```javascript
setYellow = (event) => {
    appEl.style.setProperty('background color, 'yellow');
 }
```

and then we can do the following to correctly remove the event listeners.

```javascript
appEl.addEventListener('mouseenter', this.setYellow);
appEl.removeEventListener('mouseenter', this.setYellow);
```

If we were not doing all this inside a class and just inside/outside a function then we could have done it as follows.

```javascript
function setYellow(event){
    appEl.style.setProperty('background color, 'yellow');
 }
```

or

```javascript
const setYellow = (event) => {
    appEl.style.setProperty('background color, 'yellow');
 }
```

and then removed the event listeners with

```javascript
appEl.addEventListener('mouseenter', setYellow);
appEl.removeEventListener('mouseenter', setYellow);
```

<iframe height="300" width="100%" src="https://codepen.io/anjalani/embed/KKYgoqm?default-tab=js%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/KKYgoqm">
  Techniques for removing Event Listeners: Part 3</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

### The sometimes useful `{once: true}` option

This pretty much speaks for itself.

```javascript
EventTarget.addEventListener(type, listener, {once: true});
```

When we use the `{once: true}` option when adding an event listener, the listener will be invoked at most once and immediately removed as soon as the event is invoked.

This is really useful for things like submit buttons, but the kind of interaction that we are implementing in this example won't be very helpful.

<iframe height="300" width="100%" src="https://codepen.io/anjalani/embed/ZEZpxJa?default-tab=js%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/ZEZpxJa">
  Techniques for removing Event Listeners: Part 4</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

*Using* `{once: true}` gives us the desired behaviour once and only once

### Removing the element from the DOM

It is worth noting that if the event listeners have been added to an element that we remove from the DOM, then the event listeners will be automatically destroyed anyway.

Similarly, it is possible to manually remove an element from the DOM and then add it back in without its event listeners attached. We can use the `Node.cloneNode()` method to clone an element which will create a copy of the original element but will not carry the event listeners from the original element. Removing the original element and replacing it with the clone will have effectively removed all the event listeners from the element.

Whilst, this is an approach that might feel a little heavy-handed, there might be scenarios where this becomes very useful because we are trying to control a part of the code base that is just not very easy to access or is controlled by third-party libraries.

### Using `AbortController()` with `addEventListener()`

If you're anything like me, you have probably only heard about using `AbortController()` for cancelling a network request. However, after hearing a developer at work mention being able to use `AbortController()` to cancel event listeners, thought I would dive in and try out an example.

The approach is by passing in a control signal to the options when an event listener is added.

```plaintext
const controller = new AbortController();
myElement.addEventListener('mouseenter', () => {}, {signal: controller.signal});
controller.abort();
```

There are several benefits to using this approach.

Firstly, we don't need to pass a function reference to the `addEventListener()` method which means that we don't need to keep track of which listeners were attached. Even where it can be nice to use a function reference to keep our code clean or reusable, we might be trying to cancel the event listener from a different point in our code base which can make this relatively simple task very difficult.

Secondly, we can use a single method to cancel multiple listeners attached to the same document element or different elements.

```plaintext
const controller = new AbortController();
const {signal} = controller;
myElement.addEventListener('mouseenter', () => {}, {signal});
myElement.addEventListener('mouseleave', () => {}, {signal});
otherElement.addEventListener('click', () => {}, {signal});

controller.abort();
```

Let's use this approach to change how our `Square` class registers its event listeners.

<iframe height="300" width="100%" src="https://codepen.io/anjalani/embed/poBELWw?default-tab=js%2Cresult&amp;editable=true&amp;theme-id=dark">
  See the Pen <a href="https://codepen.io/anjalani/pen/poBELWw">
  Techniques for removing Event Listeners: Part 5</a> by Anja Lani (<a href="https://codepen.io/anjalani">@anjalani</a>)
  on <a href="https://codepen.io">CodePen</a>.
</iframe>

*This approach feels as though it gives us the most flexibility in adding and removing event listeners. I'm gonna try and get in the habit of using this whenever I register event listeners in the future.*

### What are your thoughts?

Share what you think in the comments.

Do you think we should all abandon `removeEventListener()` and just use `AbortController()`?
