UNPKG

@itrocks/build

Version:

Permanently apply javascript modifiers to your dynamic DOM

89 lines (88 loc) 3.12 kB
import { SortedArrayBy } from '../sorted-array/sorted-array.js'; export const ALWAYS = 'always'; export const CALL = 'call'; class Callback { event; selectors; callback; priority; args; constructor(event, selectors, callback, priority, args) { this.event = event; this.selectors = selectors; this.callback = callback; this.priority = priority; this.args = args; } applyInto(containerElement) { for (const element of this.matchingElementsInto(containerElement)) { if (this.event === CALL) { this.callback(element, ...this.args); } else { element.addEventListener(this.event, this.callback); } } } matchingElementsInto(element) { const elements = new Set; for (const selector of this.selectors) { if ((selector[0] === ALWAYS[0]) && (selector === ALWAYS)) { elements.add(element); continue; } if (element.matches(selector)) { elements.add(element); } element.querySelectorAll(selector).forEach(element => elements.add(element)); } return elements; } } const callbacks = new SortedArrayBy('priority'); const chainedSelectors = (selector) => { const selectors = ['']; for (const sourcePart of selector) { const addParts = sourcePart.split(','); const addPart = addParts.pop(); const length = selectors.length; for (const oldPart of selectors) { for (const addPart of addParts) { selectors.push(oldPart + ' ' + addPart); } } for (let index = 0; index < length; index++) { selectors[index] += ' ' + addPart; } } return selectors; }; export function build(event, type, callback) { if (typeof event === 'function') { event = { callback: event }; } else if ((typeof event === 'string') || Array.isArray(event)) { event = (callback ? { callback, type, selector: event } : { callback: type, selector: event }); } event.args ??= []; event.priority ??= 1000; event.selector ??= ALWAYS; event.type ??= CALL; event.priority = (event.priority * 1000000) + callbacks.length; event.selector = (typeof event.selector === 'string') ? [event.selector] : chainedSelectors(event.selector); const buildCallback = new Callback(event.type, event.selector, event.callback, event.priority ?? 0, event.args); buildCallback.applyInto(document.body); callbacks.push(buildCallback); } const observer = new MutationObserver(mutations => { for (const mutation of mutations) { mutation.addedNodes.forEach(addedNode => { if ((addedNode instanceof HTMLElement) && addedNode.closest('html')) { for (const callback of callbacks) { callback.applyInto(addedNode); } } }); } }); observer.observe(document.body, { childList: true, subtree: true });