Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does adding an event handler during a handler appear to work differently from removing one?

I'm writing an event driver for a project I'm working on and I'm using the standard browser event system as a model, which has made me look at it in more detail. I'm testing things in Edge and Firefox.

Consider this:

document.documentElement.addEventListener("click", () =>
{
    console.log("Handler 1");
    document.documentElement.addEventListener("click", () => console.log("Handler 2"));

    return;
});

When the document element is clicked for the first time, the output in the console is:

Handler 1

From this, it would seem that, even though the end result are two events bound in order (Handler 1 and Handler 2), a handler added inside another event handler is not added to the currently processed queue.

Now consider this:

const handler2 = () => console.log("Handler 2");

document.documentElement.addEventListener("click", () =>
{
    console.log("Handler 1");
    document.documentElement.removeEventListener("click", handler2);

    return;
});
document.documentElement.addEventListener("click", handler2);

When the document element is clicked, the output in the console is also:

Handler 1

In this second case, the two handlers are initially bound in order, and I would presume the event queue contains both when Handler 1 is initially run, but removing an event handler is enough to also remove it from the currently processed queue.

Is there a rationale behind this apparent difference? I don't understand why removing the listener would impact the event queue. If the browser doesn't update the queue when an event is added for performance reasons, then surely those reasons apply when removing too. I would also think the logical thing would be to have the two operations behave consistently.

like image 654
Benjamin Penney Avatar asked Sep 15 '25 12:09

Benjamin Penney


1 Answers

The difference in the behavior between event listener registration and removal is spelled out in the DOM specification:

  • Event listeners added after the event has been dispatched shall have no effect.
  • Removal of event listeners after the event has been dispatched shall have an effect.

I can imagine the following rationale behind this: It shall be possible to remove an event listener through an abort signal:

const a = new AbortController()
b.addEventListener("click", function() {
  alert(1);
  a.abort();
});
b.addEventListener("click", function() {
  alert(2);
}, {
  signal: a.signal
});
<button id="b">Click</button>

The first event listener aborts the second and this must have an effect even though the event has already been dispatched.

Here is the discussion from 2015 when this behavior was written into the specification.

like image 130
Heiko Theißen Avatar answered Sep 17 '25 01:09

Heiko Theißen