When Event Listeners Won't Listen 
Tags: javascript, remove, eventlistener
Published: 2020-01-02 21:09:34

As part of my first Javascript project at Flatiron. My partner and I decided to recreate Microsoft Paint using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial">Canva HTML Element</a> and manipulating it via Javascript. The canva element is packed with tons of attributes that we can change, from stroke size to color. We simply attach an event listener to the Canva element, and it will populate with color corresponding with our mouse movements.

<img src="https://i.imgur.com/9PLaN6l.gif" width="100%"></img>
<i> Console logging the mouse x-y coordinates. (Smiley face is unintentionally creepy). 

Each color inside the color wheel is a contains an id with its respective color. i.e. yellow button = #yellow. Therefore, by adding a "click" event listener, we can assign the stroke color to the event.target.id without any issues.

The project was straight-forward until we decided to implement an eraser. Rather than assigning the stroke color to "white" when a user clicks the "eraser" button, we wanted a true eraser functionality for more flexibility later on in the project. After some searching, we found a solution <a href="https://codepen.io/progrape/pen/XXBwWe">here</a> for the eraser. 

<img src="https://i.imgur.com/paPYYyk.gif" width="100%"></img> 

The issue arose once we tried to revert back to color. Although the appropriate stroke settings were being reassigned (we know via console logging). The eraser function was also adding an event listener to the canva, and using the same mouse behaviors: <b>mousedown</b>, <b>mousemove</b>, and <b>mouseout</b>. Therefore creating event listener conflicts.

<b>mousedown:</b> The mousedown event is fired at an Element when a pointing device button is pressed while the pointer is inside the element.

<b>mousemove:</b> The mousemove event is fired at an element when a pointing device (usually a mouse) is moved while the cursor's hotspot is inside it.

<b>mouseout:</b> The mouseout event is fired  at an Element when a pointing device (usually a mouse) is used to move the cursor so that it is no longer contained within the element or one of its children.

The event listeners within the eraser function were assigning new stroke attributes and these were conflicting with the stroke attributes that were defined within the coloring function. Therefore switching back and forth between coloring and erasing was very buggy. Notice how we are console logging "drawing" and "erasing" at the same time.

<img src="https://im3.ezgif.com/tmp/ezgif-3-a2646fbf57bc.gif" width="100%"></img>

At this point, I looked into removing event listeners and found the following example from <a href="https://www.w3schools.com/jsref/met_element_removeeventlistener.asp">W3Schools</a>:

```javascript
document.getElementById("myDIV").addEventListener("mousemove", myFunction);

document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
```

In my case, myFunction would be myEraser. However, inside myEraser I had a lot of event listeners with anonymous functions (notice how there is no function name associated with mousemove).

```javascript
 function myEraser () {       
        canvas.addEventListener('mousemove', (e) => {
        if (isPress) {
      
            let x = e.offsetX;
            let y = e.offsetY;
            ctx.globalCompositeOperation = 'destination-out';
    
            ctx.beginPath();
            ctx.arc(x, y, 10, 0, 2 * Math.PI);
            ctx.fill();
            ctx.lineWidth = 20;
            ctx.moveTo(old.x, old.y);
            ctx.lineTo(x, y);
    
            old = {x: x, y: y};
        }
        });
```

I refactored all my of code and created a callback function for each event listener inside of myEraser(). i.e.:

```javascript
    function myEraser() {
        canvas.addEventListener("mousemove", eraseMouseMove) 
    }


   function eraseMouseMove(e) {
        let x = e.offsetX;
        let y = e.offsetY;
        ctx.globalCompositeOperation = "destination-out";
        ctx.beginPath();
        ctx.arc(x, y, 10, 0, 2 * Math.PI);
        ctx.fill();
        ctx.lineWidth = 20;
        ctx.moveTo(old.x, old.y);
        ctx.lineTo(x, y);
        old = { x: x, y: y };
        console.log("Eraser is active")
    }
```

Now I can directly remove the listener that I want by calling the function that corresponds with it. I will be adding this line of code inside the event listener handling clicks for the color buttons the user can select from:

```javascript
canvas.removeEventListener("mousemove", eraseMouseMove);
```


Final product (notice how we are only console logging the appropriate action).

<img src="https://i.imgur.com/ytwygCZ.gif" width="100%"></img>

The final code serves as on and off switch, depending on which button the user clicks:

```javascript
  document.querySelector("#eraser").addEventListener("click", (e) => {
    // Remove drawing event listeners 
      canvas.removeEventListener("mousedown", drawingMouseDown);
      canvas.removeEventListener("mousemove", drawinngMouseMove);
      canvas.removeEventListener("mouseup", drawingMouseUpAndOut);

    // Add eraser event listeners 
      canvas.addEventListener("mousedown", eraseMouseDown);
      canvas.addEventListener("mousemove", eraseMouseMove);
      canvas.addEventListener("mouseup", eraseMouseUp);
  })

  
  document.querySelector("#color").addEventListener("click", (e) => {
    // Remove erasing event listeners
      canvas.removeEventListener("mousedown", eraseMouseDown);
      canvas.removeEventListener("mousemove", eraseMouseMove);
      canvas.removeEventListener("mouseup", eraseMouseUp);

    // Add drawing event listeners 
      canvas.addEventListener("mousedown", drawingMouseDown);
      canvas.addEventListener("mousemove", drawinngMouseMove);
      canvas.addEventListener("mouseup", drawingMouseUpAndOut);

    });
  }
```