JavaScript - Dispatching di eventi personalizzati
Crea e invia eventi personalizzati in JavaScript con CustomEvent e dispatchEvent, passa dati tramite detail e disaccoppia i componenti.
Dispatching di eventi personalizzati in JavaScript
Il browser genera automaticamente eventi nativi come click, submit e keydown. Ma JavaScript ti consente anche di creare e lanciare i tuoi eventi personalizzati su qualsiasi nodo del DOM, per poi ascoltarli con lo stesso addEventListener() che già utilizzi per gli eventi nativi.
Questa è la base del design applicativo a basso accoppiamento: una parte del codice annuncia che qualcosa è successo ("dati caricati", "carrello aggiornato") senza sapere né preoccuparsi di chi stia ascoltando. Questa pagina illustra come creare eventi personalizzati con il costruttore CustomEvent, allegare dati ad essi, inviarli con dispatchEvent() e gestire gli errori più comuni.
Creare un evento personalizzato
Utilizza il costruttore CustomEvent. Il primo argomento è il tipo di evento (il nome che utilizzerai successivamente per ascoltarlo); il secondo è un object di opzioni:
let event = new CustomEvent("myEvent", {
detail: { message: "This is a custom event!" },
bubbles: true,
cancelable: true
});Le tre opzioni più utilizzate sono:
detail— qualsiasi valore (object, string, numero) che vuoi allegare all'evento. Il listener lo legge comeevent.detail. Questo è il canale dedicato ai dati personalizzati; le proprietà degli eventi nativi sono di sola lettura.bubbles— quando ètrue, l'evento risale attraverso gli elementi antenati dopo l'attivazione, così un listener su un elemento padre (o sudocument) può intercettarlo. Il valore predefinito èfalse.cancelable— quando ètrue, un listener può chiamareevent.preventDefault()per segnalare che l'azione predefinita deve essere saltata. Il valore predefinito èfalse.
CustomEvent vs. Event
Esiste anche un costruttore Event semplice, ma non può trasportare dati — non ha detail. Usa sempre CustomEvent quando hai bisogno di passare informazioni insieme all'evento:
// No way to attach data here:
let bare = new Event("ping", { bubbles: true });
// Use CustomEvent to send a payload:
let withData = new CustomEvent("ping", {
bubbles: true,
detail: { at: Date.now() }
});Inviare l'evento
Un evento creato non fa nulla finché non lo lanci su un elemento con dispatchEvent():
let target = document.getElementById("box");
target.dispatchEvent(event);Ci sono due cose importanti da sapere su dispatchEvent():
- Viene eseguito in modo sincrono — a differenza di
setTimeout, i listener vengono eseguiti immediatamente, prima che la riga successiva adispatchEvent()venga eseguita. - Restituisce un boolean:
falsese l'evento eracancelablee un listener ha chiamatopreventDefault(), altrimentitrue. Questo consente al codice che ha inviato l'evento di reagire a un "veto":
let cancelled = !target.dispatchEvent(event);
if (cancelled) {
console.log("A listener prevented the default action.");
}Puoi inviare l'evento su qualsiasi elemento. Inviarlo su document funziona perché gli eventi con bubbling lo raggiungono, ma lanciarlo sull'elemento specifico che "possiede" l'evento (e lasciarlo risalire) mantiene i tuoi listener circoscritti ed evita handler globali accidentali.
Ascoltare un evento personalizzato
Non esiste un'API speciale dal lato dell'ascolto — addEventListener() funziona esattamente come per click. I dati arrivano in event.detail:
element.addEventListener("myEvent", function (event) {
console.log(event.detail.message); // "This is a custom event!"
});Esempi pratici
Esempio 1: Comunicazione tra componenti
Supponiamo che due parti indipendenti di una pagina debbano comunicare senza avere riferimenti diretti l'una all'altra. Una invia un evento personalizzato; l'altra lo ascolta. Poiché l'evento ha bubbles, il listener può essere posizionato su document:
<button id="sender">Send Message</button>// Listener in another component
document.addEventListener('componentMessage', function(event) {
alert('Received message: ' + event.detail.message);
});
document.getElementById('sender').addEventListener('click', function() {
// Create and dispatch the custom event
let customEvent = new CustomEvent('componentMessage', {
detail: { message: 'Hello from another component!' },
bubbles: true,
cancelable: true
});
document.dispatchEvent(customEvent);
});Come funziona:
- Facendo clic sul pulsante si crea e si invia un evento
componentMessageche trasporta un messaggio indetail. - Un listener altrove intercetta l'evento e vi reagisce. Nessuno dei due lati importa l'altro — il nome dell'evento è l'unico contratto.
Esempio 2: Aggiornare l'interfaccia utente dopo una modifica ai dati
Gli eventi personalizzati ti consentono di separare la logica dei dati dalla logica di rendering. Il livello dati annuncia dataUpdated; il livello UI ascolta e ri-renderizza. Qui l'aggiornamento viene attivato dopo un breve ritardo per simulare un fetch asincrono:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom Event UI Update Example</title>
</head>
<body>
<h1>User Status</h1>
<div id="userInfo">Loading user information...</div>
<script>
// Function to simulate a data update
function updateData() {
let dataUpdateEvent = new CustomEvent('dataUpdated', {
detail: { data: { username: 'user123', status: 'active' } }
});
document.dispatchEvent(dataUpdateEvent);
}
// UI component listening for data updates
document.addEventListener('dataUpdated', function(event) {
let userData = event.detail.data;
document.getElementById('userInfo').innerHTML =
`Username: <strong>${userData.username}</strong>, Status: <strong>${userData.status}</strong>`;
});
// Trigger the update after 2 seconds
setTimeout(updateData, 2000);
</script>
</body>
</html>Come funziona:
- Dopo due secondi,
updateData()invia un eventodataUpdatedcon i nuovi dati indetail. - Il listener dell'interfaccia utente legge
event.detail.datae aggiorna la pagina. La funzione che ha prodotto i dati non tocca mai direttamente il DOM.
Annullare un evento personalizzato
Quando un evento è cancelable, un listener può chiamare preventDefault(). Il dispatcher vede quindi un valore di ritorno false da dispatchEvent() e può ignorare il suo comportamento predefinito — lo stesso schema che il browser usa per l'invio di form o i clic sui link. Questo snippet viene eseguito in Node e dimostra il flusso sincrono:
let target = new EventTarget();
target.addEventListener("save", (event) => {
// Veto the save
event.preventDefault();
});
let event = new CustomEvent("save", { cancelable: true });
let notCancelled = target.dispatchEvent(event);
console.log(notCancelled); // false
console.log(event.defaultPrevented); // trueEventTarget è l'interfaccia base da cui ereditano i nodi del DOM, quindi la stessa logica si applica sia che tu esegua il dispatch su un elemento nel browser sia su un EventTarget standalone.
Conclusione
Gli eventi personalizzati ti offrono un modo pulito e senza framework per collegare le parti di un'applicazione. Creali con CustomEvent, allega un payload tramite detail, lancialo con dispatchEvent() e ascoltalo con il familiare addEventListener(). Imposta bubbles: true quando un elemento padre deve intercettare l'evento, e cancelable: true quando il dispatcher deve rispettare il veto di un listener. Il risultato è un codice in cui i moduli comunicano tramite eventi denominati anziché riferimenti diretti — più facile da testare, estendere e mantenere.
Per approfondire, scopri come gli eventi si propagano nel DOM in bubbling e capturing, come i listener vengono collegati nella gestione degli eventi nel DOM, e come preventDefault() influisce sulle azioni predefinite del browser.