interface / @typed/template

EventSource

An interface for managing event listeners on DOM nodes.

It abstracts the process of adding and removing event listeners, ensuring that they are properly cleaned up when the scope is closed or the element is removed.

Package version
1.0.0-beta.13
Category
Delegated event sources
Since
1.0.0

Import

import { EventSource } from "@typed/template";

Signatures

export interface EventSource {
    readonly addEventListener: <Ev extends Event>(element: EventTarget, event: EventName, handler: Handler<Ev>) => Disposable;
    readonly setup: (rendered: Rendered, scope: Scope.Scope) => Effect.Effect<void>;
}

Why

EventSource delegates browser events across the concrete nodes represented by a rendered value. A matching callback receives a non-pooled Proxy that forwards properties, binds methods to the native event, and overrides currentTarget with the registered delegated target. Handlers may be registered before or after a mount.

Ownership and lifetime

setup binds one rendered range to an explicit Effect Scope. That Scope removes native listeners and interrupts every handler fiber it started. Cancellation is signaled without awaiting handlers, which may themselves be publishing the update that removes this mount. Disposing the value returned by addEventListener removes only that entry. No document-global registry or parallel propagation model is created, but the callback event is not object-identical to the browser event because of that currentTarget Proxy.

Property: addEventListener

Registers a delegated native event handler for one concrete target.

Property: addEventListener: Why

Registration can happen before a rendered range is attached, keeping template compilation separate from mounting.

Property: addEventListener: Ownership and lifetime

The returned Disposable removes this entry from every active mount; each mount Scope independently owns its native attachments.

Property: setup

Sets up event listeners for a rendered template within a scope.

Property: setup: Why

Delegation attaches to the concrete elements represented by DOM output. The proxy preserves the browser event’s target, properties, and bound methods while reporting the registered element as currentTarget.

Property: setup: Ownership and lifetime

The supplied Scope removes listeners and interrupts handler fibers.

Examples

import { Effect, Scope } from "effect"
import { makeEventSource } from "@typed/template/EventSource"
import * as EventHandler from "@typed/template/EventHandler"

const program = Effect.scoped(Effect.gen(function* () {
  const eventSource = makeEventSource()
  const button = document.createElement("button")

  // Add event listener
  const handler = EventHandler.make((event: MouseEvent) => {
    console.log("Button clicked")
  })

  const disposable = eventSource.addEventListener(button, "click", handler)

  // Setup listeners for rendered content
  yield* eventSource.setup(button, yield* Scope.Scope)

  // Cleanup
  disposable[Symbol.dispose]()
}))

Other public imports

These import paths expose the same declaration. Each page retains its own public name and signature.

Source