How do I handle DOM events?
data-event
<button data-event="click:_handleDelete:@dataset:id,@target">Delete</button>
<input data-event="input:_handleName:@value">
data-event="eventName:handlerMethodName[:arg1,arg2,...]"
eventName is bound via addEventListener to this[handlerMethodName](...resolvedArgs). Listeners are rebound (old ones torn down first) on every render, and fully removed on destroy(). If eventName, handlerMethodName, or a matching method on the component is missing, the attribute is silently ignored — no error.
Resolving arguments
Each :-separated arg after the handler name is resolved per-token:
| Token | Resolves to |
|---|---|
@id / @class / @name / @tag |
the matching element property, HTML-escaped |
@value |
the input/textarea .value, or a contenteditable element's .innerText, escaped |
@checked / @selected |
raw boolean, unescaped |
@textContent / @innerText / @html |
the matching element property, escaped |
@event |
the raw Event object |
@target |
event.target |
@x / @y |
event.clientX / event.clientY (defaulting to 0) |
@key / @code |
event.key / event.code, escaped |
@attr:name |
element.getAttribute('name'), escaped |
@dataset:key |
element.dataset['key'], escaped |
"literal" / 'literal' |
the literal string, unquoted |
42 |
the literal number |
Anything else falls back to the raw token text unless a custom token has been registered for it (see below).
<button data-event="click:_addToCart:42,"gift"">Add</button>
Custom tokens
TokenHelper is exported from core/index.ts:
import { TokenHelper } from '../../../core/index.js';
TokenHelper.registerCustomToken('@timestamp', () => Date.now());
TokenHelper.registerPrefixedToken('@style:', (prop, el) => (el.style as any)[prop] ?? '');
registerCustomToken(name, resolver)— for a fixed token with no variable part, e.g.@timestamp.registerPrefixedToken(prefix, resolver)— for a repeatable pattern with a dynamic suffix, e.g.@style:color,@style:width.
A prefixed token used alone as a data-event arg (e.g. data-event="click:_handler:@style:color") resolves correctly — the parser rejoins everything after the handler name on : before splitting on ,. Only combining a prefixed token with a comma-separated sibling arg introduces ambiguity worth avoiding; if unsure, test the resolved value directly with TokenHelper.resolve(token, el, event).
Gotchas
- Resolving any arg token calls
event.preventDefault()unconditionally — so adata-eventhandler with at least one argument always prevents default browser behavior (form submission, link navigation, etc.). A handler with zero args (data-event="click:handler") does not prevent default. @checked,@selected,@event,@target,@x,@yare the only tokens that are not HTML-escaped — they return raw booleans/objects/numbers, not strings.