Events
Wire a node's onclick/onfocus/onblur/onchange to a single onEvent callback.
A node binds a DOM event to a named action in its props (see §8 Events and actions). The renderer dispatches every binding into one callback: pass onEvent, a (EventPayload) -> Unit, and switch on the action name:
@Composable
fun Storefront(spec: Spec) {
JoyDom(spec = spec, onEvent = { event ->
when (event.name) {
"checkout" -> {
val sku = event.params?.getString("sku")
// ...
}
"dismiss" -> dismiss()
}
})
}There is no registry and no per-event registration — one callback fires for every bound event. Branch on event.name (or event.type) to decide what to do. The params map carries the binding's static arguments verbatim; the same typed accessors used for node props read scalars off it (event.params?.getInt("amount")) — see Reading prop values for the full set.
event.type names the kind: the inbuilt Click/Focus/Blur/Change map to onclick/onfocus/onblur/onchange, and Custom(name) carries an event a custom component emitted. event.target identifies the node the event fired on (its id, class list, and node type) — the joy-dom analog of event.target.
Passing no onEvent (the default) still leaves a bound node interactive (clickable/focusable); the dispatch is just dropped — matching the web, where ignoring an event is simply not handling it.
onchange has no built-in trigger
The supported subset has no form inputs, so onchange is parsed and carried but never fires for a
built-in node. Only a custom component can dispatch it, via
emit(EventType.Change).
Emitting from a custom component
The renderer wires bindings on built-in nodes only. A custom component receives the resolved node and calls emit from its ComponentScope to dispatch into the same onEvent callback:
@Composable
fun Tappable(spec: Spec) {
val registry = components {
component("tappy") {
// Typed binding: fires the node's `onclick`.
Text("tap", modifier = Modifier.clickable { emit(EventType.Click) })
}
component("rating") {
// String-named event: fires the node's `rated` binding by name.
Text("rate", modifier = Modifier.clickable { emit("rated") })
}
}
JoyDom(spec = spec, components = registry, onEvent = { event ->
when (event.name) {
"rated" -> { /* ... */ }
}
})
}emit(EventType.Click) fires the node's onclick binding. The string overload emit("rated") fires a custom event by name — it resolves the binding the node carries under that exact prop (here rated), and is a no-op when the node has no such binding. Runtime data passed as the second argument reaches the handler as event.payload — kept separate from the binding's static event.params, never merged, so the handler can always tell author data from component data.