ypsilon-event-handler
Version:
A production-ready event handling system for web applications with memory leak prevention, and method chaining support
293 lines (267 loc) • 10.3 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Simple Reactive Inputs - YpsilonEventHandler</title>
<meta name="description" content="Simple reactive input example">
<link rel="icon" type="image/x-icon" href="./favicon.ico">
<link rel="stylesheet" type="text/css" href="./assets/main.css">
<style>
body {
font-family: sans-serif;
background: #f9f9f9;
margin: 0;
padding: 0;
}
.demo-wrapper {
max-width: 640px;
margin: 0 auto;
padding: 2rem 0 1rem;
background: #f9f9f9;
}
h1, h2, h3 {
margin-top: 0;
}
header,
.demo-box {
background: white;
padding: 2rem;
margin: 1rem 0;
border-radius: 8px;
border: 1px solid #ddd;
}
input {
padding: 0.5rem;
margin: 0.5rem 0;
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
box-sizing: border-box;
&[type="checkbox"] {
width: 20px;
}
}
.output {
background: #f0f8ff;
padding: 1rem;
margin: 0.5rem 0;
border-radius: 4px;
border: 1px solid #4CAF50;
min-height: 40px;
display: flex;
align-items: center;
}
.output:empty::before {
content: "Type in the input above...";
opacity: 0.5;
font-style: italic;
}
.toggle-target {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 1rem;
margin: 0.5rem 0;
border-radius: 4px;
transition: opacity 0.3s ease;
}
.toggle-target.hidden {
display: none;
}
label {
background: #f0f8ffaa;
border: 1px solid #4CAF50aa;
display: block;
padding: .7rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
border-radius: 4px;
transition: all 0.2s ease;
}
label.active {
background: #4CAF50;
color: white;
border-color: #45a049;
font-weight: bold;
}
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="demo-wrapper">
<header>
<h1>🔥 Reactive Inputs</h1>
<p>How reactive is Y? <strong>Y!</strong></p>
<hr />
<p style="margin-bottom:0">Type in any input and watch the target update instantly!</p>
</header>
<div class="demo-box">
<h2>Input 1 → Single Output</h2>
<input type="text" name="demo-1" placeholder="Type something..." data-target="#output1">
<div class="output" id="output1"></div>
</div>
<div class="demo-box">
<h2>Input 2 → Multiple Outputs! 🤯</h2>
<input type="text" name="demo-2" placeholder="Watch multiple elements update..." data-target="#output2, .output2-class">
<div class="output" id="output2"></div>
<div class="output output2-class">Class-based target</div>
<div class="output output2-class">Another class target</div>
</div>
<div class="demo-box">
<h2>Input 3 → Complex Selectors!</h2>
<input type="text" name="demo-3" placeholder="Complex selector demo..." data-target=".output3, [data-reactive='advanced']">
<div class="output output3">Class selector target</div>
<div class="output output3">Another class target</div>
<div class="output" data-reactive="advanced">Data attribute target</div>
<div class="output" data-reactive="advanced">Another data target</div>
</div>
<div class="demo-box">
<h3>☑️ Reactive Checkboxes</h3>
<p>Toggle elements in and out of existence:</p>
<label>
<input type="checkbox" id="toggle1" data-toggle=".box1">
Show/Hide Yellow Box
</label>
<div class="toggle-target box1 hidden">🟡 This is Box 1! I can be toggled!</div>
<hr />
<label>
<input type="checkbox" id="toggle2" data-toggle=".box2, .box3">
Toggle Multiple Boxes
</label>
<div class="toggle-target box2 hidden">🟢 This is Box 2!</div>
<div class="toggle-target box3 hidden">🔵 This is Box 3!</div>
<hr />
<label>
<input type="checkbox" id="toggle-all" data-toggle-checkboxes="#toggle1, #toggle2">
Master Toggle (Controls Other Checkboxes)
</label>
</div>
<div class="demo-box">
<h3>🎯 The Magic</h3>
<p><strong>Two listeners</strong> handle all inputs/checkboxes and update ALL matching targets automatically!</p>
<p>✅ No individual listeners<br>
✅ No manual binding<br>
✅ Pure event delegation<br>
✅ <strong>CSS selector support</strong><br>
✅ <strong>Comma-separated multiple targets</strong><br>
✅ <strong>Text inputs + checkbox toggles</strong></p>
<h4>Reactive Patterns:</h4>
<ul>
<li><code>data-target="#myId"</code> → Update text content</li>
<li><code>data-target=".myClass"</code> → Update multiple elements</li>
<li><code>data-toggle=".box"</code> → Show/hide elements</li>
<li><code>data-toggle=".box1, .box2"</code> → Toggle multiple elements</li>
<li><code>data-toggle-checkboxes="#cb1, #cb2"</code> → Control other checkboxes</li>
</ul>
<p><strong>Safe and explicit</strong> - only affects exactly what you specify!</p>
</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-secondary">Basic Example</a>
<a href="./reactive-y.html" class="btn-disabled">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>
<!-- Include YpsilonEventHandler -->
<script src="https://cdn.jsdelivr.net/npm/ypsilon-event-handler@1.5.0/ypsilon-event-handler.min.js"></script>
<script>
class SimpleReactiveHandler extends YpsilonEventHandler {
constructor() {
super({
body: [
{ type: 'input', debounce: 100 },
'change'
]
});
}
handleInput(event, target) {
// Basic validation - only handle inputs with data-target
if (!target.dataset.target) return;
const targetSelectors = target.dataset.target;
// Split comma-separated selectors and update all matching elements
targetSelectors.split(',').forEach(selector => {
const trimmedSelector = selector.trim();
if (trimmedSelector) {
const outputElements = document.querySelectorAll(trimmedSelector);
outputElements.forEach(element => {
element.textContent = target.value;
});
}
});
}
handleChange(event, target) {
// Handle checkbox active class on parent label
if (target.type === 'checkbox') {
const parentLabel = target.closest('label');
if (parentLabel) {
if (target.checked) {
parentLabel.classList.add('active');
} else {
parentLabel.classList.remove('active');
}
}
}
// Handle checkbox that controls other checkboxes
if (target.type === 'checkbox' && target.dataset.toggleCheckboxes) {
const checkboxSelectors = target.dataset.toggleCheckboxes;
// Split comma-separated selectors and set all matching checkboxes
checkboxSelectors.split(',').forEach(selector => {
const trimmedSelector = selector.trim();
if (trimmedSelector) {
const checkboxElements = document.querySelectorAll(trimmedSelector);
checkboxElements.forEach(checkbox => {
if (checkbox.type === 'checkbox') {
checkbox.checked = target.checked;
// Trigger change event to update their targets
checkbox.dispatchEvent(new Event('change', { bubbles: true }));
}
});
}
});
}
// Handle regular checkbox toggles for elements
if (target.type === 'checkbox' && target.dataset.toggle) {
const toggleSelectors = target.dataset.toggle;
// Split comma-separated selectors and toggle all matching elements
toggleSelectors.split(',').forEach(selector => {
const trimmedSelector = selector.trim();
if (trimmedSelector) {
const toggleElements = document.querySelectorAll(trimmedSelector);
toggleElements.forEach(element => {
if (target.checked) {
element.classList.remove('hidden');
} else {
element.classList.add('hidden');
}
});
}
});
}
}
}
// Initialize
const handler = new SimpleReactiveHandler();
console.log('Simple reactive handler initialized!');
</script>
</body>
</html>