7 – Eventi e interattività
Obiettivi
-
Capire come funzionano gli event listeners (click, mouseover, keyup…).
-
Imparare a validare form in tempo reale.
-
Conoscere event bubbling e event delegation per codice più efficiente.
-
Sviluppare un esercizio pratico: form di login con validazione in tempo reale.
1) Event listeners: concetti e API base
-
Un evento è un’azione dell’utente o del browser (click, submit, keyup, mouseover, resize…).
-
Aggiungere un listener:
element.addEventListener('click', function(event){
// comportamento quando avviene il click
});
-
Rimuovere un listener:
element.removeEventListener('click', listenerFn);
-
Proprietà utili nell’oggetto event:
event.target(elemento che ha generato l’evento),event.currentTarget(elemento che gestisce l’evento),event.preventDefault()per bloccare il comportamento predefinito (es. submit),event.stopPropagation()per fermare la propagazione.
2) Tipi di eventi più comuni (esempi)
-
click— click del mouse su un elemento. -
mouseover/mouseout— passaggio del cursore su/da un elemento. -
keyup/keydown— tastiera (utile per validazione in tempo reale). -
submit— invio di un form (si può intercettare per validare). -
input— si attiva ad ogni modifica del valore di input (raccomandato per validazione live).
3) Event bubbling e event delegation
-
Event bubbling: un evento si propaga dal nodo target verso l’alto nella gerarchia DOM (bubble phase). Esiste anche la capture phase (dall’alto verso il basso) se lo si usa.
-
Event delegation: invece di aggiungere un listener su ogni child, si aggiunge un unico listener al parent e si usa
event.targetper capire quale child ha scatenato l’evento. Ripulisce il codice e funziona anche con elementi dinamici aggiunti dopo il binding.
Esempio di delega:
parentElement.addEventListener('click', (e) => {
const btn = e.target.closest('.btn');
if (!btn) return;
// gestisci click sul bottone dentro parent
});
4) Form e validazione input — principi
-
Validazione lato client: migliora UX e riduce richieste al server, ma non sostituisce la validazione lato server (per sicurezza).
-
Strategie:
-
Validazione in tempo reale (usare
input/keyup). -
Validazione al submit (usare
submitepreventDefault()se ci sono errori). -
Fornire messaggi chiari, icone di successo/errore e focus sugli errori.
-
Usare constraint HTML5 (
required,type="email",minlength) + JS per regole custom (es. controllo password).
-
5) Esercizio completo: Form di login con validazione in tempo reale
Snippet pronto per Blogger: HTML + CSS minimo + JS. Copia e incolla dentro un post in modalità HTML. Il codice è commentato e compatto, senza dipendenze esterne.
<!-- LOGIN FORM: copialo nella modalità HTML del tuo post Blogger -->
<div style="font-family: Arial, Helvetica, sans-serif; max-width:420px; margin:16px auto;">
<style>
.card { border:1px solid #e0e0e0; padding:18px; border-radius:8px; box-shadow:0 1px 3px rgba(0,0,0,0.04); background:#fff; }
label { display:block; margin-bottom:6px; font-weight:600; font-size:14px; }
input { width:100%; padding:10px 12px; margin-bottom:8px; border:1px solid #ccc; border-radius:6px; font-size:14px; box-sizing:border-box; }
.input-group { margin-bottom:12px; position:relative; }
.hint { font-size:12px; color:#666; margin-top:4px; }
.error { color:#b00020; font-size:13px; margin-top:4px; display:none; }
.success { color:#006400; font-size:13px; margin-top:4px; display:none; }
button { padding:10px 14px; font-size:15px; border-radius:6px; border:0; background:#0077cc; color:#fff; cursor:pointer; width:100%; }
button:disabled { background:#9ecae1; cursor:not-allowed; }
.small { font-size:13px; color:#444; margin-top:8px; text-align:center; }
.field-valid { border-color:#2e8b57 !important; box-shadow:0 0 0 3px rgba(46,139,87,0.06); }
.field-invalid { border-color:#b00020 !important; box-shadow:0 0 0 3px rgba(176,0,32,0.06); }
</style>
<div class="card" id="loginCard">
<h3 style="margin-top:0; margin-bottom:8px;">Login</h3>
<form id="loginForm" novalidate>
<div class="input-group">
<label for="email">Email</label>
<input id="email" name="email" type="email" placeholder="esempio@dominio.com" required>
<div class="hint">Inserisci la tua email.</div>
<div class="error" id="emailError">Email non valida.</div>
<div class="success" id="emailOk">Email valida ✓</div>
</div>
<div class="input-group">
<label for="password">Password</label>
<input id="password" name="password" type="password" placeholder="Minimo 8 caratteri" required minlength="8">
<div class="hint">Minimo 8 caratteri, almeno 1 lettera e 1 numero.</div>
<div class="error" id="passError">Password debole.</div>
<div class="success" id="passOk">Password valida ✓</div>
</div>
<div style="display:flex; gap:8px; margin-bottom:10px;">
<label style="font-weight:400; font-size:13px;"><input id="remember" type="checkbox"> Ricordami</label>
</div>
<button id="submitBtn" type="submit" disabled>Accedi</button>
<div class="small" aria-live="polite" id="statusText">Compila i campi per abilitare il pulsante.</div>
</form>
</div>
<script>
(function(){
// Elementi
const form = document.getElementById('loginForm');
const email = document.getElementById('email');
const pass = document.getElementById('password');
const submitBtn = document.getElementById('submitBtn');
const statusText = document.getElementById('statusText');
const emailError = document.getElementById('emailError');
const emailOk = document.getElementById('emailOk');
const passError = document.getElementById('passError');
const passOk = document.getElementById('passOk');
// Regole di validazione
function validateEmail(v){
// controllo semplice RFC-ish (non perfetto ma efficace)
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v.trim());
}
function validatePassword(v){
// almeno 8 char, almeno una lettera e un numero
return /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$/.test(v);
}
// Aggiorna stato visivo di un campo
function setFieldState(input, okElement, errElement, valid){
if(valid){
input.classList.remove('field-invalid'); input.classList.add('field-valid');
errElement.style.display = 'none'; okElement.style.display = 'block';
} else {
input.classList.remove('field-valid'); input.classList.add('field-invalid');
errElement.style.display = 'block'; okElement.style.display = 'none';
}
}
// Controllo live (input event)
function updateState(){
const emailVal = email.value;
const passVal = pass.value;
const emailIsValid = validateEmail(emailVal);
const passIsValid = validatePassword(passVal);
// visual feedback
if(emailVal.length === 0){
email.classList.remove('field-valid','field-invalid');
emailError.style.display = 'none'; emailOk.style.display = 'none';
} else {
setFieldState(email, emailOk, emailError, emailIsValid);
}
if(passVal.length === 0){
pass.classList.remove('field-valid','field-invalid');
passError.style.display = 'none'; passOk.style.display = 'none';
} else {
setFieldState(pass, passOk, passError, passIsValid);
}
// abilita submit solo se entrambi validi
if(emailIsValid && passIsValid){
submitBtn.disabled = false;
statusText.textContent = 'Pronto per effettuare il login.';
} else {
submitBtn.disabled = true;
statusText.textContent = 'Compila correttamente tutti i campi per abilitare il pulsante.';
}
}
// debounce semplice per input/keyup (bottleneck UX)
function debounce(fn, wait=150){
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(()=> fn(...args), wait);
};
}
const debouncedUpdate = debounce(updateState, 120);
// Event listeners: input in tempo reale
email.addEventListener('input', debouncedUpdate);
pass.addEventListener('input', debouncedUpdate);
// Submit: validazione finale e prevenzione default
form.addEventListener('submit', function(e){
e.preventDefault(); // non inviare realmente
// controllo di sicurezza
if(!validateEmail(email.value) || !validatePassword(pass.value)){
statusText.textContent = 'Errore: campi non validi. Correggi prima di inviare.';
return;
}
// Simulazione login: qui chiameresti fetch/AJAX
submitBtn.disabled = true;
submitBtn.textContent = 'Accedendo...';
statusText.textContent = 'Effettuo l\'autenticazione (simulata)...';
// simulazione asincrona
setTimeout(()=> {
submitBtn.disabled = false;
submitBtn.textContent = 'Accedi';
statusText.textContent = 'Login riuscito (simulazione). Reindirizzamento...';
// qui potresti fare window.location = '/dashboard' o simili
}, 1100);
});
// Usare event delegation: esempio dimostrativo (gestire click su elementi che possono essere aggiunti dinamicamente)
const card = document.getElementById('loginCard');
card.addEventListener('click', (e) => {
// se clicchi su un elemento con data-action, gestiscilo genericamente
const el = e.target.closest('[data-action]');
if(!el) return;
const action = el.getAttribute('data-action');
if(action === 'help'){
alert('Inserisci email e password. Password: almeno 8 caratteri, 1 lettera e 1 numero.');
}
});
// inizializza stato
updateState();
})();
</script>
</div>
6) Spiegazione del codice (punti chiave)
-
Usiamo
inputper validazione in tempo reale (migliore dikeyupperché copre incollamenti e altri input). -
Funzioni
validateEmailevalidatePasswordusano regex; sono esempi pratici: regole di password si possono adattare. -
debounceevita troppe esecuzioni successive migliorando UX e performance. -
novalidatesul form disabilita i messaggi browser standard, così mostriamo i nostri. -
Il submit usa
preventDefault()e una simulazionesetTimeoutper dimostrare comportamento asincrono; in produzione qui faifetch()verso API. -
event delegationè mostrato con gestione generica sucardper elementi dinamici (utile quando aggiungi bottoni dinamicamente).
7) Varianti e miglioramenti suggeriti
-
Mostrare icone SVG (✓ / ✕) vicino ai campi per un feedback visivo istantaneo.
-
Aggiungere suggerimenti di sicurezza (es. strength meter password).
-
Mostrare messaggi ARIA per accessibilità (
aria-live="polite"già utile perstatusText). -
Implementare autenticazione reale via
fetch()con rate-limiting, CSRF token e hashing sul server. -
Usare
IntersectionObserverse vuoi validare solo campi visibili in pagina molto lunga.
8) Mini-esempi addizionali rapidi (copy-paste)
Aggiungere un click listener con rimozione:
const btn = document.querySelector('.myBtn');
function handleClick(e){ console.log('clic!'); }
btn.addEventListener('click', handleClick);
// più tardi
btn.removeEventListener('click', handleClick);
Uso di event.capture (rare):
parent.addEventListener('click', (e)=>{ console.log('capture phase'); }, true);
// true => handler in capture phase (dall'alto verso il basso)

Nessun commento:
Posta un commento