Skip to content

Event Handlers

Attach event listeners using on + event name attributes.

Syntax

typescript
html`<button onClick=${handler}>Click</button>`
html`<input onInput=${handler} onKeydown=${handler} />`
html`<form onSubmit=${handler}>...</form>`

// Property object
applyProps(element, { onClick: handler });

Example

typescript
const state = createHook({ count: 0 });

html`
  <button onClick=${() => state.count++}>+</button>
  <span>${state.$count}</span>
  <button onClick=${() => state.count--}>-</button>
`;

Event Object

The handler receives the native Event object:

typescript
const handleSubmit = (e: Event) => {
  e.preventDefault();
  console.log("Form submitted");
};

html`
  <form onSubmit=${handleSubmit}>
    <input name="title" />
    <button type="submit">Save</button>
  </form>
`;

Event Modifiers

Append modifiers with a dot after the event name:

ModifierEffect
.preventCalls e.preventDefault()
.stopCalls e.stopPropagation()
.onlyCalls e.stopImmediatePropagation()
.selfOnly fires if e.target === e.currentTarget
.onceRemoves listener after first invocation
.captureListener runs in capture phase
.passiveListener is passive (can't call preventDefault)
typescript
// Prevent default form submission
html`<form onSubmit.prevent=${handleSubmit}>`

// Stop click propagation
html`<button onClick.stop=${handleClick}>`

// Multiple modifiers
html`<a onClick.prevent.stop=${handleLink}>`

// Fire only once
html`<div onMouseenter.once=${handleIntro}>`

Multiple Handlers

Pass an array of handlers for a single event:

typescript
const log = (e: Event) => console.log("clicked");
const track = (e: Event) => analytics.track("click");

html`<button onClick=${[log, track]}>Click</button>`;

Object Syntax (on)

Bind multiple events at once using the on attribute with an object:

typescript
html`
  <input on=${{
    focus: () => console.log("focused"),
    blur: () => console.log("blurred"),
    keydown: [handleEnter, handleEscape],
  }} />
`;

This also works for lifecycle events (see DOM Lifecycles):

typescript
html`
  <div on=${{
    mount: () => console.log("mounted"),
    destroy: () => cleanup(),
  }} />
`;

Dynamic Handlers

Handlers can reference reactive state:

typescript
const state = createHook({ editingId: null as string | null });

const startEdit = (id: string) => () => {
  state.editingId = id;
};

html`
  <li>
    <label onDblClick=${startEdit(todo.id)}>${todo.title}</label>
  </li>
`;

Supported Events

All native DOM events are supported: onClick, onDblClick, onChange, onInput, onSubmit, onKeydown, onKeyup, onFocus, onBlur, onMouseover, onMouseout, onLoad, onScroll, onResize, and any custom event name.

Cleanup

Event listeners are automatically cleaned up when elements are removed from the DOM (via the @destroy lifecycle). No manual cleanup needed.