Javascript Event Listeners Tutorial

In this Javascipt tutorial we learn how to handle user interaction with our app through event listeners.

We learn how to add and remove event listeners to elements, how to invoke multiple functions on a single event listener and how to add different event listeners on a single element.

Lastly we discuss event propagation and the difference between capturing and bubbling.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

What is an event listener

The Javascript event listener is an interface that allows us to handle user interaction with our application, such as when a user clicks on a button.

The event listener will invoke a specified function when the event it’s listening for fires off.

How to add an event listener

To add an event listener to an element, we use the addEventListener() method on the element we want to target an event for.

The method takes up to 3 parameters:

  1. The event we want to listen for, like click or keypressOpens up in a new page .
  2. The function we want to invoke when the event fires. This may be a named, anonymous or arrow function.
  3. (Optional) Define the order of event handling in nested elements. True will handle parent elements first, false will handle child elements first.
Syntax:
 element.addEventListener(event, function, use_capture);
Example:
<html>
<body>
  <button>Click Me!</button>
</body>
<script>
  // add an event listener for the
  // 'click' event on the 'button'
  document.querySelector("button").addEventListener("click", alertMe);

  // simple alert funtion that will
  // be invoked when a user clicks
  // on the button
  function alertMe() {

    alert("Alert!");
  }
</script>
</html>

In the example above, we add an event listener to the button that will invoke the alertMe() function when a user clicks on it.

Remember that when using a named function call as a parameter, we exclude the parameter list. Otherwise, Javascript will recognize it as a regular function call and invoke it immediately, instead of waiting for the event to fire first.

As mentioned, we can use an anonymous function.

Example:
<html>
<body>
  <button>Click Me!</button>
</body>
<script>
  const el = document.querySelector("button");

  // use an anonymous function as a parameter
  el.addEventListener("click", function() { alert("Alert!"); } );
</script>
</html>

Or an arrow function.

Example:
<html>
<body>
  <button>Click Me!</button>
</body>
<script>
  const el = document.querySelector("button");

  // use an arrow function as a parameter
  el.addEventListener("click", () => alert("Alert!") );
</script>
</html>

How to invoke multiple functions on a single event listener

We can invoke multiple functions on a single event listener without overwriting each other.

To do this we simply call the addEventListener() method more than once with a different function.

Example:
<html>
<body>
  <button>Click Me!</button>
</body>
<script>
  const el = document.querySelector("button");

  // use an arrow function as a parameter
  el.addEventListener("click", () => alert("Alert 1!") );
  el.addEventListener("click", () => alert("Alert 2!") );
</script>
</html>

In the example above, we add another event listener for the same event on the same button.

Once the first alert fires and we close it, the second alert will fire.

How to add different event listeners on the same element

We can add multiple event listeners for different events on the same element. One will not replace or overwrite another.

Example:
<html>
  <style>.hidden { display: none }</style>
<body>
  <button>Mouseover or Click Me!</button>
  <p class="hidden">Mouse over...</p>
</body>
<script>
  const p = document.querySelector("p");
  const el = document.querySelector("button");

  // multiple different events on
  // the 'button' element
  el.addEventListener("click", () => alert("Alert!") );
  el.addEventListener("mouseover", () => p.classList.remove("hidden") );
  el.addEventListener("mouseout", () => p.classList.add("hidden") );
</script>
</html>

In the example above we add two extra events to the ‘button’ element, mouseover and mouseout.

Event propagation: capturing vs bubbling

The optional third argument of the event listener determines the element order of nested elements when an event occurs.

For example, if the user clicks on a ‘button’ element inside a ‘div’ element, which element’s click event should be fired off first.

If we specify true, the addEventListener() method will use capturing, otherwise it will use bubbling.

So what’s the difference between capturing and bubbling:

  • Capturing will handle the outer (parent) element’s event first, then the inner (child) element’s event.
  • Bubbling will handle the inner element’s event first, then the outer element’s event.
Example:
<html>
  <style>
    body { display: flex; justify-content: center; align-items: center }
    div { width: 10rem; margin: 1rem; padding: .4rem 1.5rem 1.5rem 1.5rem; border: .2rem solid #45B0E5; background: #71D0FF; text-align: center }
    button { width: 100%; padding: 1rem; border: .1rem solid #eeeeee; background: #fff }
  </style>
<body>
  <div>
    <h2>CAPTURING</h2>
    <button>Click me!</button>
  </div>

  <div>
    <h2>BUBBLING</h2>
    <button>Click me!</button>
  </div>
</body>
<script>
  const d = document.querySelectorAll("div");
  const b = document.querySelectorAll("button");

  // capturing will handle the outer event before the inner one
  b[0].addEventListener("click", () => alert("Capturing: you clicked the button (inner element)"), true);
  d[0].addEventListener("click", () => alert("Capturing: you clicked the blue element (outer div element)"), true);

  // bubbling will handle the inner event before the outer one
  b[1].addEventListener("click", () => alert("Bubbling: you clicked the button (inner element)"), false);
  d[1].addEventListener("click", () => alert("Bubbling: you clicked the blue element (outer div element)"), false);
</script>
</html>

Notice the order in which the alerts fire when clicking on one of the buttons.

When we click on the capturing button, the outer div event is handled first. When we click on the bubbling button, the inner button event is handled first.

In most cases we want bubbling, which is why it’s the default and doesn’t have to be specified.

How to remove an event listener

If we need to remove an event listener at some point during runtime, we can use the removeEventListener() method. The method takes the event type and method to remove as arguments.

Example:
<html>
<body>
  <button>If an event exists, a click will fire an alert</button>
</body>
<script>
  // add event listener
  document.querySelector("button").addEventListener("click", alertMe);

  // remove event listener
  document.querySelector("button").removeEventListener("click", alertMe);

  function alertMe() {
    alert("Alert!");
  }
</script>
</html>

When we click on the button in the example above, it doesn’t fire the alert event because it was removed.

Summary: Points to remember

  • An event listener handles user interaction with our application. An event listener will wait for and handle an event when it triggers.
  • An event listener is added to an element on the page, such as a button.
  • The event listener will invoke a specified function when the event triggers.
  • Javascript supports multiple functions on a single event, as well as multiple event listeners for a single element.
  • Event propagation determines the order in which events are handled with nested elements.
    • Capturing will handle from the outermost element to the innermost.
    • Bubbling, the default, will handle from the innermost element to the outermost.