Many of us are still doing these things:

  • element.addEventListener('type', obj.method.bind(obj))
  • element.addEventListener('type', function (event) {})
  • element.addEventListener('type', (event) => {})

The above examples all create new anonymous event handlers that can’t be removed when no longer needed. This may cause performance problems or unexpected logic bugs, when handlers that you no longer need still get accidentally triggered through unexpected user interactions or event bubbling.

Safer event-handling patterns include the following:

Use a reference:

const handler = function () {
  console.log("Tada!")
}
element.addEventListener("click", handler)
// Later on
element.removeEventListener("click", handler)

Named function that removes itself:

element.addEventListener('click', function click(e) {
  if (someCondition) {
    return e.currentTarget.removeEventListener('click', click);
  }
});

A better approach:

function handleEvent (eventName, {onElement, withCallback, useCapture = false} = {}, thisArg) {
  const element = onElement || document.documentElement

  function handler (event) {
    if (typeof withCallback === 'function') {
      withCallback.call(thisArg, event)
    }
  }

  handler.destroy = function () {
    return element.removeEventListener(eventName, handler, useCapture)
  }

  element.addEventListener(eventName, handler, useCapture)
  return handler
}

// Anytime you need
const handleClick = handleEvent('click', {
  onElement: element,
  withCallback: (event) => {
    console.log('Tada!')
  }
})

// And anytime you want to remove it
handleClick.destroy()