Why Turbo Uses Event Capturing

1 min read
If you are interested in learning about Hotwire, check out my crash-course on Hotwire's Turbo framework.

I was 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 didn’t fully understand why Turbo is capturing the event, removing the clickBubbled event handler, and then re-assigning the same handler again? Is it trying to remove any previous handlers? If yes, what’s the reasoning behind it?

I asked the question on the Hotwire mailing list, but haven’t heard back from anyone yet.

Meanwhile, I found a good explanation in this 10-year old post from none other than Sam Stephenson.

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.

Very interesting.