const listenerRegistry = new WeakMap(); const {addEventListener, removeEventListener} = EventTarget.prototype; // Register event listeners to a key object // listeners: array of listener definitions; // - each definition must be a flat array of event target and the arguments // used to call addEventListener() on the target export function registerListeners(keyObj, listeners) { let registered = listenerRegistry.get(keyObj); if (!registered) { registered = []; listenerRegistry.set(keyObj, registered); } listeners.forEach((listener) => { addEventListener.call(...listener); registered.push(listener); }); } export function unregisterListeners(keyObj) { let listeners = listenerRegistry.get(keyObj); if (!listeners) { return; } listeners.forEach((listener) => { removeEventListener.call(...listener); }); listenerRegistry.delete(keyObj); } // Event.composedPath() polyfill for Edge // based on https://gist.github.com/kleinfreund/e9787d73776c0e3750dcfcdc89f100ec if (!Event.prototype.composedPath) { const getComposedPath = (node, path = []) => { path.push(node); let parent; if (node.parentNode) { parent = node.parentNode; } else if (node.host) { // ShadowRoot parent = node.host; } else if (node.defaultView) { // Document parent = node.defaultView; } return parent ? getComposedPath(parent, path) : path; }; Event.prototype.composedPath = function () { return getComposedPath(this.target); }; } function findFromPath(path, criteria, currentTarget, index = 0) { const el = path[index]; if (criteria(el)) { return el; } else if (el === currentTarget || !el.parentElement) { // stop when reaching currentTarget or return; } return findFromPath(path, criteria, currentTarget, index + 1); } // Search for the actual target of a delegated event export function findElementInEventPath(ev, selector) { const criteria = typeof selector === 'function' ? selector : el => el.matches(selector); return findFromPath(ev.composedPath(), criteria, ev.currentTarget); }