svelte-event
Version:
svelte-event provides a set of wrapper functions for adding modifiers to event handlers and a versatile `event` action for comprehensive event listener management in Svelte.
124 lines (123 loc) • 5.05 kB
JavaScript
import { objectToEntries } from './utils/objectToEntries.js';
function extractHandlersAndModifiers(eventParam) {
if (typeof eventParam === 'function') {
return {
handlers: [eventParam],
modifiers: undefined,
};
}
if ('handler' in eventParam && eventParam.handler) {
return {
handlers: [eventParam.handler],
modifiers: eventParam.modifiers,
};
}
if ('handlers' in eventParam && eventParam.handlers) {
return {
handlers: eventParam.handlers,
modifiers: eventParam.modifiers,
};
}
// Handle the case for only modifiers
if ('modifiers' in eventParam && eventParam.modifiers) {
return {
handlers: [(e) => { }], // need a handler to apply the modifiers
modifiers: eventParam.modifiers,
};
}
return {
handlers: [],
modifiers: undefined,
};
}
function applyModifiers(node, e, modifiers) {
if (!modifiers) {
return;
}
if (modifiers.preventDefault) {
e.preventDefault();
}
if (modifiers.stopPropagation) {
e.stopPropagation();
}
if (modifiers.stopImmediatePropagation) {
e.stopImmediatePropagation();
}
if (modifiers.self && e.target !== node) {
return;
}
if (modifiers.trusted && !e.isTrusted) {
return;
}
}
/**
* `event` is a Svelte action that can be used to attach event listeners to DOM elements with support for various modifiers.
* This action simplifies adding event listeners with additional control over event behavior such as propagation, default actions, and phases.
*
* @param node - The HTMLElement to which the event listeners will be attached.
* @param params - An object where keys are event types and values are event parameters. The parameters can be either a function (event handler) or an object providing more detailed configuration.
*
* The detailed configuration object can include:
* - `handler`: A function to be called when the event occurs. Only one handler can be specified.
* - `handlers`: An array of functions to be called when the event occurs. Multiple handlers can be specified.
* - `modifiers`: An object containing modifier flags that alter the behavior of the event:
* - `preventDefault`: If `true`, prevents the default action of the event.
* - `stopPropagation`: If `true`, stops the propagation of the event in the bubbling phase.
* - `stopImmediatePropagation`: If `true`, prevents other listeners of the same event from being called.
* - `self`: If `true`, ensures the event handler is invoked only when the event originated from the node itself, not from its children.
* - `trusted`: If `true`, the handler will only be invoked for events that are dispatched by the user agent, not by script.
* - `passive`: If `true`, indicates the event listener will not call `preventDefault()`. Useful for touch and wheel events to improve scrolling performance.
* - `capture`: If `true`, the event handler is executed during the capture phase instead of the bubbling phase.
*
* @returns An object with a `destroy` function that can be called to remove the attached event listeners.
*
* @example
* ```svelte
* // Using a single handler
* <div use:event={{ click: handleClick }}></div>
*
* // Using multiple handlers for the same event
* <div use:event={{ click: { handlers: [handleClick1, handleClick2] }}}></div>
*
* // Using modifiers
* <div use:event={{ click: { handler: handleClick, modifiers: { preventDefault: true } }}}></div>
*
* // Using the `passive` modifier for performance optimization
* <div use:event={{ wheel: { modifiers: { passive: true } }}}></div>
*
* // Attaching an event listener in the capture phase
* <div use:event={{ click: { modifiers: { capture: true } }}}></div>
* ```
*/
export function event(node, params) {
// Attach the event listeners
const eventListeners = [];
const paramEntries = objectToEntries(params);
for (const [eventType, eventParam] of paramEntries) {
const { handlers, modifiers } = extractHandlersAndModifiers(eventParam);
for (const handler of handlers) {
const wrappedHandler = (e) => {
applyModifiers(node, e, modifiers);
handler(e); // Cast to any to satisfy the compiler
};
const options = {
passive: modifiers?.passive,
capture: modifiers?.capture,
once: modifiers?.once,
};
node.addEventListener(eventType, wrappedHandler, options);
eventListeners.push({
type: eventType,
listener: wrappedHandler,
});
}
}
// Return the destroy function to remove the event listeners
return {
destroy() {
for (const { type, listener } of eventListeners) {
node.removeEventListener(type, listener);
}
},
};
}