ypsilon-event-handler
Version:
A production-ready event handling system for web applications with memory leak prevention, and method chaining support
317 lines (289 loc) • 14.5 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data-Action Aliases Test - YpsilonEventHandler</title>
<link rel="icon" type="image/x-icon" href="./../favicon.ico">
<style>
body { background: #f8f8f8; font-family: Arial, sans-serif; margin: 2rem auto; max-width: 800px; }
.btn {
padding: 10px 20px;
margin: 5px;
background: #3b82f6;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.btn:hover { background: #2563eb; }
.btn.danger { background: #dc2626; }
.btn.danger:hover { background: #b91c1c; }
.btn.success { background: #16a34a; }
.btn.success:hover { background: #15803d; }
.input {
padding: 8px;
margin: 5px;
border: 1px solid #ccc;
border-radius: 3px;
width: 200px;
}
.output {
background: #f8f9fa;
border: 1px solid #ddd;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
min-height: 50px;
}
.section { margin: 30px 0; padding: 20px; background: #fff; border: 1px solid #eee; }
h2 { color: #333; border-bottom: 2px solid #3b82f6; padding-bottom: 5px; }
.code { background: #f5f5f5; padding: 10px; border-radius: 3px; font-family: monospace; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }
</style>
</head>
<body>
<h1><a href="./index.html" title="Back to index" style="text-decoration: none;">«</a> Data-Action Aliases Test</h1>
<p>Single delegation pattern with semantic <code>data-action</code> attributes using aliases</p>
<div class="section">
<h2>🎯 The Pattern</h2>
<div class="code">
<strong>Single delegation setup:</strong><br><br>
super({ 'body': ['click', 'input'] }, {<br>
click: {<br>
save: 'handleFormSave',<br>
login: 'handleUserLogin',<br>
delete: 'handleItemDelete'<br>
},<br>
input: {<br>
search: 'handleSearchInput',<br>
validate: 'handleFieldValidation'<br>
}<br>
});<br><br>
<strong>HTML:</strong><br>
<button data-action="save">Save</button> → handleFormSave()<br>
<input data-action="search" /> → handleSearchInput()
</div>
</div>
<div class="section">
<h2>🔘 Click Actions with Aliases</h2>
<div class="grid">
<button class="btn success" data-action="save">Save Form</button>
<button class="btn" data-action="login">User Login</button>
<button class="btn danger" data-action="delete">Delete Item</button>
<button class="btn" data-action="share">Share Content</button>
<button class="btn" data-action="export">Export Data</button>
<button class="btn" data-action="refresh">Refresh View</button>
</div>
<div class="output" id="click-output">Click any button above...</div>
</div>
<div class="section">
<h2>📝 Input Actions with Aliases</h2>
<div class="grid">
<input class="input" name="y-demo-1" data-action="search" placeholder="Search (debounced)..." />
<input class="input" name="y-demo-2" data-action="validate" placeholder="Validate input..." />
<input class="input" name="y-demo-3" data-action="filter" placeholder="Filter data..." />
<input class="input" name="y-demo-4" data-action="autocomplete" placeholder="Autocomplete..." />
</div>
<div class="output" id="input-output">Type in any input above...</div>
</div>
<div class="section">
<h2>📊 Mixed Actions</h2>
<p>Same action names work differently based on event type:</p>
<button class="btn" data-action="process">Process (click)</button>
<input class="input" name="y-demo-5" data-action="process" placeholder="Process (input)..." />
<div class="output" id="mixed-output">Same action, different events...</div>
</div>
<div class="section">
<h2>📈 Action Log</h2>
<button class="btn" data-action="clearLog">Clear Log</button>
<div class="output" id="action-log" style="max-height: 200px; overflow-y: auto;">Action history will appear here...</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/ypsilon-event-handler@1.5.0/ypsilon-event-handler.min.js"></script>
<script>
class DataActionAliasTest extends YpsilonEventHandler {
constructor() {
// Single delegation pattern with event-type scoped aliases
super({
'body': ['click', 'input']
}, {
// Click event aliases - semantic action names → actual handler methods
click: {
save: 'handleFormSave',
login: 'handleUserLogin',
delete: 'handleItemDelete',
share: 'handleContentShare',
export: 'handleDataExport',
refresh: 'handleViewRefresh',
process: 'handleClickProcess',
clearLog: 'handleClearLog'
},
// Input event aliases - semantic action names → actual handler methods
input: {
search: 'handleSearchInput',
validate: 'handleFieldValidation',
filter: 'handleDataFilter',
autocomplete: 'handleAutocomplete',
process: 'handleInputProcess'
}
});
this.actionCount = 0;
}
// Universal delegation handlers
handleClick(event, target) {
const action = target.dataset.action;
if (!action) return;
// Resolve alias and call the actual handler
const resolvedHandler = this.resolveMethodName(action, 'click');
if (typeof this[resolvedHandler] === 'function') {
this[resolvedHandler](event, target);
}
}
handleInput(event, target) {
const action = target.dataset.action;
if (!action) return;
// Resolve alias and call the actual handler
const resolvedHandler = this.resolveMethodName(action, 'input');
if (typeof this[resolvedHandler] === 'function') {
this[resolvedHandler](event, target);
}
}
// Actual handler methods (aliased via click actions)
handleFormSave(event, target) {
this.logAction('save', 'handleFormSave', 'Form saved successfully!');
document.getElementById('click-output').innerHTML = `
<strong>🎯 handleFormSave() called</strong><br>
Via: data-action="save" (alias)<br>
Action: Saving form data...<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleUserLogin(event, target) {
this.logAction('login', 'handleUserLogin', 'User authentication started');
document.getElementById('click-output').innerHTML = `
<strong>🔐 handleUserLogin() called</strong><br>
Via: data-action="login" (alias)<br>
Action: Authenticating user...<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleItemDelete(event, target) {
this.logAction('delete', 'handleItemDelete', 'Item marked for deletion');
document.getElementById('click-output').innerHTML = `
<strong>🗑️ handleItemDelete() called</strong><br>
Via: data-action="delete" (alias)<br>
Action: Deleting item...<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleContentShare(event, target) {
this.logAction('share', 'handleContentShare', 'Content shared');
document.getElementById('click-output').innerHTML = `
<strong>📤 handleContentShare() called</strong><br>
Via: data-action="share" (alias)<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleDataExport(event, target) {
this.logAction('export', 'handleDataExport', 'Data export initiated');
document.getElementById('click-output').innerHTML = `
<strong>📊 handleDataExport() called</strong><br>
Via: data-action="export" (alias)<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleViewRefresh(event, target) {
this.logAction('refresh', 'handleViewRefresh', 'View refreshed');
document.getElementById('click-output').innerHTML = `
<strong>🔄 handleViewRefresh() called</strong><br>
Via: data-action="refresh" (alias)<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
// Actual handler methods (aliased via input actions)
handleSearchInput(event, target) {
this.logAction('search', 'handleSearchInput', `Searching: "${target.value}"`);
document.getElementById('input-output').innerHTML = `
<strong>🔍 handleSearchInput() called</strong><br>
Via: data-action="search" (alias)<br>
Query: "${target.value}"<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleFieldValidation(event, target) {
this.logAction('validate', 'handleFieldValidation', `Validating: "${target.value}"`);
document.getElementById('input-output').innerHTML = `
<strong>✅ handleFieldValidation() called</strong><br>
Via: data-action="validate" (alias)<br>
Value: "${target.value}"<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleDataFilter(event, target) {
this.logAction('filter', 'handleDataFilter', `Filtering: "${target.value}"`);
document.getElementById('input-output').innerHTML = `
<strong>🔽 handleDataFilter() called</strong><br>
Via: data-action="filter" (alias)<br>
Filter: "${target.value}"<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleAutocomplete(event, target) {
this.logAction('autocomplete', 'handleAutocomplete', `Autocomplete: "${target.value}"`);
document.getElementById('input-output').innerHTML = `
<strong>💡 handleAutocomplete() called</strong><br>
Via: data-action="autocomplete" (alias)<br>
Input: "${target.value}"<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
// Mixed action demonstrations (same action name, different event types)
handleClickProcess(event, target) {
this.logAction('process', 'handleClickProcess', 'Click processing started');
document.getElementById('mixed-output').innerHTML = `
<strong>⚡ handleClickProcess() called</strong><br>
Event: click, Action: "process"<br>
Different handler than input process!<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleInputProcess(event, target) {
this.logAction('process', 'handleInputProcess', `Input processing: "${target.value}"`);
document.getElementById('mixed-output').innerHTML = `
<strong>⚡ handleInputProcess() called</strong><br>
Event: input, Action: "process"<br>
Value: "${target.value}"<br>
Different handler than click process!<br>
<small>Time: ${new Date().toLocaleTimeString()}</small>
`;
}
handleClearLog(event, target) {
document.getElementById('action-log').innerHTML = 'Action history cleared...';
this.actionCount = 0;
}
// Utility method
logAction(action, handler, description) {
this.actionCount++;
const logDiv = document.getElementById('action-log');
const entry = document.createElement('div');
entry.style.cssText = 'margin: 5px 0; padding: 5px; background: #f0f0f0; border-radius: 3px;';
entry.innerHTML = `
<strong>#${this.actionCount}</strong>
<code>data-action="${action}"</code> →
<code>${handler}()</code><br>
<small>${description} at ${new Date().toLocaleTimeString()}</small>
`;
logDiv.appendChild(entry);
logDiv.scrollTop = logDiv.scrollHeight;
}
}
// Initialize test
const test = new DataActionAliasTest();
// Log initialization info
console.log('Data-Action Aliases Test initialized!');
console.log('Click aliases:', test.aliases.click);
console.log('Input aliases:', test.aliases.input);
console.log('Event listeners:', test.eventListeners.size, '(should be 2: body click + body input)');
</script>
</body>
</html>