UNPKG

multiple-select-vanilla

Version:

This lib allows you to select multiple elements with checkboxes

119 lines (107 loc) 4.54 kB
export interface ElementEventListener { element: Element; eventName: keyof HTMLElementEventMap; listener: EventListener; groupName?: string; } export class BindingEventService { protected _distinctEvent: boolean; protected _boundedEvents: ElementEventListener[] = []; get boundedEvents(): ElementEventListener[] { return this._boundedEvents; } constructor(options?: { distinctEvent: boolean }) { this._distinctEvent = options?.distinctEvent ?? false; } dispose() { this.unbindAll(); this._boundedEvents = []; } /** Bind an event listener to any element */ bind<H extends HTMLElement = HTMLElement>( elementOrElements: H | NodeListOf<H> | Window, eventNameOrNames: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap>, listener: EventListener, listenerOptions?: boolean | AddEventListenerOptions, groupName = '', ) { // convert to array for looping in next task const eventNames = Array.isArray(eventNameOrNames) ? eventNameOrNames : [eventNameOrNames]; if (typeof (elementOrElements as NodeListOf<H>)?.forEach === 'function') { // multiple elements to bind to (elementOrElements as NodeListOf<H>).forEach(element => { for (const eventName of eventNames) { if (!this._distinctEvent || (this._distinctEvent && !this.hasBinding(element, eventName))) { element.addEventListener(eventName, listener as EventListener, listenerOptions); this._boundedEvents.push({ element, eventName, listener: listener as EventListener, groupName }); } } }); } else { // single elements to bind to for (const eventName of eventNames) { if (!this._distinctEvent || (this._distinctEvent && !this.hasBinding(elementOrElements as H, eventName))) { (elementOrElements as H).addEventListener(eventName, listener as EventListener, listenerOptions); this._boundedEvents.push({ element: elementOrElements as H, eventName, listener: listener as EventListener, groupName, }); } } } } hasBinding(elm: Element, eventNameOrNames?: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap>): boolean { return this._boundedEvents.some(f => f.element === elm && (!eventNameOrNames || f.eventName === eventNameOrNames)); } /** Unbind a specific listener that was bounded earlier */ unbind( elementOrElements?: Element | NodeListOf<Element> | null, eventNameOrNames?: keyof HTMLElementEventMap | Array<keyof HTMLElementEventMap>, listener?: EventListenerOrEventListenerObject | null, ) { if (elementOrElements) { const elements = Array.isArray(elementOrElements) ? elementOrElements : [elementOrElements]; const eventNames = Array.isArray(eventNameOrNames) ? eventNameOrNames || '' : [eventNameOrNames || '']; for (const element of elements) { if (!listener) { listener = this._boundedEvents.find(f => { if (f.element === element && (!eventNameOrNames || f.eventName === eventNameOrNames)) { return f.listener; } return undefined; }) as EventListener | undefined; } for (const eventName of eventNames) { element?.removeEventListener?.(eventName, listener); } } } } /** * Unbind all event listeners that were bounded, optionally provide a group name to unbind all listeners assigned to that specific group only. */ unbindAll(groupName?: string | string[]) { if (groupName) { const groupNames = Array.isArray(groupName) ? groupName : [groupName]; // unbind only the bounded event with a specific group // Note: we need to loop in reverse order to avoid array reindexing (causing index offset) after a splice is called for (let i = this._boundedEvents.length - 1; i >= 0; --i) { const boundedEvent = this._boundedEvents[i]; if (groupNames.some(g => g === boundedEvent.groupName)) { const { element, eventName, listener } = boundedEvent; this.unbind(element, eventName, listener); this._boundedEvents.splice(i, 1); } } } else { // unbind everything while (this._boundedEvents.length > 0) { const boundedEvent = this._boundedEvents.pop() as ElementEventListener; const { element, eventName, listener } = boundedEvent; this.unbind(element, eventName, listener); } } } }