Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | 20x 20x 20x 20x 20x 40x 2x 38x 31x 7x 7x 63x 63x 63x 63x 63x 7x 15x 15x 1x 14x 14x 14x 1x 13x 13x 13x 13x 1x 12x 12x 8x 1x 7x 63x 7x 7x 7x 7x 21x 3x | /**
* Event Delegation for Coherent.js
*
* Document-level event delegation that routes events to handlers via
* data-coherent-{eventType} attributes. This ensures event handlers
* survive DOM updates since they're registered by ID, not by element.
*/
import { handlerRegistry as defaultRegistry } from './registry.js';
import { wrapEvent } from './wrapper.js';
/**
* EventDelegation class
* Manages document-level event listeners and routes to registered handlers
*/
export class EventDelegation {
/**
* @param {import('./registry.js').HandlerRegistry} [registry] - Handler registry instance
*/
constructor(registry = defaultRegistry) {
this.registry = registry;
this.initialized = false;
this.root = null;
this.boundHandlers = new Map();
/**
* Event types to delegate
* Focus/blur use capture phase because they don't bubble
*/
this.eventTypes = [
'click',
'change',
'input',
'submit',
'focus',
'blur',
'keydown',
'keyup',
'keypress',
];
}
/**
* Initialize event delegation by attaching listeners to the root element
* @param {Document|Element} [root=document] - Root element for event delegation
*/
initialize(root = typeof document !== 'undefined' ? document : null) {
if (this.initialized) {
return;
}
if (!root) {
// No DOM available (SSR context)
return;
}
this.root = root;
for (const eventType of this.eventTypes) {
const handler = (event) => this.handleEvent(event, eventType);
// Focus and blur don't bubble - must use capture phase
const useCapture = eventType === 'focus' || eventType === 'blur';
// Submit needs preventDefault capability, others can be passive
const options = {
capture: useCapture,
passive: eventType !== 'submit',
};
root.addEventListener(eventType, handler, options);
this.boundHandlers.set(eventType, { handler, options });
}
this.initialized = true;
}
/**
* Handle a delegated event
* @param {Event} event - The DOM event
* @param {string} eventType - The type of event (click, change, etc.)
*/
handleEvent(event, eventType) {
const target = event.target;
if (!target || typeof target.closest !== 'function') {
return;
}
// Find the nearest element with the appropriate data attribute
const attrName = `data-coherent-${eventType}`;
const delegateTarget = target.closest(`[${attrName}]`);
if (!delegateTarget) {
return;
}
// Get the handler ID from the attribute
const handlerId = delegateTarget.getAttribute(attrName);
Iif (!handlerId) {
return;
}
// Look up the handler in the registry
const entry = this.registry.get(handlerId);
if (!entry) {
return;
}
// Wrap the event with component context and call the handler
const wrappedEvent = wrapEvent(event, delegateTarget, entry.componentRef);
entry.handler(wrappedEvent);
}
/**
* Destroy the event delegation system
* Removes all listeners and clears the registry
*/
destroy() {
if (!this.initialized || !this.root) {
return;
}
// Remove all event listeners
for (const [eventType, { handler, options }] of this.boundHandlers) {
this.root.removeEventListener(eventType, handler, options);
}
this.boundHandlers.clear();
this.registry.clear();
this.initialized = false;
this.root = null;
}
/**
* Check if the delegation system is initialized
* @returns {boolean} True if initialized
*/
isInitialized() {
return this.initialized;
}
}
/**
* Singleton event delegation instance
* Use this for global event delegation
*/
export const eventDelegation = new EventDelegation();
|