Introduzione
Quando si lavora con il DOM, una delle situazioni più comuni è dover gestire i click su pulsanti o elementi interattivi dentro una finestra modale. Pensiamo a un modale con più azioni: chiudi, salva, annulla, elimina. Se ogni volta aggiungiamo un listener a ogni singolo bottone, il codice può diventare dispersivo e difficile da mantenere, soprattutto se il contenuto del modale cambia dinamicamente.
In questo tutorial vedremo un sotto-argomento molto pratico: come usare addEventListener e la delega degli eventi per gestire i click all’interno di un modale. Questo approccio è utile perché permette di centralizzare la logica, ridurre il numero di listener e semplificare la manutenzione del codice.
L’idea di base è semplice: invece di ascoltare il click su ogni singolo bottone, ascoltiamo il click su un contenitore comune, come il modale stesso, e poi verifichiamo quale elemento è stato realmente cliccato. È un pattern molto usato nelle applicazioni reali, soprattutto quando il contenuto viene creato o aggiornato dinamicamente.
Codice completo
<div id="modal" class="modal" hidden>
<div class="modal__content">
<h2>Gestione utente</h2>
<p>Scegli un´azione da eseguire.</p>
<div class="modal__actions">
<button data-action="save">Salva</button>
<button data-action="cancel">Annulla</button>
<button data-action="delete">Elimina</button>
</div>
</div>
</div>
<button id="openModal">Apri modale</button>
<script>
const modal = document.getElementById(´modal´);
const openModalButton = document.getElementById(´openModal´);
// Apre il modale
openModalButton.addEventListener(´click´, () => {
modal.hidden = false;
});
// Delega degli eventi sul contenitore del modale
modal.addEventListener(´click´, (event) => {
const actionButton = event.target.closest(´[data-action]´);
// Se il click non arriva da un elemento con data-action, non fare nulla
if (!actionButton || !modal.contains(actionButton)) {
return;
}
const action = actionButton.dataset.action;
switch (action) {
case ´save´:
console.log(´Salvataggio dati...´);
// qui potresti inviare un form o salvare tramite fetch
modal.hidden = true;
break;
case ´cancel´:
console.log(´Operazione annullata´);
modal.hidden = true;
break;
case ´delete´:
const confirmed = confirm(´Vuoi davvero eliminare questo elemento?´);
if (confirmed) {
console.log(´Elemento eliminato´);
modal.hidden = true;
}
break;
default:
console.warn(´Azione non riconosciuta:´, action);
}
});
// Chiude il modale cliccando fuori dal contenuto
modal.addEventListener(´click´, (event) => {
const content = modal.querySelector(´.modal__content´);
if (event.target === modal) {
modal.hidden = true;
}
// Se vuoi chiudere solo cliccando sull´overlay, questo controllo è sufficiente
// perché il contenuto interno non corrisponde al contenitore esterno.
});
</script> Spiegazione
Vediamo il codice passo per passo. Il modale contiene tre pulsanti con un attributo personalizzato data-action. Questo attributo è molto comodo perché ci permette di identificare in modo chiaro il tipo di operazione associata a ciascun pulsante, senza dipendere da classi CSS o da testi visibili che potrebbero cambiare.
1. Apertura del modale
Il pulsante #openModal usa un listener classico con addEventListener per rendere visibile il modale. Qui non serve delega, perché il bottone è uno solo e non cambia nel tempo.
2. Delegazione degli eventi sul modale
Il cuore del tutorial è questo passaggio:
- aggiungiamo un solo listener al contenitore
#modal; - quando avviene un click, analizziamo
event.target; - usiamo
closest(´[data-action]´)per risalire al bottone corretto, anche se il click avviene su un elemento interno; - leggiamo il valore di
data-actione decidiamo cosa fare.
Questo è il vantaggio principale della delega: un solo listener gestisce più elementi. Se in futuro aggiungiamo un quarto pulsante, non dobbiamo registrare un nuovo evento: basta inserire il markup corretto.
3. Perché usare closest
In un bottone reale potresti avere un’icona, uno span o altri elementi interni. Se l’utente clicca sull’icona, event.target non sarà il bottone, ma l’elemento interno. Con closest(´[data-action]´) risaliamo al primo antenato che possiede l’attributo richiesto. Questo rende il codice più robusto e meno fragile.
4. Chiusura cliccando sull’overlay
Un comportamento tipico dei modali è la chiusura cliccando sullo sfondo esterno. Nel nostro esempio controlliamo se event.target === modal. In questo modo il modale si chiude solo quando il click avviene sull’overlay, non sul contenuto interno.
Best practice
- Usa la delega quando gli elementi sono numerosi o dinamici: è più efficiente e più facile da mantenere.
- Preferisci attributi semantici come
data-action: rendono il codice più leggibile rispetto a controlli basati su testo o classi generiche. - Controlla sempre il target dell’evento: non assumere che il click arrivi direttamente sull’elemento atteso.
- Evita listener duplicati: se apri e chiudi il modale più volte, non registrare più volte gli stessi eventi.
- Separa logica e presentazione: il CSS deve gestire l’aspetto, JavaScript il comportamento.
- Usa
hiddeno classi dedicate per mostrare/nascondere il modale: è una soluzione semplice e chiara. - Gestisci i casi non previsti: uno
switchcondefaultaiuta a intercettare azioni sconosciute durante il debug.
Riepilogo
In questo tutorial abbiamo visto come combinare addEventListener e delega degli eventi per gestire in modo elegante i click dentro un modale. La strategia è particolarmente utile quando:
- ci sono più pulsanti con comportamenti diversi;
- il contenuto del modale può cambiare nel tempo;
- vuoi evitare di aggiungere un listener per ogni singolo elemento;
- ti serve un codice più compatto, scalabile e facile da leggere.
Il pattern fondamentale è questo: ascolta l’evento sul contenitore, individua l’elemento cliccato, esegui l’azione corretta. È una tecnica semplice, ma estremamente potente nello sviluppo frontend moderno.
Approfondisci con risorse ufficiali
- MDN - EventTarget.addEventListener(): documentazione ufficiale sul metodo per registrare eventi.
- MDN - Event: per capire meglio proprietà come
target,currentTargete il comportamento degli eventi. - MDN - Element.closest(): utile per risalire all’antenato corretto durante la delega.
- MDN - data-*: guida sugli attributi personalizzati
data-nel HTML. - JavaScript.info - Event delegation: ottima risorsa per approfondire il pattern della delega con esempi pratici.
