Components

Event handlers

Edit this page

Event handlers are functions that are called in response to specific events occurring in the browser, such as when a user clicks or taps on an element.

Solid provides two ways to add event listeners to the browser:

  • on:__: adds an event listener to the element. This is also known as a native event.
  • on__: adds an event listener to the document and dispatches it to the element. This can be referred to as a delegated event.

Delegated events flow through the component tree, and save some resources by performing better on commonly used events. Native events, however, flow through the DOM tree, and provide more control over the behavior of the event.


Using events

To add an event handler, prefix the event name with either on or on:, and assign it to the function you wish to call when the event is dispatched.

// delegated event
<button onClick={handleClick}>Click me</button>
// native event
<div on:scroll={handleScroll}>... very long text ...</div>

Delegated events are not case sensitive, therefore using delegated event handlers in Solid can be written using camelCase or all lowercase. Note that while delegated events can be written both ways, native events are case sensitive.

<button onclick={handleClick}>Click me</button>

For any other events, such as custom events or events you wish not to be delegated, the on: attribute will add an event listener as-is. This is what makes the event listener case sensitive.

<button on:Custom-Event={handleClick}>Click me</button>

For typing standard or custom events using on:, the TypeScript page has a section about event handlers.


Binding events

To optimize event handlers, you can pass an array as the event handler, replacing the function. When doing this, the second item passed into the array is supplied as the handler's first argument:

const handler = (data, event) => {
console.log("Data:", data, "Event:", event);
};
<button onClick={[handler, "Hello!"]}>Click Me</button>;

In this example, the Hello! string is passed as the data parameter in the handler function when the button is clicked.

By binding events in this way, Solid avoids the overhead of using JavaScript's bind method and adding an additional closure.

Dynamic handlers

An event handler does not form part of the reactive system. If you were to pass the handler as a signal, it will not respond to the changes of that signal. In other words, events do not dynamically update, and the bindings are not reactive. This is because attaching and detaching listeners is a resource-intensive task.

Since event handlers are called like a standard function, you can design them to call a reactive source, if needed.

In the following example, handleClick represents a prop that has the flexibility to adopt any function. As a result, there is no requirement for these functions to be reactive.

<div onClick={() => props.handleClick?.()} />

Event delegation

Instead of attaching event listeners to every individual element, Solid uses synthetic event delegation, through the on__ form . In this method event listeners are attached to the document element, and dispatch events to the relevant elements as they bubble up.

By keeping the number of event listeners to a minimum, events can be captured more effectively. This is especially useful when working with a large number of elements, such as in a table or list.

Supported events such as click, input and keydown are just a few examples that are optimized in this way. To view the full list see the references below.

If you need to attach an event listener to an element that is not supported by Solid's event delegation, such as a custom event in a custom element, you can use the on:__ form.

<div on:customEvent={handleCustomEvent} />

Event delegation considerations

While delegated events provide some performance enhancements, there are tradeoffs.

Event delegation is designed for event propagation through the JSX Tree, rather than the DOM Tree. This can differ from the previous expectations of how events work and flow.

Some things to keep in mind include:

  • Delegated event listeners are added once per event type and handle all future events of that type. This means that delegated event listeners remain active even if the element that added them and its handler is removed. For example, if a div listens for mousemove and is later removed, the mousemove events will still be dispatched to the document in case a different element is also listening for mouse moves.
<div onMouseMove={handleCustomEvent} />
  • event.stopPropagation() does not work as expected since events are attached to the document rather than the element.

    With cases like this, a native event is recommended. As an example, using a native event would stop the following event from reaching the div native handler, which is not the case for delegated events. You can view this example in the Solid Playground.

onMount(() => {
ref.addEventListener("click", () => {
console.log("div native");
});
});
<div ref={ref}>
<button
onClick={(event) => {
event.stopPropagation();
console.log("button");
}}
>
button
</button>
</div>;
Console output
// Button clicked
div native
button

You can solve this by switching the button event to using a native event:

// ...
<button
on:click={(event) => {
event.stopPropagation();
console.log("button");
}}
>
button
</button>
// ...
Console output
// Button clicked
button

See how this solution differs in the Solid Playground.

  • Portals propagate events following the component tree and not the DOM tree, making them easier to use. This means when a Portal gets attached to the body, any events will propagate up to the container.
<div class="container" onInput={() => console.log("portal key press")}>
<Portal mount={document.body}>
<input onInput={() => console.log("input key press")} />
</Portal>
</div>

List of delegated events

You can also view this list in our source code (see DelegatedEvents).

Report an issue with this page