ypsilon-event-handler
Version:
A production-ready event handling system for web applications with memory leak prevention, and method chaining support
367 lines (329 loc) โข 19.7 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Basic Example - YpsilonEventHandler</title>
<meta name="description" content="YpsilonEventHandler - Event Listener Example Page">
<link rel="icon" type="image/x-icon" href="./favicon.ico">
<link rel="stylesheet" type="text/css" href="./assets/main.css">
<style>
body {
font-family: system-ui, sans-serif;
background: #f5f5f5;
margin: 0;
padding: 0;
}
.container-wrapper {
max-width: 640px;
margin: 50px auto;
padding: 20px;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
input {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 5px;
}
.output {
background: #f8f9fa;
margin: 10px 0;
padding: 10px;
width: 100%;
max-width: 340px;
max-height: 200px;
position: fixed;
left: 3px;
bottom: 3px;
white-space: pre;
overflow: auto;
border-radius: 4px;
box-shadow: 0 0 5px #999;
font-size: .8rem;
font-family: monospace;
}
.scroll-area {
height: 300px;
overflow-y: scroll;
background: linear-gradient(to bottom, #e3f2fd, #bbdefb);
border: 2px solid #2196f3;
border-radius: 8px;
padding: 20px;
}
.scroll-content {
height: 800px;
background: linear-gradient(to bottom, #fff3e0, #ffcc02);
padding: 20px;
border-radius: 4px;
}
.passive-indicator {
background: #4caf50;
color: white;
padding: 8px 12px;
border-radius: 4px;
font-size: 12px;
margin: 10px 0;
}
.footer {
text-align: center;
}
nav div {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
}
nav a {
margin: 0;
padding: 8px;
flex: 1 20%;
white-space: nowrap;
}
</style>
</head>
<body>
<div class="container-wrapper">
<div class="container">
<h1>YpsilonEventHandler Example</h1>
<div class="passive-indicator" id="passive-status">
Passive Support: Checking...
</div>
<div style="background: #e3f2fd; padding: 10px; border-radius: 4px; margin: 10px 0; font-size: 12px;">
<strong>๐ Scroll Metrics:</strong><br>
Window: <span id="window-scroll">0</span>px / <span id="window-max-scroll">0</span>px max (total: <span id="window-height">0</span>px, visible: <span id="window-inner-height">0</span>px)<br>
Scroll Div: <span id="div-scroll">0</span>px / <span id="div-max-scroll">0</span>px max (total: <span id="div-height">0</span>px, visible: 300px)
</div>
<div style="margin: 15px 0;">
<button class="btn-primary" style="background: #007bff;">๐ต Primary Button</button>
<button class="btn-danger" style="background: #dc3545;">๐ด Danger Button</button>
<button class="btn-success" style="background: #28a745;">๐ข Success Button</button>
</div>
<input type="text" id="test-input" placeholder="Type here (debounced 300ms)">
<div style="margin: 15px 0;">
<button id="destroy-btn" style="background: #6c757d;">๐๏ธ Destroy Handler</button>
<button id="recreate-btn" style="background: #17a2b8;" disabled>๐ Recreate Handler</button>
<span id="handler-state" style="margin-left: 10px; font-weight: bold; color: #28a745;">ACTIVE</span>
</div>
<div style="font-size: 12px; color: #666; margin: 10px 0 15px;">
๐ฏ <strong>Custom Handlers:</strong> Each button uses a different click handler function<br>
๐ก Input is debounced (300ms) - handler only fires after you stop typing<br>
๐ก Scroll events are throttled (100ms div, 250ms window) - smooth performance<br>
๐ก All scroll events automatically use passive listeners<br>
๐งช <strong>Test destroy:</strong> Click "Destroy Handler" then try clicking/typing/scrolling!
</div>
<div class="output" id="output">Events will appear here...</div>
</div>
<div class="container">
<h2>Scroll Test (Passive Listeners)</h2>
<p>Scroll in the area below to test passive scroll events:</p>
<div class="scroll-area" id="scroll-area">
<div class="scroll-content">
<h3>Scrollable Content</h3>
<p>This scroll area demonstrates passive event listeners.</p>
<p>When you scroll here, the event will be handled with passive:true automatically.</p>
<p>This improves performance by telling the browser that preventDefault() won't be called.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.</p>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.</p>
<p>Keep scrolling to see more content...</p>
<p>The scroll events are being logged to the output area above.</p>
<p>Notice how smooth the scrolling is with passive listeners!</p>
<p>This is the power of automatic passive listener detection.</p>
<p>YpsilonEventHandler handles this optimization automatically.</p>
<p>No manual configuration needed!</p>
<p>You're near the bottom now.</p>
<p>Final paragraph of scrollable content.</p>
</div>
</div>
<div>
<h2>Lorem ipsum dolor sit amet?</h2>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
<p>Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.</p>
<hr />
<p>Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.</p>
<p>Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.</p>
<hr />
<p>Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.</p>
<p>Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.</p>
<p>Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.</p>
<p>Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.</p>
</div>
</div>
<div class="container">
<h2>Scroll Test (No Throttling)</h2>
<p>Scroll in the area below to test unthrottled scroll events:</p>
<div class="scroll-area" id="scroll-area-2">
<div class="scroll-content">
<h3>Unthrottled Scrollable Content</h3>
<p>This scroll area demonstrates scroll events without throttling.</p>
<p>When you scroll here, every scroll event will be handled immediately.</p>
<p>Compare this with the throttled scroll area above to see the difference.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p>
<p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.</p>
<p>Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.</p>
<p>Keep scrolling to see more content...</p>
<p>The scroll events are being logged to the output area above.</p>
<p>Notice how many more events are fired without throttling!</p>
<p>This demonstrates the importance of throttling for performance.</p>
<p>YpsilonEventHandler handles both throttled and unthrottled events.</p>
<p>Choose the right approach based on your use case!</p>
<p>You're near the bottom now.</p>
<p>Final paragraph of unthrottled scrollable content.</p>
</div>
</div>
</div>
<div class="nav-container">
<!-- Navigation to other examples -->
<nav class="main-nav">
<div>
<a href="./index.html" class="btn-primary">Start</a>
<a href="./basic-example.html" class="btn-disabled">Basic Example</a>
<a href="./reactive-y.html" class="btn-danger">Reactive Demo</a>
<a href="./single-listener-multiple-actions.html" class="btn-purple">Single Listener</a>
<a href="./spa.html" class="btn-success">SPA Demo</a>
<a href="./ai-reviews.html" class="btn-warning">AI Reviews</a>
<a href="https://github.com/eypsilon/YpsilonEventHandler" class="btn-dark">GitHub</a>
</div>
</nav>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/ypsilon-event-handler@1.5.0/ypsilon-event-handler.min.js"></script>
<script>
// Extend YpsilonEventHandler with custom handlers
class MyEventHandler extends YpsilonEventHandler {
constructor() {
super({
'.btn-primary': [
{ type: 'click', handler: 'handlePrimaryClick' }
],
'.btn-danger': [
{ type: 'click', handler: 'handleDangerClick', throttle: 500 }
],
'.btn-success': [
{ type: 'click', handler: 'handleSuccessClick', debounce: 300 }
],
'#test-input': [
{ type: 'input', handler: 'handleInputChange', debounce: 300 }
],
'#scroll-area': [
{ type: 'scroll', handler: 'handleAreaScroll', throttle: 100 }
],
'#scroll-area-2': [
{ type: 'scroll', handler: 'handleAreaScroll2' }
],
'window': [
{ type: 'scroll', handler: 'handleWindowScroll', throttle: 250 }
]
});
}
handlePrimaryClick(event, target) {
this.log(`๐ต PRIMARY clicked: ${target.textContent} (instant)`);
}
handleDangerClick(event, target) {
this.log(`๐ด DANGER clicked: ${target.textContent} (throttled 500ms)`);
}
handleSuccessClick(event, target) {
this.log(`๐ข SUCCESS clicked: ${target.textContent} (debounced 300ms)`);
}
handleInputChange(event, target) {
this.log(`๐ INPUT changed: "${target.value}" (debounced 300ms)`);
}
handleAreaScroll(event, target) {
const scrollTop = target.scrollTop || 0;
document.getElementById('div-scroll').textContent = Math.round(scrollTop);
this.log(`๐ DIV scroll: ${Math.round(scrollTop)}px (throttled 100ms)`);
}
handleAreaScroll2(event, target) {
const scrollTop = target.scrollTop || 0;
this.log(`๐ DIV2 scroll: ${Math.round(scrollTop)}px (no throttling)`);
}
handleWindowScroll(event, target) {
const scrollTop = window.scrollY || window.pageYOffset || 0;
document.getElementById('window-scroll').textContent = Math.round(scrollTop);
this.log(`๐ WINDOW scroll: ${Math.round(scrollTop)}px (throttled 250ms)`);
}
log(message) {
const output = document.getElementById('output');
const div = document.createElement('div');
div.textContent = message;
output.appendChild(div);
output.scrollTop = output.scrollHeight;
}
}
// Initialize
let handler = new MyEventHandler();
window.handler = handler; // Make globally accessible
// Show passive support status
document.getElementById('passive-status').textContent =
`Passive Support: ${handler.passiveSupported ? 'Enabled โ' : 'Not Available โ'}`;
// Destroy/Recreate functionality (separate from main handler to avoid circular dependency)
const destroyBtn = document.getElementById('destroy-btn');
const recreateBtn = document.getElementById('recreate-btn');
const handlerState = document.getElementById('handler-state');
destroyBtn.addEventListener('click', () => {
handler.destroy();
handler.log('๐๏ธ Handler DESTROYED - try clicking/typing/scrolling now!');
// Update UI
destroyBtn.disabled = true;
recreateBtn.disabled = false;
handlerState.textContent = 'DESTROYED';
handlerState.style.color = '#dc3545';
});
recreateBtn.addEventListener('click', () => {
handler = new MyEventHandler();
window.handler = handler; // Update global reference
handler.log('๐ Handler RECREATED - events are working again!');
// Update UI
destroyBtn.disabled = false;
recreateBtn.disabled = true;
handlerState.textContent = 'ACTIVE';
handlerState.style.color = '#28a745';
});
// Clean up on page unload
window.addEventListener('beforeunload', () => handler.destroy());
// Initialize scroll metrics
function initializeMetrics() {
const windowHeight = document.documentElement.scrollHeight;
const windowInnerHeight = window.innerHeight;
const windowMaxScroll = windowHeight - windowInnerHeight;
const scrollArea = document.getElementById('scroll-area');
const divHeight = scrollArea.scrollHeight;
const divVisibleHeight = scrollArea.clientHeight;
const divMaxScroll = divHeight - divVisibleHeight;
// Set static values
document.getElementById('window-height').textContent = Math.round(windowHeight);
document.getElementById('window-inner-height').textContent = windowInnerHeight;
document.getElementById('window-max-scroll').textContent = Math.round(windowMaxScroll);
document.getElementById('div-height').textContent = divHeight;
document.getElementById('div-max-scroll').textContent = divMaxScroll;
// Set initial scroll positions
document.getElementById('window-scroll').textContent = Math.round(window.scrollY);
document.getElementById('div-scroll').textContent = Math.round(scrollArea.scrollTop);
}
initializeMetrics();
// Log initial state
handler.log('YpsilonEventHandler initialized');
handler.log(`Passive listeners: ${handler.passiveSupported ? 'supported' : 'not supported'}`);
</script>
</body>
</html>