This article is available in English too.
Il sistema di eventi JavaScript ha tre modelli di implementazione, purtroppo tutti incompatibili tra
loro.
Vediamo di fare un po' di ordine e di capire quale usare a seconda della
situazione.
Il modello originale DOM 0
E' il modello base, il primo implementato, supportato da tutti i browser, ma
con grosse limitazioni.
Praticamente l'event handler è trattato come una proprietà dell'oggetto che
lancia l'evento.
myButton.onsubmit = myObject.onsubmiteventhandler;
Questo significa due cose importanti:
- Non ci può essere più di un event handler per ogni evento, vale cioè
sempre l'ultimo impostato che va a sostituire il precedente.
E' possibile impostare però lo stesso event handler per eventi differenti.
- Se viene usato l'oggetto
this
all'interno dell'event
handler, questo farà riferimento all'oggetto scatenante (myButton
nell'esempio) e non a quello che gestisce l'evento (myObject
)
come avviene invece nella classica programmazione Windows.
Se le limitazioni precedenti non sono un problema, per ragioni di
compatibilità questo è certamente il modello da utilizzare.
Modello avanzato DOM 2
E' l'evoluzione del precedente, ma attualmente non è supportato da Internet
Explorer di versione inferiore alla 9.
document.myForm.myButton.addEventListener("submit", function(e) { ...
}, false);
Oppure se si vuole registrare il metodo di un oggetto (si noti che Firefox
permette di usare il primo sistema per passare direttamente i metodi di un
oggetto):
document.myForm.myButton.addEventListener("submit", function(e) {
myObject.onsubmiteventhandler(e); }, false);
Offre le seguenti funzionalità aggiuntive:
- Si possono registrare più event handler per lo stesso evento.
- Agli event handler viene passato come parametro un oggetto
Event
con i dati significativi dell'evento stesso.
- Se si registra più volte lo stesso event handler, tutte le
registrazioni successive alla prima verranno ignorate.
- Gli eventi vengono propagati in tre fasi consecutive:
- Capturing Pahase: l'evento viene propagato
dall'oggetto
Document
seguendo la catena della discendenza
fino all'oggetto finale che si era sottoscritto, a verificare se qualche
antenato aveva impostato la cattura dell'evento (il terzo parametro
booleano del metodo addEventListener
serve appunto a
specificare se la gestione deve avvenire durante questa fase).
- Target Node: l'evento viene gestito dall'oggetto
che si è sottoscritto.
- Bubbling Phase: l'evento torna verso l'alto nella
gerarchia, dal nodo che si è sottoscritto fino alla radice che è
l'oggetto
Document
.
In qualsiasi momento la propagazione può essere interrotta dall'event handler
in esecuzione con stopPropagation()
.
Per quanto riguarda invece l'oggetto this
, purtroppo manca una
standardizzazione, l'interpretazione più diffusa è quella identica al livello
DOM 0.
Modello Internet Explorer
Supportato solo dai browser di casa Microsoft, è simile al DOM 2, ma con
alcune significative differenze.
document.myForm.myButton.attachEvent("onsubmit", function(e) { ...
});
- Non implementa la Capturing Phase nella propagazione
dell'evento (manca infatti il terzo parametro).
- Se si registra più volte lo stesso event handler, questo verrà
invocato più volte.
- L'oggetto
Event
non viene passato come parametro all'event
handler, ma è una variabile globale (window.event
): questo
non è un problema poiché la programmazione JavaScript è single-threaded.
- L'oggetto
this
all'interno degli event handler fa
riferimento all'oggetto globale window
.
Vediamo ora un esempio pratico di come districarsi in queste diverse
implementazioni.
Capita ad esempio spesso di dover registrare un event handler in
risposta al completamento del caricamento della pagina, ma se si usasse il
livello DOM 0 ci sarebbe il rischio di sovrascrivere un altro event handler
precedentemente impostato. Per ovviare al problema usare la seguente funzione.
function subscribeOnLoad(eventHandler)
{
if (typeof window.addEventListener != "undefined")
window.addEventListener("load", eventHandler, false);
else if (typeof window.attachEvent != "undefined")
window.attachEvent("onload", eventHandler);
else
{
if (window.onload != null)
{
var exsistingOnLoad = window.onload;
window.onload = function (e)
{ exsistingOnLoad(e); window[eventHandler](); };
}
else
window.onload = eventHandler;
}
};