JavaScript e il modello a eventi: usare <strong>addEventListener</strong> in modo professionale

by Anastasia P.
SHARE
JavaScript e il modello a eventi: usare <strong>addEventListener</strong> in modo professionale
© Guida-HTML5.it

Introduzione

Il modello a eventi è uno dei pilastri di JavaScript, soprattutto nel browser. Ogni volta che un utente clicca un pulsante, digita in un campo, passa il mouse sopra un elemento o invia un form, il sistema genera un evento. Il codice JavaScript può “ascoltare” questi eventi e reagire in modo dinamico.

In questo tutorial ci concentriamo su un sotto-argomento molto pratico e spesso sottovalutato: come registrare, gestire e rimuovere correttamente i listener degli eventi con addEventListener. È un tema fondamentale perché, nella pratica, scrivere codice event-driven non significa solo “far funzionare un click”, ma anche mantenere il codice pulito, riutilizzabile e privo di problemi di memoria o comportamenti inattesi.

Vedremo un esempio reale con una piccola interfaccia che permette di aggiungere attività a una lista, marcarle come completate e filtrare gli elementi. Questo scenario è semplice, ma rappresenta bene molte applicazioni web reali.

Codice completo

<div id="app">
  <h2>Lista attività</h2>

  <form id="taskForm">
    <input id="taskInput" type="text" placeholder="Scrivi una nuova attività" />
    <button type="submit">Aggiungi</button>
  </form>

  <div class="filters">
    <button data-filter="all">Tutte</button>
    <button data-filter="active">Attive</button>
    <button data-filter="done">Completate</button>
  </div>

  <ul id="taskList"></ul>
</div>

<script>
  const taskForm = document.querySelector(´#taskForm´);
  const taskInput = document.querySelector(´#taskInput´);
  const taskList = document.querySelector(´#taskList´);
  const filterButtons = document.querySelectorAll(´[data-filter]´);

  const state = {
    tasks: [],
    filter: ´all´
  };

  function renderTasks() {
    taskList.innerHTML = ´´;

    const filteredTasks = state.tasks.filter(task => {
      if (state.filter === ´active´) return !task.done;
      if (state.filter === ´done´) return task.done;
      return true;
    });

    filteredTasks.forEach(task => {
      const li = document.createElement(´li´);
      li.dataset.id = task.id;

      const checkbox = document.createElement(´input´);
      checkbox.type = ´checkbox´;
      checkbox.checked = task.done;
      checkbox.className = ´task-toggle´;

      const text = document.createElement(´span´);
      text.textContent = task.text;
      if (task.done) {
        text.style.textDecoration = ´line-through´;
      }

      const removeButton = document.createElement(´button´);
      removeButton.textContent = ´Rimuovi´;
      removeButton.className = ´task-remove´;

      li.appendChild(checkbox);
      li.appendChild(text);
      li.appendChild(removeButton);

      taskList.appendChild(li);
    });
  }

  taskForm.addEventListener(´submit´, function (event) {
    event.preventDefault();

    const value = taskInput.value.trim();
    if (value === ´´) return;

    state.tasks.push({
      id: crypto.randomUUID(),
      text: value,
      done: false
    });

    taskInput.value = ´´;
    renderTasks();
  });

  taskList.addEventListener(´click´, function (event) {
    const removeButton = event.target.closest(´.task-remove´);
    if (removeButton) {
      const li = removeButton.closest(´li´);
      const taskId = li.dataset.id;

      state.tasks = state.tasks.filter(task => task.id !== taskId);
      renderTasks();
    }
  });

  taskList.addEventListener(´change´, function (event) {
    if (!event.target.classList.contains(´task-toggle´)) return;

    const li = event.target.closest(´li´);
    const taskId = li.dataset.id;

    const task = state.tasks.find(task => task.id === taskId);
    if (task) {
      task.done = event.target.checked;
      renderTasks();
    }
  });

  filterButtons.forEach(button => {
    button.addEventListener(´click´, function () {
      state.filter = button.dataset.filter;
      renderTasks();
    });
  });

  renderTasks();
</script>

Spiegazione

Il cuore di questo esempio è il concetto di listener: una funzione che viene eseguita quando si verifica un evento specifico. In JavaScript, il modo più moderno e consigliato per registrare un listener è addEventListener.

1. Gestire il submit di un form

Nel nostro esempio, il form serve per aggiungere una nuova attività. Ascoltiamo l’evento submit:

  • event.preventDefault() blocca il comportamento predefinito del form, cioè il refresh della pagina.
  • Leggiamo il valore dell’input e lo puliamo con trim().
  • Se il testo è vuoto, interrompiamo l’operazione.
  • Creiamo un nuovo oggetto task e lo aggiungiamo allo stato.
  • Infine, svuotiamo l’input e ridisegniamo la lista.

Questo approccio è tipico delle applicazioni web: gli eventi non modificano direttamente il DOM in modo casuale, ma aggiornano uno stato centrale e poi la UI viene renderizzata di conseguenza.

2. Usare la delegazione degli eventi

Per gestire il click sul pulsante “Rimuovi” e il cambio dello stato checkbox, non aggiungiamo un listener a ogni singolo elemento. Invece, registriamo un solo listener sul contenitore della lista:

  • taskList.addEventListener(´click´, ...)
  • taskList.addEventListener(´change´, ...)

Questa tecnica si chiama delegazione degli eventi. È molto utile quando gli elementi vengono creati dinamicamente, come nel nostro caso. Se aggiungessimo listener a ogni task dopo il render, dovremmo ricollegarli ogni volta che la lista cambia. Con la delegazione, invece, il listener resta sul contenitore e intercetta gli eventi che “risalgono” dai figli.

3. Individuare il bersaglio corretto con closest()

Quando l’utente clicca il pulsante di rimozione, l’evento potrebbe partire da un nodo interno. Per questo usiamo event.target.closest(´.task-remove´) e closest(´li´). In questo modo troviamo l’elemento giusto anche se il click avviene su un’icona o su un contenuto annidato.

È una pratica robusta e molto utile nelle interfacce complesse.

4. Filtrare i dati con bottoni dedicati

I pulsanti filtro sono un altro esempio di gestione eventi semplice ma efficace. Ogni bottone ha un attributo data-filter che indica il filtro da applicare. Quando l’utente clicca, aggiorniamo state.filter e richiamiamo renderTasks().

Questo approccio separa bene responsabilità diverse:

  • gli eventi raccolgono l’input dell’utente;
  • lo stato conserva i dati;
  • il rendering aggiorna la UI.

5. Perché addEventListener è preferibile

Rispetto agli handler inline o alle vecchie proprietà come onclick, addEventListener offre vantaggi importanti:

  • puoi associare più listener allo stesso evento;
  • puoi rimuovere facilmente un listener con removeEventListener;
  • separa meglio HTML e JavaScript;
  • supporta opzioni avanzate come once, capture e passive.

Best practice

  • Usa addEventListener invece di inline handler: il codice è più pulito e manutentibile.
  • Preferisci la delegazione per elementi dinamici: riduce il numero di listener e semplifica il rendering.
  • Separa stato e UI: aggiorna i dati e poi ridisegna l’interfaccia in un’unica funzione.
  • Evita di duplicare listener: se un componente viene ricreato, verifica di non registrare più volte gli stessi eventi.
  • Usa nomi di classi e attributi chiari: ad esempio data-filter, .task-remove, .task-toggle.
  • Valida sempre gli input: un evento non garantisce che il dato sia corretto.
  • Gestisci gli eventi con attenzione nei cicli di vita lunghi: in applicazioni grandi, rimuovere i listener quando non servono più aiuta a evitare memory leak.

Riepilogo

Il modello a eventi in JavaScript non riguarda solo il “rispondere a un click”, ma progettare il flusso dell’interazione in modo ordinato e scalabile. In questo tutorial abbiamo visto come:

  • intercettare eventi con addEventListener;
  • gestire il submit di un form in modo corretto;
  • usare la delegazione degli eventi per elementi dinamici;
  • mantenere separati stato, logica e rendering;
  • scrivere un codice più robusto e facile da estendere.

Se impari a usare bene il sistema degli eventi, costruire interfacce interattive diventa molto più naturale. È una competenza essenziale per applicazioni web moderne, dashboard, editor, form complessi e componenti riutilizzabili.

Approfondisci con risorse ufficiali

  • MDN Web Docs - Eventi in JavaScript: documentazione chiara e completa sul sistema degli eventi.
  • MDN Web Docs - addEventListener(): riferimento ufficiale per registrare listener e usare le opzioni disponibili.
  • MDN Web Docs - Event: utile per capire proprietà come target, currentTarget e metodi come preventDefault().
  • MDN Web Docs - Element.closest(): fondamentale per la delegazione degli eventi.
  • WHATWG DOM Standard: specifica tecnica del modello DOM ed eventi, per chi vuole andare più a fondo.

SHARE