Why does Turbo use Event Capturing?

I am reading the source of the Turbo library and came across following code in link_click_observer.ts, which executes whenever the session starts (when the page loads) and Turbo starts observing the links and forms:

start() {
  if (!this.started) {
    addEventListener("click", this.clickCaptured, true)
    this.started = true
  }
}

clickCaptured = () => {
  removeEventListener("click", this.clickBubbled, false)
  addEventListener("click", this.clickBubbled, false)
}

Now I understand that event capturing phase happens before bubbling, where the event propagates from the document root to the event.target and then propagates back to the root in the event bubbling phase.

However, I don’t fully understand why Turbo is capturing the event, removing the clickBubbled event handler, and then re-assigning the same handler again? My guess is that it’s trying to remove any previous handlers. Is it correct? If yes, what’s the reasoning behind it?

Any insight into the above code is really appreciated.

I did some more research and found a good explanation in this 10-year old post from none other than Sam.

Using event capturing to improve Basecamp page load times

The mechanics of the capturing phase make it ideal for preparing or preventing behavior that will later be applied by event delegation during the bubbling phase. And that’s how we’re going to use it here—to initialize the sortable in response to a mouse click, but just before the event starts bubbling and other handlers have a chance to deal with it.

Still, I’d appreciate it if anyone else has further insights about this.

I think this is a way to make sure that the clickBubbled method will be called as the last event handler. When two event handlers are registered on the same element, for the same phase, they are called in the order they were registered. So unregistering and re-registering puts it as the last event handler on window.

Why would you want to execute clickBubbled last? Probably to give a chance to every other event handlers to call preventDefault() on the event and therefore prevent the navigation.

That’s just my guess.

Yes, that does make sense. Thanks for your reply, @rik.