Listenable
Updated on August 22, 2024Source codeTests
Listenable
is a class that enriches an event type (including observation types, media queries, custom gestures, and more), allowing it to:
- Listen for that event type and execute a callback function when it occurs
- Retrieve a list of active listeners that it has added
- Store a status (
ready
,listening
, orstopped
) - Easily clean up all listening activity to avoid memory leaks
Example
Baleada's docs use Listenable
to:
- Toggle the dark theme when you press
Shift
+D
- Toggle the minimalist theme when you press
Shift
+M
- Set your default theme (dark or light) based on your device's color theme preference
- On mobile, open and close the sidebar nav and table of contents when you swipe right or left
Construct a Listenable
instance
The Listenable
constructor accepts two parameters:
type
options
Listenable
instance. See the Listenable
constructor options section for more guidance.Valid event types
Listenable
supports a ton of different event types and can deduce which web APIs to use under the hood based on the type
you pass.
Most of the time, you don't need to be concerned with exactly which web API is being used, and you can think of it as an implementation detail.
But in certain cases where you want to customize the way a specific web API behaves, you'll need to know which API is being used in order to know what customization options are available.
You'll find more guidance down below, in the How to customize listen
behavior section.
The table below has a breakdown of valid event types, the corresponding web APIs that Listenable
uses under the hood, and the main purpose of the API.
click
, keydown
, mousemove
, etc.intersect
message
and messageerror
BroadcastChannel
recognizeable
Listenable
instance 🤯Listens for custom gestures, powered by Recognizeable
.
See the How to listen for custom gestures section for more guidance.
Listenable
constructor options
recognizeable
Passes options for a new instance of the Recognizeable
class. See the How to listen for custom gestures section for more guidance.
The recognizeable
option only has an effect when your type
is recognizeable
.
State and methods
type
The event type you passed to the Listenable
constructor.
If you assign a value directly to type
, a setter will pass the new value to setType
.
status
The status (String) of the Listenable
instance.
status
is ready
after the instance is constructed, and changes to listening
after the listen
method is called for the first time, and change to stopped
after all web API activity has been stopped and cleaned up.
active
Set
of objects that describe all the currently active listeners, observers, etc.recognizeable
The Recognizeable
instance constructed using the options you passed to options.recognizeable
.
If you didn't pass that option, the recognizeable
property will be undefined
.
See the How to listen for custom gestures section for more guidance.
setType(type)
type
, after stopping and cleaning up all existing web API activity.Listenable
instancelisten(effect, options)
type
, and performs side effects via a callback function when the events happen. Can't be called until the DOM is available.A side effect (Function, required) that will be performed when the events are detected, and an optional options
object.
To learn more about handling events with your side effect function, see the How to handle events and How to customize listen
behavior sections.
Listenable
instancestop(options)
An optional object with a target
property, whose value is a DOM element, window
, document
, or a BroadcastChannel
instance, depending on what type of event you're listening for.
If options.target
is passed, only activity related to that target
will be stopped.
If options.target
is not passed, all activity is stopped.
See the Default values for
Listenable
instanceHow to handle events
Depending on your type
, your effect
—passed as the required first argument of the listen
method—will receive different parameters.
The table below has a full breakdown of what the listen
method passes to your effect
for each specific event type:
type
effect
receivesclick
, keydown
, mousemove
, etc.intersect
IntersectionObserverEntry
objectsresize
ResizeObserverEntry
objectsmutate
MutationRecord
objectsMediaQueryListEvent
objectidle
IdleDeadline
objectmessage
and messageerror
recognizeable
The latest sequenceItem
added to your Recognizeable
instance's sequence
.
Often, your effect
won't actually do anything with this argument. Instead, it will reach into listenableInstance.recognizeable.metadata
for additional information about the captured sequence of events.
How to customize listen
behavior
The listen
method accepts an optional second parameter, which is an Object whose properties customize the behavior of the web APIs Listenable
uses under the hood.
Depending on your type
only certain properties will have an effect.
First, here's a breakdown of what each options
property does, and below that, in the Available options for each type
section, you'll find a table of which properties can be used for each type
:
target
The target that will listen for events.
See the Default values for target
based on type
section for more guidance on default target
values.
useCapture
useCapture
parameter of addEventListener
. Ignored if an addEventListener
object was passed.except
[]
An array of DOM elements that, if they are the target of the event, should not cause your effect
to be executed.
When the only
option is a non-empty array, except
is ignored.
only
[]
An array of DOM elements that, if they are the target of the event, should cause your effect
to be executed.
When only
is a non-empty array, except
is ignored.
An empty only
array is ignored (otherwise, the effect
would never execute).
observe
keyDirection
down
Indicates which keyboard event should be listened to when detecting keycombos. Valid options are down
and up
.
The keyDirection
option only has an effect when your type
is a keycombo, as described the How to format combos.
Default values for target
based on type
An object with a `target` property, which should be the `BroadcastChannel` instance that is receiving messages.
The default value for the listen
method's target
option depends on your type
. The table below has a full breakdown.
type
target
document
HTMLElementEventMap
, e.g. visibilitychange
, fullscreenchange
, etc.document
(can't be overridden)intersect
document.querySelector('html')
resize
document.querySelector('html')
mutate
document.querySelector('html')
idle
message
and messageerror
A new BroadcastChannel
.
You'll technically be able to retrieve this BroadcastChannel
from listenableInstance.active
and send messages from it, but the DX of creating and passing in your own BroadcastChannel
is much better.
Be sure to check out Baleada's Broadcastable
class, and especially the Using Broadcastable
with Listenable
docs, if you're interested in handling BroadcastChannel
messages with Baleada and Listenable
.
recognizeable
type
handled by your Recognizeable
instance's effects
Available options for each type
type
target
addEventListener
useCapture
wantsUntrusted
except
only
HTMLElementEventMap
, e.g. visibilitychange
, fullscreenchange
, etc.addEventListener
useCapture
wantsUntrusted
intersect
observer
resize
observe
mutate
observe
idle
requestIdleCallback
message
and messageerror
target
recognizeable
How to listen for custom gestures
Listenable
allows you to listen for custom gestures defined by Baleada Logic's Recognizeable
class.
Before you read any further, it's worth checking out the usage docs for the gestures that Baleada supports out of the box.
Those docs will show you the overall workflow for using Listenable
, with Recognizeable
under the hood, to recognize complex gestures.
If you get a better understanding of that system and want to define a gesture of your own, definitely visit the Recognizeable
docs for more guidance.
Using with TypeScript
Listenable
will type check your listen
method effect
functions and options based on the type
(String) you pass to the constructor:
const instance = new Listenable('intersect')
instance.listen(
// Listenable infers from the `intersect` event type that
// `entries` is an array of IntersectionObserverEntry objects.
//
// entries[0].boundingClientRect.width is correctly typed
// as a number, automatically!
entries => console.log(entries[0].boundingClientRect.width),
// Listenable also knows that the `listen` method for an
// 'intersect' type can accept an `observer` option,
// passing an IntersectionObserverInit object.
{ observer: { threshold: 0.5 } }
)
Even complex types like media queries will be detected:
const screenSize = new Listenable('(min-width: 420px)')
screenSize.listen(
// TypeScript knows that this is a MediaQueryListEvent
event => doSomething(event)
)
There's one situation where you'll need to adopt a little bit of type unsafety: when using Listenable
to configure a Recognizeable
instance for recognizing a custom gesture.
Recall that to use Listenable
with Recognizeable
, you should pass recognizeable
as the Listenable
constructor's type
argument, and pass your Recognizeable
instance's options to listenableOptions.recognizeable
const instance = new Listenable(
'recognizeable',
{ recognizeable: { ... } }
)
There's a way to type-annotate your code so that your options.recognizeable
object is fully type checked based on all the possible events that you want your Recognizeable
instance to be able to handle, and type unsafety is minimized.
We won't cover all that information here, though. Instead, you should visit the Using with TypeScript section of the Recognizeable
docs. Those docs give full information on not only how to set up a standalone Recognizeable
instance, but more importantly, how to set up a fully type-safe Listenable
instance that uses Recognizeable
under the hood to recognize custom gestures.
API design compliance
options
object.type
setType
set<Property>
methodsstatus
, active
, recognizeable
listen
, stop
stop
methodstop
able