WASM: add declarative event handling and targeted DOM updates #5

Open
opened 2026-08-07 14:23:34 +00:00 by hum3 · 0 comments
Owner

Problem

WASM interactivity currently requires manual syscall/js bridging. Each interactive element needs:

  1. A custom Go function exported via js.Global().Set()
  2. Manual JS event listener attachment in app.js
  3. Full buffer re-render via Reset() + rebuild on every interaction

The 08_water_tank_multi example shows this works but it's boilerplate-heavy and splits interaction logic across Go and JS files.

This came up building gogal WASM demos — the HTMX example (server-side legend toggling) has no clean WASM equivalent because there's no event mechanism in the model.

What HTMX provides (for comparison)

  • Declarative triggers: hx-trigger="click" on any element
  • Targeted updates: hx-target="#chart" replaces only part of the page
  • Polling: hx-trigger="every 2s" for periodic refresh of a region

Proposed API

A minimal extension that stays true to lofigui's philosophy (no JS frameworks, Go-first):

1. Go-side action registration

func model(app *lofigui.App) {
    hidden := map[string]bool{}

    app.OnAction("toggle", func(param string) {
        hidden[param] = !hidden[param]
        lofigui.UpdateTarget("chart", renderChart(hidden))
    })

    lofigui.Target("chart", renderChart(hidden))
    lofigui.HTML(`<button data-action="toggle" data-param="Temperature">Toggle Temperature</button>`)
}

2. Targeted DOM updates

lofigui.Target("chart", svgHTML)  // wraps in <div id="lofigui-chart"> and tracks separately
lofigui.UpdateTarget("chart", newSVGHTML)  // replaces just that region

3. JS side (built into lofigui's WASM runtime, not user-written)

  • After each goRender(), scan for data-action attributes and attach click handlers
  • Click handler calls goAction(name, param) (new exported function)
  • goAction dispatches to registered Go callback
  • goRenderTarget(name) returns just one region's HTML for partial update

Complexity budget

This should be ~100 lines in lofigui (wasm.go + a target registry). The user writes zero JS for basic interactions. The existing app.js pattern continues to work for custom cases.

Alternatives considered

  • Keep manual syscall/js: works but duplicates boilerplate across every interactive example
  • Full virtual DOM: too complex, against lofigui philosophy
  • Embed HTMX in WASM pages: HTMX needs a server to send requests to — defeats the point of WASM

Use cases from gogal

  • Legend toggling (show/hide chart series)
  • Axis mode switching (temporal vs ordinal)
  • Theme switching (light/dark)
  • Parameter sliders (point count, smoothing)

Migrated from Codeberg: originally #5, opened 2026-04-08.

## Problem WASM interactivity currently requires manual `syscall/js` bridging. Each interactive element needs: 1. A custom Go function exported via `js.Global().Set()` 2. Manual JS event listener attachment in `app.js` 3. Full buffer re-render via `Reset()` + rebuild on every interaction The `08_water_tank_multi` example shows this works but it's boilerplate-heavy and splits interaction logic across Go and JS files. This came up building gogal WASM demos — the HTMX example (server-side legend toggling) has no clean WASM equivalent because there's no event mechanism in the model. ## What HTMX provides (for comparison) - **Declarative triggers**: `hx-trigger="click"` on any element - **Targeted updates**: `hx-target="#chart"` replaces only part of the page - **Polling**: `hx-trigger="every 2s"` for periodic refresh of a region ## Proposed API A minimal extension that stays true to lofigui's philosophy (no JS frameworks, Go-first): ### 1. Go-side action registration ```go func model(app *lofigui.App) { hidden := map[string]bool{} app.OnAction("toggle", func(param string) { hidden[param] = !hidden[param] lofigui.UpdateTarget("chart", renderChart(hidden)) }) lofigui.Target("chart", renderChart(hidden)) lofigui.HTML(`<button data-action="toggle" data-param="Temperature">Toggle Temperature</button>`) } ``` ### 2. Targeted DOM updates ```go lofigui.Target("chart", svgHTML) // wraps in <div id="lofigui-chart"> and tracks separately lofigui.UpdateTarget("chart", newSVGHTML) // replaces just that region ``` ### 3. JS side (built into lofigui's WASM runtime, not user-written) - After each `goRender()`, scan for `data-action` attributes and attach click handlers - Click handler calls `goAction(name, param)` (new exported function) - `goAction` dispatches to registered Go callback - `goRenderTarget(name)` returns just one region's HTML for partial update ## Complexity budget This should be ~100 lines in lofigui (wasm.go + a target registry). The user writes zero JS for basic interactions. The existing `app.js` pattern continues to work for custom cases. ## Alternatives considered - **Keep manual `syscall/js`**: works but duplicates boilerplate across every interactive example - **Full virtual DOM**: too complex, against lofigui philosophy - **Embed HTMX in WASM pages**: HTMX needs a server to send requests to — defeats the point of WASM ## Use cases from gogal - Legend toggling (show/hide chart series) - Axis mode switching (temporal vs ordinal) - Theme switching (light/dark) - Parameter sliders (point count, smoothing) --- *Migrated from Codeberg: originally [#5](https://codeberg.org/hum3/lofigui/issues/5), opened 2026-04-08.*
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hum3/lofigui#5
No description provided.