ypsilon-event-handler
Version:
Supercharged multi-handler event system with closest-match DOM resolution and QuantumType docs. The most advanced event delegation architecture available.
760 lines (616 loc) β’ 27.2 kB
Markdown
# YpsilonEventHandler - Advanced Usage Patterns
Revolutionary patterns and techniques discovered during YpsilonEventHandler development.
All examples work with `file://` protocol - no build, no setup, no server needed. **Just open in your browser.**
- [π Pattern: Quantum-Entangled Modules](#-pattern-quantum-entangled-modules)
- [β‘ Pattern: Reactive State Management](#-pattern-reactive-state-management)
- [π Pattern: Super Delegation](#-pattern-super-delegation)
- [ποΈ Pattern: Self-Contained Module Architecture](#-pattern-self-contained-module-architecture)
- [π― Pattern: Inline Method Injection](#-pattern-inline-method-injection)
- [π Pattern: Performance Monitoring](#-pattern-performance-monitoring)
- [π― Key Patterns Summary](#-key-patterns-summary)
- [π¨ WARNING: OUT OF CONTROL!](#-warning-out-of-control)
- [π€― Unhinged Usage Patterns](#-unhinged-usage-patterns)
- [π¨ Pattern: Cross-Tab Event Broadcasting](#-pattern-cross-tab-event-broadcasting)
- [π¨ Pattern: Event-Driven CSS Toggling](#-pattern-event-driven-css-toggling)
- [π¨ Pattern: Self-Modifying Handlers](#-pattern-self-modifying-handlers)
- [π¨ Pattern: DOM-to-WebWorker Event Bridge](#-pattern-dom-to-webworker-event-bridge)
> π‘ **How to Run**: Save any example as an `.html` file and double-click to open in your browser. Works instantly!
## π **Pattern: Quantum-Entangled Modules**
**Decouple your UI and data management. Communicate via events for true modularityβno direct references, no import spaghetti.**
**Use When**: You want to build modular apps where data and UI are developed independently, or when building micro-frontend architectures.
Independent modules that communicate through event dispatch without coupling.
Event-driven architecture in minutes and zero configuration.
[Live example on JSFiddle](https://jsfiddle.net/65hdmz9v/), just because.
```js
// Module A: User Interface (can be in same file or separate files)
class UserInterface extends YpsilonEventHandler {
constructor() {
super({
// Handles clicks within its own UI section
'#user-panel': ['click'],
// Subscriber, subscribes to 'data.update'
'document': [{ type: 'data.update', handler: 'onDataChange' }]
});
}
handleClick(event, target, containerElement) {
// Broadcast UI actions to any listening modules
this.dispatch('ui.action', {
action: target.dataset.action,
userId: containerElement.dataset.userId
});
}
// Listen for data updates from other modules
onDataChange(event) {
console.log('πΌοΈ UI updating from data change:', event.detail);
}
}
// Module B: Data Manager (completely independent - no imports needed!)
class DataManager extends YpsilonEventHandler {
constructor() {
super({
// Handles clicks within its own control section
'#data-controls': ['click'],
// Subscriber, subscribes to 'ui.action'
'document': [{ type: 'ui.action', handler: 'onUIAction' }]
});
}
handleClick(event, target, containerElement) {
// Broadcast data updates to any listening modules
this.dispatch('data.update', {
timestamp: Date.now(),
source: 'data-manager'
});
}
// React to UI actions from other modules
onUIAction(event) {
console.log('π Data responding to UI action:', event.detail);
}
}
// Zero coupling, infinite scalability! Each module operates independently
const ui = new UserInterface();
const data = new DataManager();
```
## β‘ **Pattern: Reactive State Management**
**Build React/Vue-style reactivity with pure vanilla JavaScript. State changes instantly update the entire UI.**
**Use When**: You need reactive interfaces without framework overhead, building configuration panels, or creating dynamic dashboards.
[Live example on JSFiddle](https://jsfiddle.net/4zqem37g/), where I copy&pasted from below to there, again.
```html
<label>
Font size
<input
value="16"
data-key="fontSize"
data-action="setConfig"
type="number" min="10" max="40" step="1">
</label>
<label>
Distance top
<input
value="10"
data-key="marginTop"
data-action="setConfig"
type="number" min="5" max="100" step="1">
</label>
<pre id="show-config">Loading configuration</pre>
fontSize: <span data-config-key="fontSize">Loading value of fontSize</span>
<script src="https://cdn.jsdelivr.net/npm/ypsilon-event-handler@latest/ypsilon-event-handler.min.js"></script>
<script>
// Our reactive state - just a plain object
const config = {
ui: {
fontSize: 0, // Will become 16 after init
marginTop: 0, // Will become 10 after init
}
};
class ConfigHandler extends YpsilonEventHandler {
constructor() {
// MAGIC #1: Super delegation! Single listener handles all config inputs
super({
// One body listener instead of listeners on every input
// New config form elements work automatically when added via JS
body: [
{ type: 'change', handler: 'handleChange' }
]
});
// Cache DOM queries to avoid repeated querySelectorAll calls
this.cache = new Map();
this._parseConfigs();
this._updateUiConfig();
}
// MAGIC #2: Super router - configurable via data-action attributes
handleChange(event, target) {
const action = target.dataset.action;
if (action && this[action]) {
this[action](target, event);
}
}
// Reactive config setter - the heart of reactivity
setConfig(target, event) {
const key = target.dataset.key;
config.ui[key] = this._getConfigValue(key, target);
// DRY principled UI updater - this is where the magic happens
this._updateUiConfig();
}
// Flexible value resolver - customize per config type
_getConfigValue(key, target) {
switch(key) {
case 'fontSize':
case 'marginTop': return parseInt(target.value, 10);
}
return null; // Handle validation/fallbacks as needed
}
// Initialize reactivity - existing input values become defaults
_parseConfigs() {
let setConfigElements = this.getCache('setConfigElements');
if (!setConfigElements) {
setConfigElements = document.querySelectorAll('[data-action="setConfig"]');
this.setCache('setConfigElements', setConfigElements);
}
setConfigElements.forEach(element => {
const key = element.dataset.key;
config.ui[key] = this._getConfigValue(key, element);
});
}
// The reactive core - updates happen everywhere instantly
_updateUiConfig() {
// Update the main config display
let showConfig = this.getCache('showConfig');
if (!showConfig) {
showConfig = document.getElementById('show-config');
this.setCache('showConfig', showConfig);
}
showConfig.textContent = JSON.stringify(config, null, 2);
// Update individual elements scattered throughout the page
let individualElements = this.getCache('individualElements');
if (!individualElements) {
individualElements = document.querySelectorAll('[data-config-key]');
this.setCache('individualElements', individualElements);
}
individualElements.forEach(element => {
const configKey = element.dataset.configKey;
element.textContent = config.ui[configKey];
});
}
// Simple cache management
getCache(key) { return this.cache.get(key); }
setCache(key, value) { this.cache.set(key, value); }
}
// Initialize and enjoy React-style reactivity in vanilla JS!
const configHandler = new ConfigHandler();
// configHandler.destroy() // Clean removal when needed
</script>
```
**React/Vue developers will be amazed - full reactivity with zero framework overhead!** β‘
## π **Pattern: Super Delegation**
**Maximum efficiency: One listener rules them all. Perfect for large applications with dynamic content.**
**Use When**: Building large SPAs with hundreds/thousands of dynamic elements, or when you want ultimate performance with minimal memory footprint.
One listener handles infinite elements with intelligent routing.
**Something's wrong, HEY?**
"What?"
**Where is my example to copy&paste?**
"Challenge, you write your Example!"
**Say what?**
```js
class SuperDelegator extends YpsilonEventHandler {
constructor() {
super({
// One listener for the entire application
body: [
'click',
{ type: 'input', debounce: 300 },
{ type: 'scroll', throttle: 100 }
// 'resize', 'touchstart', 'touchmove', 'touchend'...
]
});
}
handleClick(event, target, containerElement) {
const action = target.dataset.action;
const component = target.closest('[data-component]')?.dataset.component;
// Smart routing: Component-specific handlers get priority
if (component && this[`handle${component}Click`]) {
this[`handle${component}Click`](event, target, containerElement);
}
// Fallback to action-based routing for generic behaviors
else if (action && this[action]) {
this[action](event, target, containerElement);
}
}
// Component-specific handlers
handleModalClick(event, target, containerElement) { console.log('πͺ Modal component click', arguments) }
handleFormClick(event, target, containerElement) { console.log('π Form component click', arguments) }
// Action handlers
saveData(event, target, containerElement) { console.log('πΎ Saving data...', arguments) }
deleteItem(event, target, containerElement) { console.log('ποΈ Deleting item...', arguments) }
}
```
## ποΈ **Pattern: Self-Contained Module Architecture**
**Build completely independent modules that manage their own lifecycle, configuration, and event handling.**
**Use When**: Creating reusable components, building plugin systems, or developing modular applications where each module should be completely self-sufficient.
```js
function deepMerge(target, source) {
for (const key in source) {
if (source[key] instanceof Object && !Array.isArray(source[key])) {
target[key] = YaiUtils.deepMerge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Self-contained module that handles everything internally
class YpsExample {
constructor(customConfig = {}) {
// Deep merge configuration system
this.config = deepMerge({
rootSelector: '[data-y-tabs]', // Module root selector
selectors: {}, // component-specific selectors
events: {} // Event configuration overrides
}, customConfig);
// Own YpsilonEventHandler instance with complete lifecycle
this.events = new YpsilonEventHandler({
[this.config.rootSelector]: ['click']
}, {
click: { run: 'runAction' } // Event-scoped aliases
}, {
methods: {
// Event-scoped method organization
click: {
handleClick: (event, target, container) => {
const action = target.dataset.tabAction;
if (!action) return;
// Smart method resolution with fallbacks
const handler = this[action] ||
this[this.events.resolveMethodName(action, event.type)];
if (handler) return handler.call(this, target, event, container);
console.warn(`No handler for action: ${action}`);
}
}
},
methodsFirst: true, // Check methods object first
abortController: true // Enable clean abort capability
});
}
// Module-specific business logic
runAction(target, event, container) {
console.log('π― Module action executed:', target.dataset.tabAction);
// Module handles its own business logic completely independently
}
// Lifecycle management - critical for clean module systems
destroy() {
this.events.destroy(); // Complete YpsilonEventHandler cleanup
this.config = null; // Clean up configuration
// Clean up any other module resources
}
abort() {
this.events.abort(); // Quick abort if abortController enabled
}
}
// Usage: Multiple independent instances - zero conflicts!
const tabsModule1 = new YpsExample({ rootSelector: '[data-tabs-primary]' });
const tabsModule2 = new YpsExample({ rootSelector: '[data-tabs-secondary]' });
// Each module is completely self-contained and independent
```
**Perfect for building plugin architectures where each module is completely independent!** π
## π― **Pattern: Inline Method Injection**
**Organize your code by co-locating methods with HTML for rapid prototyping and clean separation.**
**Use When**: Building small to medium apps where you want Vue.js-style external methods without the framework overhead.
Write your code where your HTML lives and inject methods into the handler on the fly.
[Live example on JSFiddle](https://jsfiddle.net/xrju1gca/), where I copy&pasted from below to there.
```html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Patterns - YpsilonEventHandler</title>
</head>
<body>
<header><h1>Advanced Usage Patterns</h1></header>
<div id="test-section" data-action="testRunAll">
Test the power of inline method injection
</div>
<form id="user-form">
<input type="text" data-action="validateForm" placeholder="Username">
<button type="button" data-action="openModal">Open Settings</button>
</form>
<script>
// π External Methods Object (Vue.js style organization)
const advancedMethods = {
// Global methods (available to all events - think of these as universal handlers)
testRunAll(event, target, containerElement) {
console.log('π― Test initiated from:', containerElement?.id || 'unknown');
// Dispatch custom events for inter-module communication
this.dispatch('test.started', { target: target.dataset.action });
},
// Event-scoped methods (organized by event type - prevents naming conflicts!)
click: {
validateForm(event, target, containerElement) {
console.log('π Click validation in:', containerElement?.tagName);
},
openModal(event, target, containerElement) {
console.log('πͺ Opening modal from:', target.dataset.action);
// Cross-module communication via events
this.dispatch('modal.open', { trigger: target.dataset.action });
}
},
// Same method name, different event scope = zero conflicts!
input: {
validateForm(event, target, containerElement) {
console.log('β‘ Real-time input validation:', target.value);
}
}
};
</script>
<script src="https://cdn.jsdelivr.net/npm/ypsilon-event-handler@latest/ypsilon-event-handler.min.js"></script>
<script>
// π Pattern 2: Super Router with Advanced Configuration
class AdvancedHandler extends YpsilonEventHandler {
constructor() {
super({
// Target specific sections for true component isolation
'#test-section': ['click', ],
'#user-form': ['click', 'input'],
'document': [
// Emit + Subscribe
{ type: 'test.started', handler: 'onTestEvent' },
{ type: 'modal.open', handler: 'onModalEvent' },
{ type: 'app.ready', handler: 'onAppReady' },
]
}, {}, {
methods: advancedMethods, // Inject external methods
methodsFirst: true, // Check methods object first (performance optimization)
enableStats: true, // Track performance metrics
enableHandlerValidation: false // Skip validation for production performance
});
// See main README.md "Configuration Options" for all available parameters
this.cache = new Map();
}
// π Pattern 3: Universal Event Proxy (DRY principle)
handleClick(event, target, containerElement) {
this.proxyHandle(event, target, containerElement);
}
handleInput(event, target, containerElement) {
this.proxyHandle(event, target, containerElement);
}
// DRY Proxy for data-action routing
proxyHandle(event, target, containerElement) {
const action = target.dataset.action;
if (!action) return;
// Try instance method first, then fall back to external methods
if (typeof this[action] === 'function') {
return this[action](event, target, containerElement);
}
// Let YpsilonEventHandler resolve from methods object
const resolvedHandler = this.resolveHandler(action, event.type);
if (typeof resolvedHandler === 'function') {
resolvedHandler.call(this, event, target, containerElement);
}
}
// Event listeners for cross-module communication
onTestEvent(event, target) {
console.log('π Test event received:', event.detail);
}
onModalEvent(event, target) {
console.log('π Modal event received:', event.detail);
}
onAppReady(event, target) {
console.log('π App is loaded and unlocked, ready to engage!', event.detail);
}
// π Advanced Caching System - Copy & Paste Ready!
setCache(key, value) {
this.cache.set(key, value);
}
// Smart element finder with caching - perfect for repeated DOM queries
getCache(key, isMultiple = false, refresh = false, scope = document.body) {
return this._cacheManager(key, isMultiple, refresh, scope);
}
getAllCache() { return this.cache }
deleteCacheItem(key) { return this.cache.delete(key) } // Clean cache item removal
clearCache() { this.cache.clear() }
// Advanced cache manager - handles DOM element caching automatically
_cacheManager(cacheKey, isMultiple, refresh, scope) {
// cacheKey doubles as CSS selector - genius optimization!
let getElements = refresh ? null : this.cache.get(cacheKey);
if (!getElements || !getElements.length) {
const select = isMultiple ? 'querySelectorAll' : 'querySelector';
getElements = scope[select](cacheKey);
this.cache.set(cacheKey, getElements);
}
return getElements;
}
}
// Initialize the advanced handler
const handler = new AdvancedHandler();
// π Pattern 5: Static Utilities (Framework-independent)
const throttledSave = YpsilonEventHandler.throttle(() => {
console.log('πΎ Auto-saving...');
}, 2000, 'auto-save');
// Global event broadcasting without instances
YpsilonEventHandler.dispatch('app.ready', { timestamp: Date.now() });
console.log('π Performance stats:', handler.getStats());
</script>
</body>
</html>
```
## π **Pattern: Performance Monitoring**
**Track your app's performance in real-time. Built-in metrics with zero overhead when disabled.**
**Use When**: Optimizing production applications, debugging performance issues, or conducting load testing.
```js
class PerformanceHandler extends YpsilonEventHandler {
constructor() {
super({
body: ['click', 'input', 'scroll']
}, {}, {
enableStats: true, // Enable performance tracking (default: false)
enableDistanceCache: true, // Cache DOM distance calculations (default: true)
enableHandlerValidation: false // Skip validation for production speed (default: false)
});
// See main README.md "Configuration Options" for all performance settings
// Monitor performance every 5 seconds
setInterval(() => this.logPerformance(), 5000);
}
logPerformance() {
const stats = this.getStats();
if (stats) {
console.log('π Performance Report:', {
listeners: stats.totalListeners,
events: stats.totalEventTypes,
cacheHitRate: stats.distanceCache.hitRate,
userInteracted: stats.userHasInteracted
});
}
}
handleClick(event, target, containerElement) {
// Use throttled methods for expensive operations
this.throttle(() => {
this.heavyComputation();
}, 1000, 'heavy-work');
}
heavyComputation() {
console.log('π§ Performing expensive operation...');
}
}
```
---
## π― **Key Patterns Summary**
1. **Inline Method Injection** - Write methods where HTML lives
2. **Event-Scoped Organization** - Organize methods by event type
3. **Super Router Pattern** - Universal event proxy with data-action routing
4. **Quantum-Entangled Modules** - Independent modules with event communication
5. **Super Delegation** - One listener handles infinite elements
6. **Self-Contained Module Architecture** - Complete lifecycle management with plugin systems
7. **Reactive State Management** - React/Vue-style reactivity with zero framework overhead
8. **Performance Monitoring** - Built-in stats and optimization
---
## π¨ **WARNING: OUT OF CONTROL!**
> β οΈ **YpsilonEventHandler may cause sudden bursts of inspiration, code refactoring frenzies, or feelings of deep satisfaction. Proceed at your own risk.**
**The LOC-to-Feature ratio is completely out of control.**
### π€― **Unhinged Usage Patterns**
*Nobody expects these to work, but they do. Welcome to the bleeding edge.*
### π¨ **Pattern: Cross-Tab Event Broadcasting**
```js
// Broadcast events across browser tabs - because why not?
class CrossTabHandler extends YpsilonEventHandler {
constructor() {
super({ body: ['click'] });
// Listen for events from other tabs
window.addEventListener('storage', (e) => {
if (e.key === 'ypsilon-event') {
const data = JSON.parse(e.newValue);
this.dispatch(data.type, data.detail);
}
});
}
handleClick(event, target) {
// Broadcast to ALL open tabs instantly
localStorage.setItem('ypsilon-event', JSON.stringify({
type: 'cross-tab-click',
detail: { action: target.dataset.action, timestamp: Date.now() }
}));
localStorage.removeItem('ypsilon-event'); // Trigger storage event
}
}
```
### π¨ **Pattern: Event-Driven CSS Toggling**
```js
// CSS as a service via events - completely unhinged
class CSSEventHandler extends YpsilonEventHandler {
constructor() {
super({ body: ['click', { type: 'theme-change', handler: 'applyTheme' }] });
}
handleClick(event, target) {
// Dispatch CSS changes as events
this.dispatch('theme-change', {
theme: target.dataset.theme,
colors: target.dataset.colors?.split(',') || []
});
}
applyTheme(event) {
const { theme, colors } = event.detail;
// Inject CSS dynamically via events
const style = document.createElement('style');
style.textContent = `
.theme-${theme} {
--primary: ${colors[0] || '#000'};
--secondary: ${colors[1] || '#fff'};
}
`;
document.head.appendChild(style);
document.body.className = `theme-${theme}`;
}
}
```
### π¨ **Pattern: Self-Modifying Handlers**
```js
// Handlers that rewrite themselves - pure madness
class SelfModifyingHandler extends YpsilonEventHandler {
constructor() {
super({ body: ['click'] });
this.clickCount = 0;
}
handleClick(event, target) {
this.clickCount++;
// After 10 clicks, completely change behavior
if (this.clickCount === 10) {
// Replace the handler method at runtime!
this.handleClick = function(event, target) {
console.log('π€― Handler has evolved!');
// Now dispatch different events
this.dispatch('handler-evolved', {
originalClicks: this.clickCount,
newBehavior: 'chaos-mode'
});
};
}
console.log(`Click ${this.clickCount} - ${this.clickCount < 10 ? 'Normal' : 'ERROR: Should not see this'}`);
}
}
```
### π¨ **Pattern: DOM-to-WebWorker Event Bridge**
Revolutionary data flow architecture bridging DOM events to WebWorker computations:
**Event Chain:**
```
DOM input β handleInput() β worker.postMessage() β
WebWorker computation β worker.onmessage β
this.dispatch('worker-result') β displayResult()
```
**Key Innovation:** Seamless DOM-to-WebWorker-to-DOM event bridging through YpsilonEventHandler's custom event system. Heavy computations never block the UI.
[Live example on JSFiddle](https://jsfiddle.net/j0ev6woq/).
```js
// Bridge DOM events to WebWorkers via custom events - completely out of control
class WebWorkerBridge extends YpsilonEventHandler {
constructor() {
super({ body: ['input'] });
// Create worker for heavy computations on the fly
this.worker = new Worker('data:application/javascript,' + encodeURIComponent(`
self.onmessage = function(e) {
// Simulate heavy computation
const result = Array(1000000).fill(0).reduce((a, b, i) => a + i);
self.postMessage({ type: 'computation-done', result, input: e.data });
};
`));
this.worker.onmessage = (e) => {
// Worker results become DOM events
this.dispatch('worker-result', e.data);
};
}
handleInput(event, target) {
// Send DOM input to WebWorker
this.worker.postMessage(target.value);
}
}
// Extend to handle worker results
class WorkerResultHandler extends WebWorkerBridge {
constructor() {
super();
// Add worker result handling
this.addEvent('document', { type: 'worker-result', handler: 'displayResult' });
}
displayResult(event) {
console.log('π§ Worker computed:', event.detail);
}
}
new WorkerResultHandler();
```
**"Out of Control!" isn't a bug - it's our superpower.** π
These patterns enable enterprise-level architecture with radical simplicity - all running on `file://` protocol with zero build tools and zero server instances.