@solid-primitives/event-listener

Primitive to manage creating event listeners.

Usage no npm install needed!

<script type="module">
  import solidPrimitivesEventListener from 'https://cdn.skypack.dev/@solid-primitives/event-listener';
</script>

README

@solid-primitives/event-listener

lerna size size stage

A set of primitives that help with listening to DOM and Custom Events.

Installation

npm install @solid-primitives/event-listener
# or
yarn add @solid-primitives/event-listener

createEventListener

Can be used to listen to DOM or Custom Events on window, document, list of HTML elements or any EventTarget. The target prop can be reactive.

How to use it

import { createEventListener } from "@solid-primitives/event-listener";

const clear = createEventListener(
  document.getElementById("myButton"),
  "mousemove",
  e => console.log("x:", e.pageX, "y:", e.pageY),
  { passive: true }
);

// to clear all of the event listeners
clear();

// target element and event name can be reactive signals
const [ref, setRef] = createSignal<HTMLElement>();
const [name, setName] = createSignal("mousemove");
createEventListener(ref, name, e => {}, { passive: true });

Custom events

// you can provide your own event map type as well:
// fill both type generics for the best type support
createEventListener<{ myCustomEvent: MyEvent; other: Event }, "myCustomEvent">(
  window,
  "myCustomEvent",
  () => console.log("yup!")
);
// just don't use interfaces as EventMaps! (write them using `type` keyword)

Listening to multiple events

Added in @1.4.3

You can listen to multiple events with single createEventListener primitive.

createEventListener(el, ["mousemove", "mouseenter", "mouseleave"], e => {});

Directive Usage

props passed to the directive are also reactive, so you can change handlers on the fly.

import { eventListener } from "@solid-primitives/event-listener";
// avoids tree-shaking the directive:
eventListener;

<button use:eventListener={["click", () => console.log("Click")]}>Click!</button>;

createEventSignal

Like createEventListener, but events are handled with the returned signal, instead of with a callback.

How to use it

import { createEventSignal } from "@solid-primitives/event-listener";

// all arguments can be reactive signals
const [lastEvent, clear] = createEventSignal(el, "mousemove", { passive: true });

createEffect(() => {
  console.log(lastEvent()?.x, lastEvent()?.y);
});

// to clear all the event listeners
clear();

createEventListenerMap

A helpful primitive that listens to a map of events. Handle them by individual callbacks.

How to use it

import { createEventListenerMap } from "@solid-primitives/event-listener";

const clear = createEventListenerMap(element, {
  mousemove: mouseHandler,
  mouseenter: e => {},
  touchend: touchHandler
});

// to clear all the event listeners
clear();

// both target can be reactive:
const [target, setTarget] = createSignal(document.getElementById("abc"));
createEventListenerMap(
  target,
  {
    mousemove: e => {},
    touchstart: e => {}
  },
  { passive: true }
);

// createEventListenerMap can be used to listen to custom events
// fill both type generics for the best type support
createEventListenerMap<
  {
    myEvent: MyEvent;
    custom: Event;
    other: Event;
  },
  "myEvent" | "custom"
>(target, {
  myEvent: e => {},
  custom: e => {}
});

Directive usage

import { eventListenerMap } from "@solid-primitives/event-listener";
// prevent tree-shaking:
eventListenerMap;

<div
  use:eventListenerMap={{
    mousemove: e => {},
    click: clickHandler,
    touchstart: () => {},
    myCustomEvent: e => {}
  }}
></div>;

createEventStore

Similar to createEventListenerMap, but provides a reactive store with the latest captured events.

How to use it

const [lastEvents, clear] = createEventStore(el, "mousemove", "touchend", "click");

createEffect(() => {
  console.log(lastEvents?.mousemove.x);
});

// to clear all the event listeners
clear()

// both target and options args can be reactive:
const [target, setTarget] = createSignal(document.getElementById("abc"));
const [lastEvents] = createEventStore(target, "mousemove", "touchmove");

// createEventStore can be used to listen to custom events
// fill both type generics for the best type support
const [lastEvents] = createEventStore<
  {
    myEvent: MyEvent;
    custom: Event;
    unused: Event;
  },
  "myEvent" | "custom"
>(target, "myEvent", "custom");

// DON'T DO THIS:
const [{ mousemove }] = createEventStore(target, "mousemove", ...);
// the store cannot be destructured

createEventListenerBus

Added in @1.5.0

Dynamically add and remove event listeners to an event target by calling appropriate property. The listeners will be automatically removed on cleanup.

How to use it

Import

import { createEventListenerBus } from "@solid-primitives/event-listener";

Basic usage

createEventListenerBus takes two arguments:

  • target - the event target, could be a window, document, HTMLElement or MediaQueryList. Defaults to window
  • options - event listener options, such as passive or capture
const bus = createEventListenerBus(document.body);
bus.onpointerenter(e => {...});
// listeners return a function that removes them
const clear = bus.onpointermove(e => {...});
clear();

Reactive target

Target argument could be an array, and a reactive signal.

const [target, setTarget] = createSignal([document.body]);
const bus = createEventListenerBus(target);
setTarget(p => [...p, window]); // will change the targets of all active listeners.

generic on()

Using bus.on(type, handler) gives you more options in passing event type.

bus.on("click", e => {});
bus.on(["mousemove", "mousedown"], e => {});
const [types, setTypes] = createsignal(["focus", "blur"]);
bus.on(types, e => {});

Custom Events

The createEventListenerBus can be used to listen to Custom Events.

const bus = createEventListenerBus<{
  foo: SomeEvent;
  bar: MyEvent;
}>();
bus.onfoo(e => {});
bus.onbar(e => {});

Reactive Roots

The EventListenerBus is designed in a way to let you add event listeners outside of reactive roots/ in different root then the one primitive was used in.

const bus = createRoot(dispose => {
  return createEventListenerBus();
});

// listeners can be added outside of the original root setup function.
createRoot(dispose => {
  bus.onclick(e => {});
});

WindowEventListener

Listen to the window DOM Events, using a component.

You can use it with any Solid's Control-Flow components, e.g. <Show/> or <Switch/>.

The event handler prop is reactive, so you can use it with signals.

How to use it

import { WindowEventListener } from "@solid-primitives/event-listener";

<WindowEventListener onMouseMove={e => console.log(e.x, e.y)} />;

DocumentEventListener

The same as WindowEventListener, but listens to document events.

How to use it

import { DocumentEventListener } from "@solid-primitives/event-listener";

<DocumentEventListener onMouseMove={e => console.log(e.x, e.y)} />;

Demo

You may view a working example here: https://codesandbox.io/s/solid-primitives-event-listener-elti5

Changelog

Expand Changelog

0.0.100

First ported commit from react-use-event-listener.

1.1.4

Released a version with type mostly cleaned up.

1.2.3

Switched to a more idiomatic pattern: Warning: incompatible with the previous version!

1.2.5

Added CJS build.

1.2.6

Migrated to new build process.

1.3.0

(minor breaking changes to type generics and returned functions) Primitive rewritten to provide better types and more solidlike (reactive) usage. Added a lot more primitives.

1.3.8

Published recent major updates to latest tag.

1.4.1

Updated to Solid 1.3

1.4.2

Minor improvements.

1.4.3

Allow listening to multiple event types with a single createEventListener | createEventSignal. Removed option to pass a reactive signal as options.

1.5.0

Add createEventListenerBus.