ESL Event Listeners - Basic concepts
Basic concepts
The esl-event-listener module is based on the following major terms and classes:
The host
Everything that happens with ESL event listeners should be associated with a host object. The host is an object that "owns" (registers / deletes) the subscription.
By default, the host is used as an EventTarget (or an object implementing EventTarget interface) to subscribe. But the host object is not necessarily related to an EventTarget. We have at least two options to change the target. First of all, you can define the target explicitly. Another way is to specify the default DOM target of the host object by providing a special $host key (see ESLMixinElement implementation).
The host object is also used as a context to call the handler function of the subscription.
The handler
The handler is a function that is used to process the subscription event. ESL declares a generic type to describe such functions - ESLListenerHandler;
export type ESLListenerHandler<EType extends Event = Event> = (
event: EType
) => void;
The ESLEventListener class and subscription
The subscriptions created by the ESL event listener module are instances of ESLEventListener class. All active subscriptions are stored in a hidden property of the host object.
ESLEventListener has the following basic properties:
event- event type that the subscription is listening to;handler- reference for the function to call to handle the event (see The handler);host- reference for the object that holds the subscription (see The host);target- definition ofEventTargetelement (or stringTraversingQueryto find it, see details inESLEventDesriptor);selector- CSS selector to use built-in event delegation;capture- marker to use the capture phase of the DOM event life-cycle;passive- marker to use passive (non-blocking) subscription of the native event (if supported);once- marker to destroy the subscription after the first event catch.group- auxiliary property to group subscriptions. Does not affect the subscription behavior. Can be used for filtering and bulk operations.
All of the ESLEventListener instance fields are read-only; the subscription can't be changed once created.
The ESLEventListener, as a class, describes the subscription behavior and contains static methods to create and manage subscriptions.
Descriptors (ESLEventDesriptor, ESLEventDesriptorFn)
The event listener Descriptor is an object to describe future subscriptions. The ESL event listeners module has a few special details regarding such objects.
A simple descriptor is an object that is passed to ESLEventListener API to create a subscription. It contains almost the same set of keys as the ESLEventListener instance.
In addition to that, ESL allows you to combine the ESLEventDesriptor data with the handler function. ESLEventDesriptorFn is a function handler that is decorated with the ESLEventDesriptor properties.
Here is the list of supported keys of ESLEventDesriptor:
-
eventkeyType:
string | PropertyProvider<string>
Description: the event type for subscription. Can be provided as a string or via provider function that will be called right before the subscription.The event string (as a literal, or returned by
PropertyProvider) can declare multiple event types separated by space. ESL will create a subscription (ESLEventListenerobject) for each event type in this case. -
targetkeyType:
string | EventTarget | EventTarget[] | PropertyProvider<string | EventTarget | EventTarget[]>
Default Value:hostobject itself or$hostkey of thehostobject
Description: the key to declare exact EventTarget for the subscription. In case thetargetkey is a string it is considered as aTraversingQuery. The query finds a target relatively tohost | host.$hostobject, or in bounds of the DOM tree if it is absolute. Thetargetkey also supports an exact reference forEventTarget(s).⚠ Any
EventTargetor even ESLSynteticEventTarget(includingESLMediaQuery) can be a target for listener API.⚠ See OOTB Extended Event Targets of ESL to know how to optimize handling of frequent events.
The
targetproperty can be declared viaPropertyProvideras well. -
selectorkeyType:
string | PropertyProvider<string>
Default Value:null
Description: the CSS selector to filter event targets for event delegation mechanism.⚠ If you want to get the currently delegated event target, you can access the
$delegatekey under the received event instance. In order to have access to$delegatestrictly typed use theDelegatedEvent<EventType>type decorator.E.g.:
@listen({ event: 'click', selector: 'button' }) onClick(e: DelegatedEvent<MouseEvent> /* instead of MouseEvent */) { const delegate = e.$delegate; //instaead of e.target && e.target.closest('button'); ... }Supports
PropertyProviderto declare the computed value as well. -
conditionkeyType:
boolean | PropertyProvider<boolean>Default Value:true
Description: the function predicate or boolean flag to check if the subscription should be created. Resolves right before the subscription.Useful in combination with
@listendecorator to declare subscriptions.class MyEl extends ESLBaseElement { @attr() enabled = true; @listen({event: 'click', condition: (that) => that.enabled}) onClick(e) {} attributeChangedCallback(name, oldValue, newValue) { if (name === 'enabled') { ESLEventUtils.unsubscribe(this, this.onClick); ESLEventUtils.subscribe(this, this.onClick); } } } -
capturekeyType:
boolean
Default Value:false
Description: marker to use the capturing phase of the DOM event to handle. -
passivekeyType:
boolean
Default Value:trueif the event type iswheel,mousewheel,touchstartortouchmoveDescription: marker to use passive subscription to the native event.⚠ ESL uses passive subscription by default for
wheel,mousewheel,touchstart,touchmoveevents. You need to declarepassivekey explicitly to override this behavior. -
oncekeyType:
boolean
Default Value:false
Description: marker to unsubscribe the listener after the first successful handling of the event. -
groupkeyType:
string
Description: auxiliary property to group subscriptions. Does not affect the subscription behavior. Can be used for filtering and bulk operations.E.g.:
ESLEventUtils.subscribe(host, {event: 'click', group: 'group'}, handler1); ESLEventUtils.subscribe(host, {event: 'click', group: 'group'}, handler2); // ... ESLEventUtils.unsubscribe(host, {group: 'group'}); // Unsubscribes all subscriptions with the 'group' key -
autokey (forESLEventDesriptorFndeclaration only)Type:
boolean
Default Value:falseforESLEventUtils.initDescriptor,truefor@listendecorator Description: marker to make an auto-subscribable descriptor. See Automatic (collectable) descriptors. -
inheritkey (forESLEventDesriptorExtonly)Type:
boolean
Description: available in extended version ofESLEventDesriptorthat is used in the descriptor declaration API. Allows to inheritESLEventDesriptordata from theESLEventDesriptorFnfrom the prototype chain. SeeinitDescriptorusages example.
Automatic (collectable) descriptors
Auto-collectable (or auto-subscribable) descriptors can be subscribed at once during the initialization of the host object.
To make an ESLEventDesriptorFn auto-collectable, the consumer should declare it with the auto marker using ESLEventUtils.initDescriptor or @listen decorator.
⚠ ESLEventUtils.initDescriptor (or @listen) stores the auto-collectable descriptors in the internal collection on the host.
The ESLBaseElment and the ESLMixinElement subscribe all auto-collectable descriptors in the connectedCallback. See the usage of ESLEventUtils.subscibe for more details.
PropertyProvider for event, selector, or target
The descriptor declaration usually happens with the class declaration when the instance and its subscription do not exist. We might have a problem if we want to pass subscription parameters that depend on the instance.
To resolve such a case, the event, selector, and target keys of ESL event listener API support PropertyProvider mechanism:
type PropertyProvider<T> = (this: unknown, that: unknown) => T;
See examples in the ESLEventUtils.initDescriptor section.