JavaScriptDOM and events

EventTarget.removeEventListener()

Detaches an event handler function added earlier with addEventListener(). Requires the same function and the same event type.

Returns
undefined
On this page

Example

#
JavaScript
const like = document.querySelector('#like')
const lock = document.querySelector('#lock')
let likes = 0

function addLike() {
  likes += 1
  console.log('Likes:', likes)
}

like.addEventListener('click', addLike)

lock.addEventListener('click', () => {
  like.removeEventListener('click', addLike)
  console.log('Likes blocked')
})

like.click()
lock.click()
like.click()
Console
Likes: 1
Likes blocked

The last like.click() prints nothing, since the addLike function is already detached. Clicking the “Like the lesson” button in the preview won’t change anything either.

Definition and usage

#

The removeEventListener() method removes an event handler registered earlier with addEventListener(). After calling it, the function stops reacting to that event: a button stops counting clicks, and a document stops listening for keyboard shortcuts.

The browser identifies the handler to remove by three things: the event type, the function, and the phase, i.e. the capture option. You must pass the exact same function (the same reference), not a different function with identical code. So store a handler you plan to detach someday in a variable or a named function. An anonymous function written directly inside addEventListener() can’t be removed.

If the handler was added with capture: true, pass capture: true when removing it too. Other options, such as once or passive, don’t matter here. Trying to remove a handler that isn’t there doesn’t cause an error, it simply does nothing. A convenient alternative is the signal option in addEventListener(): a single controller.abort() call then detaches every handler registered with that signal.

Syntax

#
Syntax
target.removeEventListener(type, listener)
target.removeEventListener(type, listener, options)
target.removeEventListener(type, listener, useCapture)

Parameters

#
  • type

    string

    The event type, e.g. "click". Must be the same as when the handler was added.
  • listener

    function

    The same function (the same reference) that was passed to addEventListener().
  • options

    optional object or boolean

    Only capture matters. If the handler was added with capture: true, pass the same value here.

More examples

#
An anonymous function can’t be detached
JavaScript
const save = document.querySelector('#save')

save.addEventListener('click', () => console.log('Saved'))
save.removeEventListener('click', () => console.log('Saved'))

save.click()
Console
Saved

Both arrow functions have the same code, but they’re two different objects. removeEventListener() doesn’t find a matching handler, so it removes nothing.

The capture option has to match
JavaScript
const menu = document.querySelector('#menu')
const log = (event) => console.log('Clicked:', event.target.textContent)

menu.addEventListener('click', log, { capture: true })

menu.removeEventListener('click', log)
menu.children[0].click()

menu.removeEventListener('click', log, { capture: true })
menu.children[1].click()
Console
Clicked: Courses

The first call looks for a handler added without capture, so the function stays. Only passing capture: true detaches it, and clicking “Ranking” no longer prints anything.

Detaching several handlers with AbortController
JavaScript
const nick = document.querySelector('#nick')
const controller = new AbortController()
const { signal } = controller

nick.addEventListener('input', () => console.log('input:', nick.value), { signal })
nick.addEventListener('change', () => console.log('change:', nick.value), { signal })

nick.value = 'Emma'
nick.dispatchEvent(new Event('input'))
nick.dispatchEvent(new Event('change'))

controller.abort()

nick.value = 'Jack'
nick.dispatchEvent(new Event('input'))
nick.dispatchEvent(new Event('change'))
console.log('Both handlers detached')
Console
input: Emma
change: Emma
Both handlers detached

One controller.abort() call removes every handler added with that same signal at once. No need to remember each function individually.

Browser support

#

Widely available

Works in all modern browsers, phones included. You can use it without worry.

  • Chrome
  • Edge
  • Firefox
  • Safari

Good practices

#
  • Store the handler you plan to remove in a variable or a named function. Passing a “matching” arrow function won’t do anything.
  • Watch out for bind(): every call to this.onClick.bind(this) creates a new function. Store the result once, e.g. in an object field, and use it for both adding and removing.
  • When detaching several handlers at once, for example when closing a dialog, it’s simpler to use the signal option and a single controller.abort() call.

Related entries

#

Spotted a mistake or missing an example? Write to us.