Skip to content

Instantly share code, notes, and snippets.

@julik
Created October 8, 2025 10:14
Show Gist options
  • Save julik/e3e16d58c5e9bef0b1723d18c1d1d0ab to your computer and use it in GitHub Desktop.
Save julik/e3e16d58c5e9bef0b1723d18c1d1d0ab to your computer and use it in GitHub Desktop.

Revisions

  1. julik created this gist Oct 8, 2025.
    54 changes: 54 additions & 0 deletions event_listener_manager.mjs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,54 @@
    /*
    Encapsulates a number of event handlers that should later
    at some point be released. Useful for attaching blocks
    of complex interactions to elements or to `document` and then detaching
    them in bulk when the component/UI that they are serving is
    unmounted or disconnected.
    connect() {
    this.evm = new EventListenerManager();
    // ....
    this.evm.add(document, "drag", (evt) => {
    // ......
    });
    }
    disconnect() {
    this.evm.removeAll();
    }
    The usefulness is that usually one needs to add event listeners in bulk, and they
    need to be wrapped with various throttle() and debounce() functors. When this is done,
    a function with a new identity is created - and to remove these you need to preserve references
    to these functions to be able to remove the event listeners at some point.
    Since Turbolinks presents a "never reload" model by default it is primordial to manage
    event listeners correctly and remove them when they are no longer required.
    */
    class EventListenerManager {
    constructor(...otherManagers) {
    this.releaseFns = [];
    this.nest(...otherManagers);
    }

    nest(...otherManagers) {
    for (let mgr of otherManagers) {
    this.releaseFns.push(() => mgr.removeAll());
    }
    }

    add(target, event, handlerFn, options) {
    target.addEventListener(event, handlerFn, options);
    this.releaseFns.unshift(() => {
    target.removeEventListener(event, handlerFn, options);
    });
    }

    removeAll() {
    for (let r of this.releaseFns) {
    r();
    }
    this.releaseFns.length = 0;
    }
    }

    export { EventListenerManager };