Solving the Mystery: Why addEventListener Fires Immediately in JavaScript & TypeScript

If you don't like using libraries for everything and you want to develop your own modal, this simple trick can save you some time.

When you want to open a modal on a button click and add a click listener to the page so your modal will close when you click outside of it, strangely the listener triggers as soon as you add it. But why? Your modal just showed up, you haven't clicked since then.

tl;dr

Just use `{capture: true}`

document.addEventListener('click', alert('hey'), {capture: true})

The detailed explanation

Let's create an example code

<style>  
  #button {
    background: red;
  }
</style>

<div id="button" onClick="document.addEventListener('click', () => alert('hello'))">
  Button
</div>

It's a basic example code to present the problem. When we click on the button, it registers an event listener on the document for clicks. After that it will show an alert when we click anywhere on the document.

But what happens? You click on the button, and the alert shows up immediately. Did we miss something? The code looks okay, but that's strange behavior.

So why did it happen? The answer is bubbling and capturing.

Bubbling and capturing

Bubbling goes from bottom to top. It goes from our button to the most outer layer (html). It first calls any listener in the bottom of the tree. So when we add an event listener on the click of the button, it will still check for event listeners outside our element. As we just added one for `document`, it will trigger that too.

Capturing is the same process, but in the other direction. By adding the { capture: true } option to the new event listener, it will only trigger in the capturing phase, and by the time the button's listener fires, the capturing phase is over.

document.addEventListener('click', alert('hey'), {capture: true})

This way, we can be sure that our new event listener only fires for events happening after adding it.

💡
There is one exception of course, and it's when you add the new listener to an element that is lower in the hierarchy while the capturing is still in progress. 

Photo by Alotrobo