XHTML, CSS, Javascript, PHP...

... e chissà cos'altro!

Questo è il 1° post di una serie di articoli che avranno lo scopo di approfondire i metodi standard e non per la gestione degli eventi in Javascript, fornendo poi una soluzione per estendere al massimo la compatibilità dei primi.

Il principale problema che si deve affrontare nel realizzare applicazioni per il web è molto semplice: i browser interpretano a modo loro e, a volte, con non poca fantasia quelli che invece sono (o dovrebbero essere) degli standard.
La conseguenza di queste originali interpretazioni è la difficoltà di creare siti web che possano essere visualizzati correttamente e che permettano l’interazione con l’utente nello stesso modo ed in qualsiasi browser.

Lo scopo di questo primo articolo è quello di vedere cosa il “Document Object Model (DOM) Level 2 Events Specification” del W3C dice riguardo alla gestione degli eventi in Javascript; sarà compito dei prossimi post analizzare invece i reali comportamenti dei vari browser cercando, infine, di trovare una soluzione il più possibile cross-browser per la gestione degli eventi con il noto linguaggio di scripting client side.

Iniziamo ;)

Il flusso degli eventi

La Gestione degli eventi consiste nell’associare ad un elemento della pagina una una o più azioni (definite dal programmatore) da eseguire, appunto, al verificarsi di un determinato evento.

Per offrire la massima flessibilità ai programmatori, gli eventi si propagano anche attraverso gli elementi “ancestor” del nodo in cui l’evento è stato generato eseguendo, ad ogni passaggio, le azioni registrate per quel tipo di evento sull’elemento corrente.

Questa propagazione avviene in 2 differenti fasi: parte dall’elemento radice dell’intero DOM (document) fino a raggiungere il nodo che ha scatenato l’evento (Capturing Phase), successivamente esegue all’inverso lo stesso cammino raggiungendo nuovamente il document (Bubbling Phase).

Cerchiamo di comprendere meglio la situazione con un esempio tratto proprio dal sito del W3C:

Flusso degli eventi

Se l’utente clicca sull’elemento <td> della tabella, l’evento che si genererà percorrerà il DOM dall’elemento document fino a raggiungere la cella cliccata (Fase di Capture), inizierà poi a propagarsi anche in ordine inverso partendo dalla cella e tornando al document (Fase di Bubble).

Nell’immagine è presente anche la fase di Target che si presenta quando l’evento raggiunge la cella che l’ha scatenato.

Registrazione degli Handler

Ora che abbiamo visto come un evento scatenato si propaga nel DOM vediamo come invece è possibile gestire e associare delle azioni a questi eventi.

Metodo Tradizionale

Il metodo più semplice è il Metodo Tradizionale che consiste nell’assegnare una funzione all’elemento su cui vogliamo attendere l’arrivo degli eventi.
Essendo molti i tipi di eventi che possono essere scatenati, l’attributo dell’elemento a cui la funzione dovrà essere associata varierà appunto in base al tipo di evento che vorremmo gestire.

Facciamo subito un esempio: Vogliamo mostrare un messaggio al click su un elemento <button>:

&lt;button id="pulsante"&gt;Cliccami&lt;/button&gt;
var pulsante = document.getElementById("pulsante");
var handler = function(){
alert("Sono stato cliccato!");
}
pulsante.onclick=handler;

Bene, abbiamo realizzato il nostro primo esempio di gestione degli eventi in Javascript.

Con il Traditional Method è possibile registrare i nostri handler solo per la fase di Bubbling del flusso degli eventi.
Verifichiamolo: proviamo a inserire <button> all’interno di un altro tag, per esempio dentro ad un paragrafo, e andiamo a specificare degli handler anche per <p>.

&lt;p id="paragrafo"&gt;
&lt;button id="pulsante"&gt;Cliccami&lt;/button&gt;
&lt;/p&gt;
var paragrafo = document.getElementById("paragrafo");
var pulsante = document.getElementById("pulsante");
pulsante.onclick = function(){
alert("Il pulsante è stato raggiunto dall'evento");
}
paragrafo.onclick = function(){
alert("Il paragrafo è stato raggiunto dall'evento");
}

Vediamo cosa dovrebbe succedere subito dopo il click sul pulsante:

  • Fase di Capture: L’evento percorre il DOM da document fino a button
    Come abbiamo già detto gli eventi assegnati con il metodo tradizionale non vengono registrati per la fase di Capture e quindi non dovrebbe accade nulla
  • Fase At Target: Viene raggiunto l’elemento che ha generato l’evento
    Abbiamo specificato una funzione per l’evento onclick di button e quindi questa viene eseguita mostrando all’utente il messaggio “Il pulsante è stato raggiunto dall’evento
  • Fase di Bubble: L’evento percorre il DOM all’indietro tornando a document
    Il primo elemento che viene raggiunto da questa fase è il paragrafo a cui abbiamo registrato una funzione per l’evento onclick che verrà quindi eseguita facendo apparire il messaggio: “Il paragrafo è stato raggiunto dall’evento

Tutto sembra funzionare a meraviglia. ;)

Metodo Avanzato

Il metodo appena visto è molto semplice ed immediato, ma ha delle limitazioni: con il traditional method non è possibile registrare gli eventi per la fase di Capture e non si può associare più di un handler per lo stesso evento sullo stesso oggetto.

E’ per questo motivo che il W3C ha scelto di implementare in tutti i nodi una interfaccia che permette la gestione avanzata degli eventi e il superamento dei limiti imposti dal metodo tradizionale:

addEventListener e removeEventListener
Con questi due metodi si possono registrare o rimuovere le funzioni per la gestione di uno specifico evento per qualsiasi nodo della pagina.
Entrambe accettano 3 parametri.
Il primo, di tipo Stringa, indica quale tipologia di evento deve essere gestita: sono valori validi “click“,”focus“,”load“,”dblclick“,”mousemove“,ecc…
Come secondo parametro viene invece passata la funzione di gestione (handler) che dovrà essere aggiunta (addEventListener) o rimossa (removeEventListener)
Il terzo ed ultimo parametro è un valore booleano che indica se la funzione specificata per gestire l’evento deve essere registrata per essere invocata durante la fase di Capture (passando un valore true) o di Bubble/Target (passando un valore false).

Vediamo di applicare subito questi metodi:

&lt;p id="paragrafo"&gt;
&lt;button id="pulsante"&gt;Cliccami&lt;/button&gt;
&lt;/p&gt;
var paragrafo = document.getElementById("paragrafo");
var pulsante= document.getElementById("pulsante");

// Impostiamo gli handler
paragrafo.addEventListener("click",function(){
alert("Handler sul paragrafo, Capturing");
},true);
pulsante.addEventListener("click",function(){
alert("Handler sul pulsante, Bubbling");
},false);
paragrafo.addEventListener("click",function(){
alert("Handler sul paragrafo, Bubbling");
},false);
paragrafo.addEventListener("click",function(){
alert("Secondo Handler sul paragrafo, Bubbling");
},false);
Guarda l’esempio
(supportato da tutti i browser tranne che da Internet Explorer)

Vediamo anche questa volta cosa succede subito dopo il click sul pulsante:

  • Fase di Capture: L’evento percorre il DOM da document fino a button.
    Quando raggiunge l’elemento <p> esegue la funzione che è stata registrata per la fase di capture mostrando a video: “Handler sul paragrafo, Capturing
  • Fase At Target: Viene raggiunto l’elemento che ha generato l’evento
    button ha un evento registrato per la fase NON di Capture e viene eseguito mostrando all’utente: “Handler sul pulsante, Bubbling
  • Fase di Bubble: L’evento percorre il DOM all’indietro tornando a document
    Il primo elemento che viene raggiunto da questa fase è il paragrafo a cui abbiamo registrato 2 funzioni per la fase NON di Capture e vengono quindi eseguite facendo apparire sullo schermo: “Handler sul paragrafo, Bubbling” e “Secondo Handler sul paragrafo, Bubbling

Handler ed Event Object

In ogni funzione associata ad uno specifico evento, registrata attraverso uno qualsiasi dei metodi appena visti, è possibile ottenere un riferimento all’oggetto su cui l’handler è in esecuzione mediante la parola chiave this.

Inoltre ad ogni handler viene passato come primo parametro un oggetto che fornisce, tramite le sue proprietà, delle informazioni sull’evento in corso, fra cui:

type
Indica il tipo di evento (es: “click“,”mousemove“,”load“,”focus“,ecc…)
target
Contiene un riferimento al nodo che ha generato l’evento
currentTarget
Contiene un riferimento al nodo per cui si stanno eseguendo gli handler
eventPhase
Un valore intero che indica se la fase corrente è di Capture (1), At Target (2) o Bubble (3)
bubbles
Un valore booleano che indica se l’evento corrente supporta il bubbling.
(infatti per tutti gli eventi vengono eseguite le fasi At Target e di Capture, ma solo per alcuni anche quella di Bubble.)
cancelable
Un altra proprietà booleana che indica se è possibile annullare l’azione di default associata all’evento

Lo stesso oggetto che fornisce l’accesso alle proprietà appena indicate, offre anche 2 utilissimi metodi:

stopPropagation()
Invocando questo metodo viene terminata la propagazione dell’evento ad altri nodi. Ferma quindi la fase di Bubble o di Capture, ma non annulla l’effetto dell’evento.
preventDefault()
Chiamando questo metodo è, invece, possibile annullare l’eventuale azione di default associata all’evento (es: l’invio di un form, la navigazione dopo il click su un link, ecc…)

Vediamo di fare quindi un esempio finale che possa mostrare all’opera alcune di queste funzionalità:

&lt;p id="paragrafo"&gt;
&lt;a href="http://www.google.it"&gt;Google.it&lt;/a&gt;
&lt;/p&gt;

&lt;form id="form" action="." method="get"&gt;
&lt;label&gt;
Input Box:
&lt;input id="text" type="text" /&gt;
&lt;/label&gt;
&lt;input type="submit" value="Invia" /&gt;
&lt;/form&gt;
var p = document.getElementById("paragrafo");
var link = document.links[0];
var form = document.getElementById("form");
var txt = document.getElementById("text");

var generalHandler = function(e){
var fase="";
switch(e.eventPhase){
case 1:
fase="di Capturing";
break;
case 2:
fase="At Target";
break;
case 3:
fase="di Bubbling";
}

var msg = "Sto gestendo l'evento " + e.type;
msg+=" sul tag " + this.tagName;
msg+=" per la fase " + fase;
msg+="\nTutto è stato generato dal tag " + e.target.tagName;

if(confirm(msg + "\n\nFermo il propagarsi degli eventi?"))
e.stopPropagation();
if(confirm(msg + "\n\nCancello l'azione di default?"))
e.preventDefault();
}

p.onclick = link.onclick = form.onsubmit = txt.onfocus = generalHandler;
p.addEventListener("click",generalHandler,true);
Guarda l’esempio
(supportato da tutti i browser tranne che da Internet Explorer)

Grazie alle informazioni contenute nell’oggetto passato agli handler, abbiamo creato una funzione generica che mostra le informazioni relative agli eventi per cui è registrata.
Lascio ai lettori più interessati provare “giocare” con l’esempio qui sopra ;)

Questo lungo e forse anche un po’ noioso post è finito, ma è solo il 1° :D

Nel prossimo articolo vedremo, molto più velocemente, come Internet Explorer gestisce gli eventi e successivamente, nel 3° post, cercheremo una soluzione per riuscire a creare script compatibili che funzionino sia con i browser che rispettano gli standard sia con IL browser che purtroppo segue sue personali fonti di ispirazione…

Se ritenete che questo articolo sia stato troppo superficiale o poco tecnico potete sempre approfondire l’argomento con una ancor più lunga e noiosa, ma ottima e decisamente più dettagliata, lettura direttamente sul sito del W3C:
DOM Level 2 Events Specification
;)

Tags: ,

Scrivi un commento

Se questo articolo ti è piaciuto puoi scrivere un tuo commento, fare un trackback dal tuo sito, iscriverti al feed rss o offrirmi una coca-cola. ;)

8 Commenti a “Eventi in Javascript: Il W3C”

  1. interessantissimo…

    è bello che tu Epper dia una mano a noi poveri informatici (se così posso classificarmi…) :D
    ottimo thread.

  2. Ebbbbbbravo Epper!!
    Bell’articolo..
    Il sito è sempre lento, ma bell’articolo!!
    Compliments..
    A presto!

  3. Che FIGO (se si può scrivere) il plugin per l’inserimento realtime in AJAX dei commenti!! Dimmi cos’è che lo metto pur io!! :D

  4. Grazie ad entrambi…
    La 2° parte è in lavorazione: è il tempo quello che manca!

    @Alberto:
    Non è un plugin questo dei commenti Ajax, l’ho fatto io usando la libreria JQuery. Metterò online un how-to ;)

  5. ciao Epper, grazie per la spiegazione, ti giuro mi ci stavo arrovellando il cervello prima di capitare qui! thanks a lot :)

  6. Davvero molto chiaro…spero che tutto ciò mi sia d’aiuto per il mio esame di tecnologie web.

    Grazie.
    PS: gli appunti della prof.ssa fanno c… :-)

  7. Ciao solo una cosa: l’oggetto this NON è accessibile col metodo tradizionale! leggi qui: http://www.quirksmode.org/js/this.html

  8. Ciao thestoniano.

    Nel metodo tradizionale che ho descritto ti confermo che è disponibile, nell’handler, l’oggetto this.

    Nota che io con metodo tradizionale intendo:

    elemento.onclick = handler;

    E non

    <button onclick=”handler()”>…</a>

    Questo ultimo metodo non l’ho volutamente descritto e effettivamente in questo caso l’oggetto this non è disponibile.

Scrivi un commento

Allowed: <a> <abbr> <acronym> <b> <blockquote> <code> <em> <i> <strong>