mercoledì 8 ottobre 2025

Corso di JavaScript & Programmazione Web: 7 – Eventi e interattività

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.target per 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 submit e preventDefault() 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 input per validazione in tempo reale (migliore di keyup perché copre incollamenti e altri input).

  • Funzioni validateEmail e validatePassword usano regex; sono esempi pratici: regole di password si possono adattare.

  • debounce evita troppe esecuzioni successive migliorando UX e performance.

  • novalidate sul form disabilita i messaggi browser standard, così mostriamo i nostri.

  • Il submit usa preventDefault() e una simulazione setTimeout per dimostrare comportamento asincrono; in produzione qui fai fetch() verso API.

  • event delegation è mostrato con gestione generica su card per 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 per statusText).

  • Implementare autenticazione reale via fetch() con rate-limiting, CSRF token e hashing sul server.

  • Usare IntersectionObserver se 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

Corso Fondamenti di Informatica e Reti: 6 Reti di computer e Internet

Reti di computer e Internet Introduzione Prova a pensare alla vita quotidiana senza reti informatiche: niente messaggi WhatsApp, niente m...