Eventi: l’interazione con le pagine web attraverso Javascript

L’utente clicca su di un link, tocca il touch screen, digita un tasto, il browser carica un contenuto di una pagina, l’utente ridimensiona la finestra, compila un form… Ciascuno di questi eventi può essere usato per richiamare una funzione in Javascript, consentendo alla pagina web di “rispondere” ad un determinato evento.

Quando l’utente clicca su di un link Html, il browser risponde caricando l’url corrispondente. Qualcuno, tanto tempo fa, ha inventato ed implementato il collegamento ipertestuale, che risponde al click dell’utente. Allo stesso modo in cui, in Html, ad un testo viene associata la funzionalità di collegamento ad un determinato link, così, in Javascript, è possibile selezionare un determinato elemento e “sintonizzarlo” su di un dato evento, affinché ne registri l’avvenimento e risponda, processando il codice predisposto in una determinata funzione.

women telephone operators at work

Teoricamente esistono tre modi per “sintonizzare” un elemento su di un evento; in pratica solo uno dei tre ha un futuro nel Web. Saltiamo quindi a piè pari i primi due, dimentichiamoci della guerra dei browser del tempo che fu, e concentriamoci sui così detti “Event Listeners” (ascoltatori di eventi), l’approccio più moderno ed efficiente per gestire gli eventi. Morte ai vecchi browser! Morte a Internet Explorer 8 e compagni!

Da dove cominciamo? Dunque, c’è questa cosa, chiamata DOM (Document Object Model), che fornisce una struttura del documento Html, organizzata in “nodi”, rendendo così possibile agli sviluppatori, far riferimento a ciascun elemento della pagina Web, attraverso Javascript, sfruttando i marcatori e gli attributi Html, come ad esempio l’attributo “id”. Poniamo il caso di voler far riferimento ad un paragrafo, nel nostro codice html, a cui abbiamo dato un attributo “id” uguale a “pippo”.

<p id="pippo">sono un paragrafo di testo!</p>

Grazie all’id specifico con il quale abbiamo battezzato il nostro paragrafo, possiamo farvi riferimento, in Javascript, utilizzando la stringa di codice: document.getElementById(“pippo”) e cambiare ad esempio il testo al suo interno.

document.getElementById("pippo").innerHTML = "Ciao Mondo!";

Ma possiamo anche abbinare al nostro paragrafo un Event Listener, in grado di registrare l’avvenimento di un determinato evento e rispondere eseguendo il codice contenuto in una determinata funzione, semplicemente utilizzando la funzione addEventListener, specificando come argomenti: il tipo di evento su cui mettersi in ascolto, la funzione che vogliamo venga eseguita (senza parentesi però, giacché l’esecuzione non è immediata ma posticipata nel tempo!), l’ordine di propagazione dell’evento ai vari elementi html in cui il nostro paragrafo è nidificato (false: dall’interno verso l’esterno, oppure true: dall’esterno verso l’interno. Vedremo meglio questo ultimo aspetto in seguito*).

var pippoParagraph = document.getElementById("pippo");

pippoParagraph.addEventListener("click", changeContent, false);

Chiaramente dobbiamo predisporre la funzione che intendiamo eseguire al verificarsi dell’evento sul nostro elemento.

function changeContent() {
 document.getElementById("pippo").innerHTML = "Ciao Mondo!";
}

//o, più semplicemente, facendo riferimento alla parola chiave "this"

function changeContent() { 
 this.innerHTML = "Ciao Mondo!";
 }

Cliccando sul paragrafo il contenuto testuale cambierà secondo quando specificato nella funzione di esempio changeContent. È anche possibile dichiarare la nostra funzione in forma anonima, direttamente all’interno del codice in addEventListener, in questo modo:

var pippoParagraph = document.getElementById("pippo");

pippoParagraph.addEventListener("click", function(){this.innerHTML = "Ciao Mondo!"}, false);

Risparmiate qualche linea di codice ma la leggibilità è peggiore e tale funzione non è riutilizzabile. Ciò non di meno, scrivere una funzione anonima può risultare una soluzione preziosa, nel caso particolare, in cui abbiate l’esigenza di passare degli argomenti; in tal caso, infatti, non essendo previste parentesi dopo il nome di una funzione, l’unico modo per aggirare il problema è quello di racchiuderla al suo interno!

var pippoParagraph = document.getElementById("pippo");

pippoParagraph.addEventListener("click", function(){changeContent("Ciao Mondo!")}, false); 

function changeContent(arg) { 
  this.innerHTML = arg; // ERRORE! "this" non è sufficiente!
 };

Ops! In questo caso la funzione richiamata dall’interno, non può fare riferimento all’elemento Html a cui fa capo semplicemente con “this” (come abbiamo invece visto in precedenza); dovrà invece essere più specifica e riferirsi al paragrafo utilizzando l’aiuto di due altre parole chiave: “event” e “target”:  this.event.target.innerHTML, riscriviamo il codice in maniera corretta, di seguito.

var pippoParagraph = document.getElementById("pippo");

pippoParagraph.addEventListener("click", function(){changeContent("Ciao Mondo!"}, false); 

function changeContent(arg) { 
 this.event.target.innerHTML = arg; // FUNZIONA!
 };

Ogni volta che avviene un evento, Javascript crea per noi un “oggetto evento“, contenente tutta una serie di dati preziosi, e, dietro le quinte, lo passa alla funzione che ha atteso il suo verificarsi, come un parametro invisibile a cui è possibile far riferimento attraverso la parola chiave event. Tra le proprietà più utili dell’oggetto event abbiamo: target, che ci restituisce l’elemento sui cui l’evento si è verificato, e type, che ci restituisce informazioni sul tipo di evento. A dirla tutta in questo caso possiamo omettere this che risulta pleonastico, e riferirci direttamente all’evento.

var pippoParagraph = document.getElementById("pippo");

pippoParagraph.addEventListener("click", function(){changeContent("Ciao Mondo!"}, false); 

function changeContent(arg) { 
 event.target.innerHTML = arg; // CORRETTO!
 console.log(event.target + " " + event.type);
 };

Abbiamo appena creato una funzione, in grado di essere riutilizzata più e più volte, per mandare messaggi diversi a seconda di interazioni diverse con elementi diversi della nostra pagina web. Urrà!

Senza dover toccare nuovamente la funzione changeContent, posso ad esempio aggiungere altri EventListener ad altri elementi, e creare per ciascuno un messaggio diverso:

var pippoParagraph = document.getElementById("pippo");
var plutoParagraph = document.getElementById("pluto");
var paperinoParagraph = document.getElementById("paperino");

pippoParagraph.addEventListener("click", function(){changeContent("Sono Pippo!")}, false); 

plutoParagraph.addEventListener("click", function(){changeContent("Sono Pluto!")}, false);

paperinoParagraph.addEventListener("click", function(){changeContent("Sono Paperino!")}, false);

paperinoParagraph.addEventListener("mouseout", function(){changeContent("Ciao, ciao!")}, false);

function changeContent(arg) { 
 event.target.innerHTML = arg; // CORRETTO!
 console.log(event.target + " " + event.type);
 };

telegraph

*La struttura degli elementi della nostra pagina Html è nidificata, vale a dire che gli elementi sono contenuti in altri elementi, che a loro volta sono contenuti in altri elementi ancora. Quando l’utente clicca su di un pulsante, clicca anche sull’elemento che contiene il pulsante (ad esempio una lista), e su quello che contiene quest’ultimo (ad esempio un div), e così via, risalendo fino all’oggetto documento. Dato che gli Event Listeners possono essere abbinati a ciascuno di questi elementi, anche contemporaneamente, è importante poter determinare l’ordine con cui, le relative funzioni vengono processate: il cosiddetto flusso degli eventi, “Event Flow“. Di default l’evento fluisce dall’elemento più interno, verso quello più esterno, ma è anche possibile invertire tale flusso, specificando “true” come ultimo argomento della funzione addEventListener.

myElement.addEventListener('theEventToListenTo', theFunctionToCall, false);

Sfortunatamente, più event listener aggiungete ai vari elementi della vostra pagina Html, maggior memoria usate, fino a rischiare di pregiudicare le performance del vostro codice. Fortunatamente, grazie all’Event Flow, potete aggiungere un solo listener ad un oggetto contenitore, e delegare ad esso, utilizzando la proprietà Target dell’oggetto Event, il compito di determinare su quale elemento, nidificato al suo interno, si sia verificato. Questa tecnica, chiamata “Event Delegation” ci consente anche aggiungere nuovi elementi “figli” all’interno di quello “genitore”, in corso d’opera, senza dover modificare il codice Javascript.

Immaginiamo, ad esempio, di avere una lista di elementi in un ipotetico menu, possiamo aggiungere un unico Event Listener all’elemento Html “ul“, e risalire a quale elemento “li” della lista l’utente abbia fatto click. Provate per credere!

<!DOCTYPE html>
<html>

<head>
 <title>JavaScript &amp; Event Delegation</title>
</head>

<body>
 <h2>Lista della spesa</h2>
 <ul id="shoppingList">
 <li>Coffee</li>
 <li>Tea</li>
 <li>Milk</li>
 </ul>
 <script>
  var el = document.getElementById('shoppingList');
  el.addEventListener("click", changeContent, false);

  function changeContent() {
    event.target.style.textDecoration = "line-through";
    console.log(event.target + " "  + event.type); 
  };
 </script>
</body>

</html>

Facendo click su qualsiasi elemento della lista, la scritta corrispondente apparirà barrata. Niente male, vero? 😉

Infine, è bene sapere che, oltre agli eventi appartenenti al Document Object Model (DOM), ce ne sono altri, specifici per il Browser Object Model (BOM), che fanno riferimento alla finestra (window), anziché al documento (document); eventi concernenti il caricamento, il ridimensionamento, le modalità di interazione touch con la finestra ed altro ancora, che non è però argomento per questo post.

Leave a Reply

Your email address will not be published. Required fields are marked *