In questo articolo ci occupiamo dei comportamenti e delle interazioni cui le RIA ci hanno abituato. Le nuove specifiche HTML5 tendono a ‘portare’ all’interno tutta una serie di funzionalità che già esistevano ed erano realizzabili grazie ad API JavaScript o librerie Flash e per la cui programmazione era spesso quasi necessario utilizzare dei framework. In tal senso, non c’è nulla di nuovo ma siamo in presenza di un notevole lavoro di normalizzazione e semplificazione.
Comportamenti complessi in maniera “semplice”
Fino ad oggi ci siamo sempre persi nel mare delle librerie JavaScript alla ricerca della migliore libreria per aggiungere alla nostra pagina il drag & drop oppure un semplice file uploader: tutto questo spesso andava a discapito delle prestazioni perche’ eravamo costretti ad aggiungere molti kilobyte di codice JavaScript. L’obiettivo che HTML5 si è prefisso in questo campo è di rendere nativamente presenti all’interno del nostro browser molte funzioni necessarie a comportamenti e interazioni anche complessi. In pratica, vengono in parte semplificate numerose operazioni di programmazione: senza dover ricorrere ad API esterne, o alla definizione delle funzioni, si tratta “solo” di richiamarle e di configurarle correttamente.
Va detto che queste funzionalità sono, al momento della scrittura dell’articolo, un po’ un mondo in divenire: come ben sappiamo, HTML5 è ben lontano dalla sua definitiva specifica, ma il suo supporto da parte di browser e dispositivi è oramai maturo. Inoltre, la presenza dentro i dispositivi mobili e anche in alcuni portatili di nuova generazione, di accelerometri e di vari sensori per la geolocalizzazione, consentono agli sviluppatori un’ampia libertà creativa.
Vedremo nel corso dell’articolo alcune di queste funzioni, probabilmente quelle più comuni, ma anche più interessanti: molti avranno già sperimentato certi comportamenti grazie ad API JavaScript esterne o alle potenzialità di Flash. Qui li presentiamo implementati grazie alle API interne fornite da HTML5. Visto il carattere “animato” di molti di questi comportamenti, nell’articolo non riportiamo immagini statiche relative ai comportamenti, ma invitiamo i lettori a sperimentare tali interazioni visualizzando gli esempi allegati (contenuti nel file “esempi-7.zip” scaricabile dal menu a destra) con un browser di ultima generazione che supporti tutte queste funzionalità (al momento attuale è Google Chrome a presentare il massimo grado di compatibilità, ma gran parte degli esempi funzionerà bene anche su altri browser, come Mozilla Firefox, Safari o Opera, anche se gli ultimi due non supportano ancora pienamente il drag & drop).
Drag & Drop
Ormai questo termine è familiare a tutti gli utenti che normalmente utilizzano un computer: drag & drop è quel comportamento usuale con cui trasciniamo le icone sul desktop, spostiamo i file etc. HTML5 ha introdotto in maniera nativa [1] questa funzionalità che fino ad ora è stata possibile solo con l’inclusione di librerie JavaScript esterne.
Vediamo in breve il codice che serve per attuare questo meccanismo. Per prima cosa dobbiamo definire quali sono gli elementi trascinabili e i target sui quali poterli rilasciare. La prima operazione è semplicissima: dobbiamo solo impostare l’attributo draggable del nostro elemento a true e aggiungere un eventListener sull’evento startDrag in modo da poter assegnare all’oggetto DataTransfer le informazioni necessarie per eseguire il drag&drop.
…
... function handleDragStart(e) { e.dataTransfer.effectAllowed='move'; e.dataTransfer.setData("Text", .target.getAttribute('id')); } quad.addEventListener('dragstart', handleDragStart, false); ...
Per il momento per rendere quanto più compatibile con tutti i browser il nostro codice, setData accetta come dataType solamente “Text” o “Url”. Così facendo, il nostro div quadrato diventa “trascinabile” all’interno della pagina HTML.
Completiamo il nostro Drag & Drop individuando l’elemento sul quale rilasciare il nostro quadrato. Per far questo andiamo ad aggiungere a tale elemento alcuni eventListener sugli eventi: dragenter, dragleave, dragover, drop. Per il momento, ai primi tre eventi, assegnamo semplicemente una funzione per evitare la propagazione dell’evento ai listener di default.
... function noPropagate(event){ if (event.preventDefault) event.preventDefault(); if(event.stopPropagation) event.stopPropagation(); return false; } ...
Infine implementiamo il listener dell’evento drop:
... function handleDrop(event) { if (event.preventDefault) event.preventDefault(); if (event.stopPropagation) event.stopPropagation(); document.getElementById('destinazione').appendChild( document.getElementById(event.dataTransfer.getData("Text"))); return false; } ...
Nell’archivio “esempi-7.zip ” allegato (menu in alto a destra), trovate il codice completo di questo (“drag_and_drop.html”) e degli altri esempi che illustriamo nell’articolo. Chiaramente, come già detto più volte, vanno provati in browser di ultimissima generazione, che supportino le nuove funzionalità HTML5.
Desktop Drag-In
Un’importantissima novità introdotta da HTML5 è la possibilità di interagire con i file locali senza dover utilizzare Flash o Applet. Questo è un enorme passo avanti: in questo modo potremo, infatti, usare delle logiche lato client per il controllo delle dimensioni dei file, per il controllo del tipo di file o anche per mostrare, per esempio, le thumbnail delle immagini da caricare in upload.
Vediamo in breve come si realizza tutto ciò sfruttando le API Drag & Drop del nostro browser. L’implementazione è molto simile a quella appena vista: assegnamo la funzione noPropagate ai tre eventi dragenter, dragleave, dragover in modo che il browser recepisca l’evento drop.
Per quanto riguarda l’evento drop dobbiamo, come visto nella sezione precedente, utilizzare l’oggetto dataTransfer. In questo caso andremo a leggerne la proprietà files che contiene la lista dei file trascinati dal desktop al browser. L’attributo files è di tipo FileList.
Vediamo una prima semplice implementazione per estrapolare le infomrazioni dei file caricati:
…
// FileList
var files = event.dataTransfer.files;
var info = “”;
for (var i = 0, file; file = files[i]; i++) {
info += ‘
- Nome: ‘+ file.name + ‘, tipo: ‘
+ file.type + ‘, dim: ‘+ file.size +’ bytes
‘;
}
document.getElementById(‘info’).innerHTML = ‘
- ‘ + info + ‘
'; ...
In files abbiamo un array di oggetti di tipo File. Scorrendolo tramite il ciclo for possiamo, per ogni elemento, stampare il nome, il tipo e la dimensione.
Adesso complichiamo un po’ le cose. Modifichiamo la funzione in modo che ci permetta di visualizzare una thumbnail dei file trascinati nel caso in cui questi siano file di tipo immagine.
... var files = event.dataTransfer.files; var file; for (var i = 0; file = files[i]; i++) { if (!file.type.match('image.*')) { continue; } var reader = new FileReader(); reader.onload = function(evt) { document.getElementById('thumbs').innerHTML += ''; }; reader.readAsDataURL(file); } return false; ...
Come possiamo vedere dal codice, l’oggetto che si occupa di caricare in memoria il file specificato è FileReader. Questo oggetto ci mette a disposizione 4 metodi per il caricamento dei file:
- readAsDataURL(): il result contiene il file come data URL;
- readAsBinaryString(): il result contiene il file come stringa binaria;
- readAsText(): il result contiene il file sottoforma di stringa di testo codificata in UTF-8;
- readAsArrayBuffer(): il result contiene il file come oggetto ArrayBuffer.
Al termine del caricamento viene scatenato l’evento load sull’oggetto FileReader che ci permette di accedere al contenuto del file caricato (evt.target.result). Il codice completo dell’esempio lo trovate nel file “desktop_dragin.html”.
Desktop Drag-Out
Il download di un file non è mai stato così semplice: basterà trascinare il collegamento al file dalla pagina web al nostro desktop per avviare immediatamente lo scaricamento. Vediamo con un esempio pratico come si realizza:
... data-downloadurl="MIMETYPE:FILENAME:ABSOLUTE_URI_TO_FILE">download ... var files = document.querySelectorAll('.dragout'); for (var i = 0, file; file = files[i]; ++i) { file.addEventListener('dragstart', function(e) { e.dataTransfer.setData('DownloadURL', this.dataset.downloadurl); }, false); } ...
A un classico link HTML aggiungiamo l’attributo draggable=”true” e l’attributo data-downloadurl contenente in ordine il mime-type del file, il nome con cui salvare il file, e il percorso assoluto del file sul server. Nel codice JavaScript non dobbiamo far altro che associare all’evento dragstart applicato sul link, la chiamata al metodo event.dataTransfer.setData(), passando il valore di data-downloadurl.
Il codice completo dell’esempio si trova nel file “desktop-dragout.html”.
Geolocalizzazione
Geolocalizzazione è il processo atto a determinare la posizione dell’utente rispetto a un sistema di riferimento geografico. Secondo le direttive del W3C, il browser deve obbligatoriamente chiedere l’autorizzazione esplicita all’utente prima di poter inviare le informazioni di posizione geografica al sito web visitato. Per questo motivo, ad ogni chiamata dell’API di geolocalizzazione, vi verrà chiesto se autorizzare o meno l’invio della posizione.
Figura 1 – L’utente deve esplicitamente autorizzare il browser a conoscere i suoi dati di localizzazione.
Il codice è veramente semplice in questo caso.
... if (navigator.geolocation) { function positionHandler(position) { var dest = document.getElementById('result'); dest.innerHTML = 'Lat: '+position.coords.latitude +', Lng: '+position.coords.longitude +', Accur: '+position.coords.accuracy; } function errorHandler(){}; navigator.geolocation.getCurrentPosition(positionHandler, errorHandler); } ...
Per prima cosa controlliamo il supporto della geolocalizzazione da parte del browser. Per ottenere la posizione ci basterà richiamare il metodo getCurrentPosition sull’oggetto geolocation. È sempre un buon metodo di lavoro implementare l’errorHandler, anche perche’ come detto è possibile che l’utente non accetti l’invio della propria posizione.
Device Orientation
L’ultima API di cui intendiamo parlarvi è deviceorientation tramite la quale è possibile ottenere le informazioni di orientamento del dispositivo dell’utente. Contrariamente a quanto si potrebbe pensare, l’orientamento del device non è limitato solo a dispositivi mobili come smartphone e tablet ma anche alcuni computer portatili di fascia alta, MacBook Pro in primis, sono equipaggiati con accelerometri e/o giroscopi.
Vediamo subito il codice necessario per realizzare l’orientamento dei dispositivi:
... window.addEventListener('deviceorientation', function(event) { var a = event.alpha; var b = event.beta; var g = event.gamma; var r= document.getElementById('result'); r.innerHTML = a + ' - ' + b + ' - ' + g; }, false); ...
I valori di alpha, beta e gamma restituiti specificano l’orientamento rispetto al sistema di coordinate fisse terrestri. Alpha è la rotazione rispetto all’asse z e quindi indica in quale direzione è orientati il dispositivo; beta è la rotazione rispetto all’asse x e quindi la rotazione fronte/retro e gamma è la rotazione rispetto all’asse y ovvero la rotazione destra/sinistra.
Nei browser che non supportano questa funzionalità, o nei dispositivi in cui non siano presenti accelerometri/giroscopi come buona parte dei computer portatili/desktop, il browser non mostrerà alcun comportamento particolare, o si limiterà a restituire come testo i valori null – null – null relativi ai tre assi.
Conclusioni
Come già abbiamo avuto modo di illustrare nei primi articoli della serie, le nuove specifiche HTML5 tendono a risolvere in maniera sempre più dichiarativa anche elementi che in precedenza erano di pertinenza dei CSS e talvolta di JavaScript. Questa semplificazione abbraccia anche le API JavaScript, come dimostrano alcuni esempi visti nell’articolo. “Meno codice, più funzioni” potrebbe essere uno slogan che ben sintetizza questa tendenza in atto.
In realtà queste funzioni, in buona parte, già esistevano: chi ha scritto codice JavaScript avrà però vissuto sulla propria pelle le difficoltà legate alla corretta gestione del cross browsing. La nascita e la fortuna dei numerosi framework che si sono imposti negli ultimi anni è dipesa essenzialmente dalla facilitazione del lavoro che essi garantivano, pur in un panorama non “normato”. In questo senso, il passo successivo, e probabilmente la mossa vincente di HTML5, è stata quella di portare all’interno delle proprie specifiche una fetta significativa di queste API, “forzando” i browser ad adattarsi ad esse in tempi relativamente brevi.
Riferimenti
[1] Le specifiche per il drag & drop
http://dev.w3.org/html5/spec/dnd.html#dnd