Introduzione
Una guida operativa
Affrontare un tema vasto come quello dell'HTML5 spaventa sempre un po', soprattutto quando
l'obiettivo è quello di abbracciare l'intera tecnologia sottostante le specifiche proponendo al contempo
un'opera che sia fruibile e divertente da leggere. Il primo passo che abbiamo deciso di compiere nella
stesura di questo progetto è stato individuare il target di lettori ai quali la guida vorrebbe rivolgersi.
Abbiamo allora identificato da un lato un gruppo di sviluppatori interessati alla consultazione di
specifiche referenze del linguaggio, dall'altro un insieme di lettori desiderosi di informarsi a tutto
tondo sulle novità offerte dall'HTML5. A questa commistione di interessi si aggiunge una naturale
segmentazione della guida secondo i due temi che maggiormente permeano le specifiche: il nuovo
approccio semantico nella creazione di pagine web e il ricco set di API reso disponibile.

Il percorso si snoda in modo organico tra le due macro-sezioni alternando lezioni di stampo
divulgativo, ottime per avere una visione di insieme della tematica trattata, a percorsi verticali
costruiti attorno a funzionalità specifiche. Non sono previsti articoli introduttivi alla sintassi del
linguaggio HTML e nemmeno approfondimenti su elementi o API già presenti nelle versioni precedenti,
a meno che questi non abbiano subito un cambio radicale; se fosse necessario recuperare
informazioni su tali aspetti rimandiamo alla lettura della guida HTML 4
(http://xhtml.html.it/guide/leggi/51/guida-html/) redatta da Wolfgang Cecchin.


I progetti guida
Per questa guida abbiamo deciso di combinare i tanti piccoli esempi dedicati ad ogni lezione in un
vero e proprio progetto che sappia anche mostrare come le singole funzionalità HTML5 lavorano
insieme.

In realtà i progetti sono due, come due gli aspetti principali di questa specifica: al termine delle
lezioni legate al nuovo supporto semantico dell'HTML5 la combinazione dei vari esempi mostrati darà
vita ad untemplate per blog studiato per trarre vantaggio da elementi come <section> e
<article>, dai nuovi content model e dalle novità in materia di form. La parte incentrata sulle API
dedicate allo sviluppo di applicazioni web invece includerà tutti gli elementi necessari alla stesura di
una lavagna virtuale sulla quale si potranno tracciare linee utilizzando il mouse e che darà la
possibilità di salvare in locale le opere create.

Chiaramente tale scelta è stata implementata in modo da non pregiudicare l'indipendenza delle
singole lezioni ma con l'idea di suggellare un ulteriore filo conduttore all'interno dell'opera.

Figura 1 - Anteprima del template per blog in HTML5 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_1/blog_preview.png)


Alcuni prerequisiti
Nella prossima lezione ci interesseremo con maggior attenzione alla timeline ufficiale HTML5, per ora
basti sapere che la data di accettazione delle specifiche come standard W3C è ancora
sufficientemente lontana. Nonostante questo, buona parte di quanto verrà trattato in questa guida è
già disponibile sulla grande maggioranza dei browser e, come vedremo, alcuni aspetti del linguaggio
sono da tempo in produzione su portali di notevoli dimensioni, come youtube.com
(http://www.youtube.com/html5) o vimeo.com (http://vimeo.com/blog:268).

Esistono tuttavia ampie porzioni delle specifiche che, perché meno strategiche, di più difficile
implementazione o meno mature, sono ad oggi supportate in modo superficiale e disomogeneo; per
poter beneficiare al massimo delle demo e degli esempi anche per elementi o API che ricadono in
questa categoria si consiglia quindi di dotarsi di un browser che utilizzi WebKit come layout engine in
quanto dotato del più ampio supporto HTML5 ad oggi disponibile sia per le parti della specifica ormai
consolidate sia per quelle al momento più 'sperimentali'.

In particolare, tutto il codice di questa guida è stato sviluppato e testato usando la ‘Nightly Build' di
Chromium, cioè la versione speciale per fini di sviluppo che contiene ogni feature ad oggi
implementata, per quanto sperimentale esso sia. La pagina per il download, disponibile per i principali
sistemi operativi, è raggiungibile all'indirizzo http://build.chromium.org/f/chromium/snapshots/
(http://build.chromium.org/f/chromium/snapshots/) seguendo il link nominato come il proprio
sistema operativo e successivamente la cartella recante il numero più alto tra i presenti.

Tabella della compatibilità sui browser

Se Chromium, lo accennavamo, garantisce ad oggi il supporto più ampio alle funzionalità previste
nella specifica e in via di definizione presso il W3C e il WHATWG, la maggior parte dei browser
commerciali più diffusi, con ritmi e tempi diversi, si sta adeguando. Internet Explorer 9 e Firefox 5,
rilasciati nella primavera di quest’anno, condividono infatti un ottimo supporto a queste specifiche.
Lo stato dell'arte relativamente al supporto HTML5 lo abbiamo tracciato in questa tabella di
compatibilità (http://www.html.it/guide/esempi/html5/tabella_supporto/tabella.html) che sarà via via
aggiornata con l'estendersi del supporto alle feature che attualmente non sono supportate. Estratti
della tabella sono inseriti anche a corredo delle lezioni dedicate a ciascun elemento.


Mappa della guida
Nell'immagine seguenti è riassunta l'intera struttura dell'opera mettendo in evidenza la posizione delle
singole lezioni rispetto al contenuto globale con l'obiettivo di fornire una panoramica esauriente sui
temi che verranno trattati.


Ai nastri di partenza!
Figura 2 - Mappa della guida (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_1/2.jpg)
Bene, con questo può definirsi conclusa questa necessaria sezione introduttiva, la prossima lezione
affronterà la travagliata storia che ha caratterizzato la definizione di queste specifiche, con un piccolo
ma sentito cameo anche da parte dell'XHTML2.



                                   Da HTML 4 ad HTML5
Un po' di storia
Siete curiosi di sapere come tutto è nato? Venerdì 4 giugno 2004, in una notte buia e tempestosa, Ian
Hickson annuncia la creazione del gruppo di ricerca WHAT (http://www.whatwg.org/), acronimo di
Web Hypertext Application Technology in un sintetico ma ricco messaggio
(http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2004-June/000005.html).

L'obiettivo del gruppo è quello di elaborare specifiche per aiutare lo sviluppo di un web più orientato
alle applicazioni che ai documenti; tra i sottoscrittori di questa iniziativa si annoverano aziende del
calibro di Apple, Mozilla e Opera. Questa piccolo scisma dal W3C è determinato dal disaccordo in
merito alla rotta decisa dal consorzio durante un famoso convegno del 2004 dove, per un pugno di
voti, prevalse la linea orientata ai documenti di XHTML2.

"XHTML 2.0 considered harmful (http://lists.w3.org/Archives/Public/www-html/2003Jan/0123.html)"
è il titolo di un messaggio inviato alla mailing list ufficiale del W3C datato 14 gennaio 2003 che ben
riassume i sentimenti contrastanti che all'epoca si respiravano in merito a queste specifiche. La
principale causa di perplessità è da ricercarsi nella decisione azzardata di non mantenere la retro-
compatibilità con la versione 1.1 eliminando alcuni tag e imponendo agli sviluppatori web un
controllo rigoroso nella creazione delle pagine, pena lo stop del processo di parsing e la
visualizzazione a schermo degli errori di interpretazione. Nei due anni successivi i gruppi XHTML2 e
WHAT proseguono i lavori sulle proprie specifiche in modo indipendente e parallelo, sollevando dubbi
e confusione sia da parte dei produttori di browser che degli sviluppatori web. Emblematico in tal
senso è un articolo firmato da Edd Dumbill nel dicembre 2005 intitolato The future of HTML
(http://www.ibm.com/developerworks/xml/library/x-futhtml1/). Il 27 ottobre 2006 in un post sul
proprio blog intitolato Reinventing HTML (http://dig.csail.mit.edu/breadcrumbs/archive/2006/10/27),
Tim Berners-Lee annuncia la volontà di creare un nuovo gruppo di ricerca che strizzi l'occhio al WHAT
e ammette alcuni sbagli commessi seguendo la filosofia XHTML2:

      Some things are clearer with hindsight of several years. It is necessary to evolve HTML
      incrementally. The attempt to get the world to switch to XML, including quotes around
      attribute values and slashes in empty tags and namespaces all at once didn't work. The
      large HTML-generating public did not move, largely because the browsers didn't
      complain. Some large communities did shift and are enjoying the fruits of well-formed
      systems, but not all. It is important to maintain HTML incrementally, as well as
      continuing a transition to well- formed world, and developing more power in that world.
      Tim Berners-Lee.

Dovranno passare quasi altri due anni di competizione tra le due specifiche, questa volta entrambe
interne al W3C, prima che nel luglio del 2009 lo stesso Tim sancisca di non voler riconfermare il
gruppo XHTML2 per l'anno successivo preferendo di fatto la direzione intrapresa dall'HTML5.
Frattanto il gruppo di ricerca, formato da una commistione di elementi del W3C e del WHAT, sotto la
guida di Ian Hickson, è giunto alla 4° versione delle specifiche (http://www.whatwg.org/specs/web-
apps/current-work/multipage/).

Nonostante il continuo e solerte lavoro e il grande intervallo di tempo speso nella stesura di questo
documento, i passi residui necessari ad eleggere questa tecnologia al rango di 'W3C Reccomandation',
relegandola così tra gli standard riconosciuti, sono ancora molti, al punto che si prevede di
raggiungere l'agognato traguardo soltanto attorno al 2020.

Ricordiamo però che il consorzio si riferisce sempre all'intero universo inscritto nelle specifiche mentre
alcune feature ritenute peculiari ed importanti, ad esempio il tag <video>, sono già implementate da
tempo dalla totalità (o quasi) dei browser in commercio.


Che cos'è l'HTML5?
Domanda semplice solo all'apparenza; siamo sicuramente di fronte alla quinta revisione delle
specifiche HTML ma anche di un vastissimo elenco di funzionalità che si sviluppano attorno al tema
del linguaggio di markup: cerchiamo quindi di fare un po' di ordine.

Per prima cosa è importante ricordare che, anche in virtù della storia della sua nascita, all'interno
dell'HTML5 convivono in armonia due anime: la prima, che raccoglie l'eredità semantica dell'XHTML2,
e la seconda che invece deriva dallo sforzo di aiutare lo sviluppo di applicazioni Web. Il risultato di
questo mix di intenti è più articolato di quanto si immagini; in prima istanza si assiste ad una
evoluzione del modello di markup, che non solo si amplia per accogliere nuovi elementi, ma
modifica in modo sensibile anche le basi della propria sintassi e le regole per la disposizione dei
contenuti sulla pagina. A questo segue un rinvigorimento delle API JavaScript che vengono
estese per supportare tutte le funzionalità di cui una applicazione moderna potrebbe aver bisogno:

       salvare informazioni sul device dell'utente;
       accedere all'applicazione anche in assenza di una connessione Web;
       comunicare in modo bidirezionale sia con il server sia con altre applicazioni;
       eseguire operazioni in background;
       pilotare flussi multimediali (video, audio);
       editare contenuti anche complessi, come documenti di testo;
       pilotare lo storico della navigazione;
       usare metafore di interazione tipiche di applicazioni desktop, come il drag and drop;
       generare grafica 2D in tempo reale;
       generare grafica 3D in tempo reale;
       accedere e manipolare informazioni generate in tempo reale dall’utente attraverso sensori
       multimediali quali microfono e webcam.

Anche l'aspetto semantico a contorno del markup non è dimenticato; ecco quindi nascere le specifiche
per la nuova generazione di microformati e per l'integrazione tra HTML5 e RDFa. Ma non è
tutto, attorno a quello che può essere definito il nucleo autentico delle specifiche gravitano tutta una
serie di altre iniziative, alcune delle quali in avanzato stato di definizione, studiate per:

       accedere alle informazioni geografiche del device dell'utente (posizione, orientamento,...);
       mantenere un database sul device dell'utente;
       generare grafica 3D in tempo reale;

E non dimentichiamo che l'evoluzione dell'HTML viaggia di pari passo con quella dei CSS, che si
avvicinano alla terza versione, e di altri importanti standard come SVG e MathML, e che ognuno di
questi componenti è progettato nella sua versione più recente per recare e ricevere beneficio dagli
altri.


Perché dovremmo passare all'HTML5?
Il panorama di Internet è cambiato molto dall'assunzione a 'W3C Reccomandation' della versione
precedente delle specifiche, avvenuta verso la fine del 1999. In quel tempo il Web era
strettamente legato al concetto di ipertesto e l'azione più comune per l'utente era la fruizione di
contenuti, tipicamente in forma testuale. La mediamente bassa velocità di connessione e il limitato
investimento sul media contribuivano ad una scarsa presenza di applicazioni web, più care da
sviluppare ed esigenti in termini di banda.

Tutto questo era ben rappresentato da un linguaggio, HTML, principalmente orientato ad agevolare
la stesura di semplici documenti testuali collegati fra loro. Negli anni successivi l'interesse
intorno alla rete ha subito una brusca accelerazione e questo ha condizionato positivamente sia la
diffusione che la velocità di connessione della stessa, attirando di conseguenza maggiori investimenti
e ricerca. Al modello di fruizione dei contenuti si è aggiunta la possibilità per l'utente finale di divenire
esso stesso creatore attraverso applicazioni web sempre più elaborate ed interessanti.

Questo nuovo livello di complessità, in termini di sviluppo, ha però dovuto scontrarsi con un set di
specifiche poco inclini ad essere utilizzate per tali fini e che quindi si sono prestate al compito solo a
scapito di infiniti hack e workaround. Esempi di questi 'utilizzi non premeditati' dell'HTML si possono
trovare un po' ovunque, famoso il caso degli attributi rel e ref che hanno assunto nel tempo valori
non previsti, (eg:nofollow) anche esterni alla loro funzione naturale (eg: l'utilizzo di questi due
attributi in librerie come Lightbox).
Parallelamente il percorso di crescita del web ha fatto emergere alcune strutture di contenuto
ricorrenti, ben caratterizzate dal fenomeno dei blog: informazioni di testata, menu di navigazione,
elenchi di articoli, testo a pie' di pagina, ed altri. La parte dedicata al singolo articolo presenta
anch'essa solitamente lo stesso set di informazioni quali autore, data di pubblicazione, titolo e corpo
del messaggio. Anche in questo caso l'HTML4 non ha saputo fornire gli strumenti adatti a consentire
una corretta gestione e classificazione del contenuto obbligando gli sviluppatori web a ripiegare su
strutture anonime, quali <div> e<p>, arricchite di valore semantico con l'utilizzo di attributi quali
class e id.

L'HTML5 nasce per risolvere questi problemi offrendo agli sviluppatori web un linguaggio pronto ad
essere plasmato secondo le più recenti necessità, sia dal lato della strutturazione del
contenutoche da quello dello sviluppo di vere e proprie applicazioni.


Grandi cambiamenti nell'ombra
La differenza principale tra le versioni correnti di HTML e XHTML risiede nella sintassi. Il linguaggio di
markup creato da Tim Berners-Lee, e che ancora oggi popola i nostri browser, è stato studiato come
applicazione del più generico SGML, Standard Generalized Markup Language; ne è la prova la
dichiarazione di Document Definition Type che dovrebbe essere posta nella prima riga di una pagina
Web ad indicare la grammatica, HTML per l'appunto, usata nel documento:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/ strict.
dtd">




In realtà la quasi totalità dei browser ignora la definizione e interpreta il documento secondo logiche
più permissive, frutto di anni di eccezioni e di esperienza accumulata su pagine malformate. L'XHTML,
invece, è una versione della sintassi HTML costretta all'interno delle regole XML, a sua volta
grammatica SGML: questo approccio dovrebbe implicare un maggior rigore nella pagina e l'aderenza
a regole quali l'obbligo di chiusura di tutti i tag. Il parser XML inoltre dovrebbe sospendere
l'interpretazione della pagina al primo errore rilevato.

L'arrivo dell'HTML5 introduce una importante novità in questo scenario, per la prima volta l'obiettivo
delle specifiche è quello di definire un linguaggio ubiquo, che possa poi essere implementato
su entrambe le sintassi. L'occasione è buona anche per fare un po' di pulizia e rompere
definitivamente il legame tra HTML e SGML formalizzando e traducendo in standard le regole adottate
da tempo nei browser. Per indicare un documento HTML5 è nata quindi la seguente semplice
istruzione:

<!DOCTYPE html>




Che si affianca a quella da utilizzare in caso si intenda scrivere una pagina XHTML5:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">




E adesso?
Questa lezione aveva l'obiettivo di fornire un contesto storico e operativo ai temi dei quali questa
guida tratterà in modo approfondito. Nelle prossime lezioni affronteremo in sequenza prima la parte
legata al markup ed alla gestione della disposizione del contenuto e successivamente le principali API
introdotte da queste specifiche. Il viaggio all'interno dell'universo dell'HTML5 è appena iniziato!



                                    La sintassi di HTML5


Prima di scendere nei dettagli presentando i nuovi elementi e le nuove API definite nella specifica, è
necessario spendere qualche momento per parlare delle regole sintattiche introdotte dall’HTML5, per
larga misura ereditate e razionalizzate dalla precedente versione delle specifiche. In primo luogo
familiarizziamo con il concetto noto di tag. Esistono tre distinte versioni di questa particella, ognuna
di esse si applica selettivamente solo ad alcuni elementi:

Tag ‘classico’
<p> bla bla bla bla ... </p>


Tag ‘vuoto’
<img src="immagine_interessante.jpg" alt="Una immagine interessante">

Tag ‘autochiudente’
<rect x="150" y="40" width="60" height="30" fill="black" stroke="black"/>




Gli elementi HTML5 si possono dividere in tre categorie sulla base della tipologia di tag da usare per
implementarli.

1. Elementi normali: sono quelli che possono racchiudere dei contenuti sotto forma di testo,
commenti HTML, altri elementi HTML, etc. Sono elementi normali, dunque, i paragrafi (<p>), le liste
(<ul>), i titoli (<h1>), etc. Salvo specifici casi, cui accenneremo nel seguito della lezione, gli elementi
normali vengono definiti attraverso un tag di apertura (<p>) e un tag di chiusura (</p>).

2. Elementi vuoti: gli elementi vuoti sono quelli che non possono avere alcun contenuto. Per questi
elementi si utilizza un tag ‘vuoto’. Essi sono: area, base, br, col, command, embed, hr, img, input,
keygen,link, meta, param, source, track, wbr.

Per gli elementi vuoti, la chiusura del tag, obbligatoria in XHTML, è invece opzionale. Possiamo
dunque definire un tag <img> secondo le regole XHTML:

<img src="immagine.png" alt="testo" />


O seguendo la vecchie regole di HTML 4:

<img src="immagine.png" alt="testo">


3. Elementi provenienti da altri namespace: per questi elementi sono richiesti i tag
‘autochiudenti’. Si tratta degli elementi adottati da specifiche esterne, come SVG e MathML.


Maiuscolo, minuscolo
Una delle differenze principali rispetto alle regole XHTML riguarda l'uso del maiuscolo e del minuscolo
per definire un tag. In XHTML è obbligatorio usare il minuscolo. In HTML5 è consentito scrivere un tag
usando anche il maiuscolo:
<IMG src="immagine.png" alt="testo">




Casi particolari
Esistono alcune casistiche per le quali un tag classico può mancare della sua particella di apertura o di
chiusura; questo succede quando il browser è comunque in grado di determinare i limiti di operatività
dell’elemento. Gli esempi più eclatanti riguardano i tag ‘contenitori’, come head, body e html, che
possono essere omessi in toto a meno che non contengano un commento o del testo come istruzione
successiva. È quindi sintatticamente corretto scrivere un documento come segue:



<meta charset="utf-8">
<title>Pagina HTML5 Valida</title>
<p>Un paragrafo può non avere la particella di chiusura
<ol>
  <li>e anche un elemento di lista
</ol>




Notiamo che, come mostrato nell’esempio, anche i tag p e li possono essere scritti omettendo la
particella di chiusura, a patto che l’elemento successivo sia all’interno di una cerchia di elementi
definita dalle specifiche. A fronte di queste opzioni di semplificazione è però errato pensare che la
pagina generata dal codice di cui sopra manchi, ad esempio, dell’elemento html; esso è infatti
dichiarato implicitamente ed inserito a runtime dallo user-agent.

Per un quadro dettagliato rimandiamo a questa sezione
(http://www.w3.org/TR/html5/syntax.html#optional-tags) delle specifiche.


Attributi
Anche rispetto alle definizione degli attributi HTML5 consente una libertà maggiore rispetto a XHTML,
segnando di fatto un ritorno alla filosofia di HTML 4. In sintesi: non è più obbligatorio racchiudere
i valori degli attributi tra virgolette.

I casi previsti nella specifica sono 4.

Attributi 'vuoti': non è necessario definire un valore per l'attributo, basta il nome, il valore si ricava
implicitamente dalla stringa vuota. Un caso da manuale:

Secondo le regole XHTML:
<input checked="checked" />


In HTML5:
<input checked>




Attributi senza virgolette: è perfettamente lecito in HTML5 definire un attributo senza racchiudere
il valore tra virgolette. Esempio:

<div class=testata>
Attributi con apostrofo: il valore di un attributo può essere racchiuso tra due apostrofi (termine più
corretto rispetto a 'virgoletta singola'). Esempio:

<div class='testata'>




Attributi con virgolette: per concludere, è possibile usare la sintassi che prevede l'uso delle
virgolette per racchiudere il valore di un attributo. Il codice:

<div class="testata">




Semplificazioni
In direzione della semplificazione vanno anche altre indicazioni. Ci soffermiamo su quelle riguardanti
due elementi fondamentali come style e script. La sintassi tipica di XHTML prevede la
specificazione di attributi accessori come type:

<style type="text/css"> regole CSS... </style>
<script type="text/javascript" src="script.js"> </script>




In HTML5 possiamo farne a meno, scrivendo dunque:

<style> regole CSS... </style>
<script src="script.js"> </script>




Conclusioni
Come abbiamo sperimentato, la sintassi HTML5 si caratterizza per una spiccata flessibilità e
semplicità di implementazione. Le osservazioni che abbiamo snocciolato in questa lezione sono
chiaramente valide a patto di implementare la serializzazione HTML; ricordiamo infatti che le
specifiche prevedono anche l’utilizzo di una sintassi XML attraverso l’uso delle istruzioni:

<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">




Infine, per una migliore leggibilità del codice sorgente, consigliamo di ricorrere il meno possibile
all’utilizzo di elementi impliciti, scrivendo sempre tutti i tag necessari nella loro forma completa.



                  Elementi disegnati per un web moderno


All’inizio erano tabelle; ricordiamo tutti bene il tedio provocato dal dover costruire strutture complesse
spezzandole all’interno di una griglia fatta da infiniti <tr> e <td>: un attività noiosa, resa ancora
peggiore dalla scarsa qualità e flessibilità del risultato. Poi arrivarono i <div> e fu una vera e propria
rivelazione; finalmente un modello di costruzione del documento pensato per separare in modo chiaro
i contenuti tipici di una pagina web. Grazie alla commistione tra questo elemento e i CSS nacquero
pagine con codici HTML eleganti e leggibili come:

<html>
<head>
</head>
  <body>
    <div id="header">
       --- Titolo e Testata ---
    </div>
    <div id="body">
        <div id="menu">
        --- Voci di Menu ---
        </div>
        <div id="main_content">
           <div class="post">
           --- Un Post ---
           </div>
           <div class="post">
           --- Un altro Post ---
           </div>
       </div>
    </div>
  </body>
</html>




All’interno di un costrutto come questo è tutto molto semplice da interpretare: basta seguire il flusso
dei rientri di pagina facendosi guidare dai valori semantici attribuiti a id e class.

Passarono anni e il modello cominciò a mostrare le sue prime debolezze; in primo luogo ci si accorse
che da un lato non vi era una regola collettiva e quello che per uno sviluppatore era ‘body’ per l’altro
poteva benissimo essere ‘corpo’; inoltre si realizzò che né il browser né tantomeno i motori di ricerca
avrebbero mai potuto beneficiare di questa divisione semantica, proprio per colpa di quell’arbitrarietà
che permetteva a milioni di siti web di essere organizzati in strutture simili ma sempre leggermente
diverse tra loro e per questo non raggruppabili secondo schemi automatici. Emerse in questo modo
uno dei più grandi problemi dell’HTML4: l’incapacità di descrivere il significato delle
informazioni di una pagina web in un formato interpretabile da altri software. In un mondo
sempre più caratterizzato dall’interconnessione e dall’aggregazione su base semantica dei contenuti ci
si adattò inventando e condividendo convenzioni studiate per rappresentare eventi di calendario,
persone e quant’altro; nacquero così diversi microformati, come ad esempio hRecipe
(http://microformats.org/wiki/hrecipe) per le ricette:

<span class="hrecipe">
    <span   class="fn">Tisana alla liquirizia</span>
    <span   class="ingredient">2 cucchiai di Zucchero</span>
    <span   class="ingredient">20g Radice di liquirizia</span>
    <span   class="yield">2</span>
    <span class="instructions">
Scaldare un pentolino con 200cl d’acqua fino a 95°C;
         versare la radice di liquirizia;
         lasciar macerare per 7 minuti;
         zuccherare e servire.
     </span>
     <span class="duration">
         <span class="value-title" title="PT90M"></span> 90 min
     </span>
</span>




L’HTML5 nasce per gestire ed incanalare tutte queste problematiche; nelle prossime lezioni
scopriremo come beneficiare di nuovi tag disegnati appositamente per le più comuni strutture di
contenuto e di attributi utilizzabili per definire ulteriore contenuto semantico alle pagine. Ma per
arrivare a questo prima serve fare un po’ di pulizia...


Elementi e attributi non più previsti nelle specifiche
Le specifiche HTML5 (http://dev.w3.org/html5/spec/Overview.html) sanciscono definitivamente la
fine di tutta una serie di elementi e attributi che mantengono validità formale solo per
preservare la retrocompatibilità di pagine datate ma sono espressamente vietati nella creazione di
nuovi documenti.

I primi a subire questo esilio sono tutti quei costrutti funzionali alla parte di presentazione e caduti
ampiamente in disuso con l’introduzione dei fogli di stile. Stiamo parlando di elementi come:
basefont, big,center, font, s, strike, tt e u.

Ad essi si aggiunge una moltitudine di attributi più o meno noti, tra i quali ricordiamo: align e
valign, background, bgcolor, cellpadding, border, cellspacing e molti altri. In realtà alcuni tra i
citati perdurano solamente su specifici elementi, per una lista completa ed esaustiva consigliamo di
visionare questa pagina del W3C (http://www.w3.org/TR/html5-diff/#absent-attributes) dedicata al
tema.

Interessante notare come si sia deciso invece di mantenere elementi come i e b; trasformandoli,
però, da modificatori tipografici a semplici indicatori di porzioni di testo particolari e
consentendone l’uso solo come ultima risorsa.

La falce della semplificazione si è successivamente abbattuta su un piccolo elenco di elementi
obsoleti: acronym (sostituibile dal più comune abbr), applet (rimpiazzato da object), isindex (già
arcaico in HTML4) e infine dir, sfavorito nel confronto con ul.

Cadono, infine, anche tutti i tag che gravitano intorno al concetto dei frame, ritenuti dannosi per
usabilità e accessibilità: frame, frameset e noframes.

E con questi ultimi si chiude la lista degli elementi soppressi; in loro onore terminiamo la lezione con
un piccolo snippet che li omaggi:

<center>
    <font color="blue" size="2">
         <big>Addio</big>,
         <s>monti sorgenti dall'acque, ed elevati al cielo;
          cime</s> elementi di markup inuguali,
          noti a chi è cresciuto tra voi,
e impressi nella sua mente,
         non meno che lo sia l'aspetto de' suoi più familiari.
    </font>
    Liberamente adattato da: <acronym title="I Promessi Sposi">IPS</acronym>

</center>




                                      Attributi globali


Di attributi globali (quelli cioè che si possono applicare a tutti gli elementi HTML) ce ne sono
sempre stati: basti pensare al classico ‘id’, disponibile da sempre sulla totalità degli elementi.
HTML5 formalizza questa definizione e arricchisce lo sparuto gruppo con nuovi membri che, come
intuibile, possono essere applicati a un qualsiasi tag di queste specifiche. In questo capitolo
dedicheremo qualche paragrafo ad ognuno dei nuovi arrivati, alcuni dei quali, vedrete, sono
decisamente interessanti.


Modificare il contenuto di una pagina: contenteditable
TinyMCE, CKEditor e WYMeditor sono solo tre di una lunga lista di librerie studiate per offrire uno
strumento di editing testuale su web che superi le classiche textarea. I risultati sono già ad oggi
eccellenti, vicini a prodotti desktop come Microsoft Word, ma le soluzioni implementate fanno tutte
uso di escamotage più o meno furbi per ottenere questo livello di interazione in quanto l’HTML 4 non
prevede alcun modo esplicito di generare controlli del genere.

Durante la stesura delle specifiche HTML5 questo problema è stato affrontato e si è deciso di
sviluppare un approccio comune al rich-editing di un documento re-introducendo un set di specifiche
implementate in sordina da Microsoft (http://msdn.microsoft.com/en-
us/library/ms537837(VS.85).aspx) nella versione 5.5 di Internet Explorer. Questo lavoro ha portato
alla creazione di un nuovo attributo globale:contenteditable, che impostato a true su di un qualsiasi
elemento lo rende modificabile da browser; lo stesso destino subiscono tutti gli elementi in esso
contenuti a meno che non espongano un esplicitocontenteditable=false.

Ma... cosa significa esattamente modificabile da browser? Che tipo di feedback visivo ci si
dovrebbe aspettare? E come si comporterebbe il markup a fronte delle modifiche? Sfortunatamente
qui le specifiche non sono troppo chiare e sanciscono semplicemente che qualsiasi cosa venga
concessa all’utente il risultato deve comunque restare conforme all’HTML5: questa libertà operativa
ha prodotto comportamenti diversi da browser a browser; ad esempio c’è chi traduce il tasto invio
com un <br> e chi invece crea un nuovo paragrafo con <p>. Parallelamente è disponibile anche un set
di API utilissime (http://dev.w3.org/html5/spec/dnd.html%23execCommand) per insistere sulla zona
modificabile con comandi attivati dall’esterno, come ad esempio da una toolbar. Un pulsante che
volesse attivare/disattivare il grassetto sulla selezione corrente dovrebbe invocare la seguente
funzione:

document.execCommand('bold')


Prima di passare oltre sappiate che l’attributo contenteditable è stato applicato con valore true a
all’intera sezione precedente con l’esclusione di questo paragrafo, permettendovi così di editarla e
sperimentare l’interazione di questa nuova interessante caratteristica! (e se selezionate del testo e
cliccate qui (javascript:document.execCommand('bold');), potrete renderlo grassetto!).
Menu contestuali associati ad un elemento:
contextmenu
L’attributo globale contextmenu serve ad associare a un elemento un menu contestuale;
questa forma di interazione è poco comune sul web ma molto praticata sul desktop dove, ad esempio,
su di una cartella di sistema ci si aspetta di poter operare azioni quali ‘copia’, ‘elimina’ e ‘rinomina’.
Vediamo un esempio:

<img src="http://farm4.static.flickr.com/3623/3527155504_6a47fb4988_d.jpg"
contextmenu="image_menu">
<menu type="context" id="image_menu">
    <command label="Modifica il contrasto" onclick="contrastDialog();">
    <command label="Negativo" onclick="negativo();">
</menu>


Cliccando con il tasto destro del mouse sull’immagine il browser dovrebbe mostrare un menu
contenente le due azioni disponibili. Purtroppo ad oggi non esiste ancora nessuna implementazione
funzionante di questa feature, che resta al momento relegata a semplice specifica.


Lʼattributo data-*
L’HTML5 predispone la possibilità di associare ad ogni elemento che compone la pagina un numero
arbitrario di attributi il cui nome può essere definito dall’utente sulla base di esigenze personali, a
patto che venga mantenuto il suffisso ‘data-’; ad esempio:

<img id="ombra"
    class="cane"
    data-cane-razza="mastino corso”
    data-cane-eta="5"
    data-cane-colore="nero"
    src="la_foto_del_mio_cane.jpg">




È inoltre interessante notare come queste informazioni, che arricchiscono e danno valore semantico
all’elemento, siano accessibili anche attraverso un comodo metodo Javascript:

alert("Ombra ha :" + document.getElementById("ombra").dataset.caneEta + " anni");




La fine del display:none in linea: hidden
L’attributo globale hidden è stato introdotto per offrire un’alternativa all’utilizzo del predicato
‘style="display:none"’ che ha subito una proliferazione incontrollata soprattutto a seguito della
diffusione di librerie Javascript come jQuery o Prototype. Un elemento marcato come hidden deve
essere considerato dallo user agent come non rilevante e quindi non visualizzato a schermo.


Spellcheck
La quasi totalità dei browser oggi in commercio contiene un motore di verifica della sintassi
grammaticale. Le specifiche HTML5 introducono un meccanismo per abilitare o disabilitare il
controllo della sintassi su porzioni della pagina modificabili dall’utente. L’attributo in questione si
chiama spellcheck e, quando impostato a true, ordina al browser di attivare il proprio correttore
sull’elemento corrente e su tutti i suoi figli.

Laddove invece non venga impostata nessuna preferenza, le specifiche prevedono che il browser
utilizzi un proprio comportamento di default.


Altri attributi globali
Ci sono un paio di altri attributi globali introdotti dalle specifiche e non trattati in questa sede,
draggable e aria-*; entrambi sottendono un discorso che si estende ben al di là della semplice
implementazione di markup e verranno trattati più avanti nella guida.

Ecco comunque la lista di tutti gli attributi globali previsti dalla specifica:

accesskey, class, contenteditable, contextmenu, dir, draggable
hidden, id, lang, spellcheck, style, tabindex, title



Tabella del supporto sui browser

Attributi globali

contenteditable     5.5+ 3.0+ 3.1+             2.0+           9.0+
contextmenu         No     No    No            No             No
data-*              No     No    5.0+          6.0+           No
draggable           No     3.5+ 5.0+           5.0+           No
hidden              No     4.0+ Nightly build Nightly build Nightly build
spellcheck          No     2.0+ No             No             No


Conclusioni
In questa lezione abbiamo appreso che la nuova configurazione di markup dell’HTML5 è stata studiata
per ovviare a tutti i problemi emersi in anni sviluppo di siti web e applicazioni con la precedente
versione delle specifiche. Nelle prossime lezione introdurremo il nuovo content model, pensato non
più nella forma binaria ‘block’ e ‘inline’ ma articolato in una serie di modelli di comportamento
complessi ed interessanti.




                                 Un nuovo content model
Non più solo div
Ecco come si potrebbe codificare l’esempio della lezione precedente utilizzando i nuovi elementi messi
a disposizione dall’HTML5:

<!doctype html>
<html lang="it">
<head>
</head>
  <body>
<header>
    --- Titolo e Testata ---
    </header>
    <nav>
    --- Voci di Menu ---
    </nav>
    <article>
    --- Un Post ---
    </article>
    <article>
    --- Un altro Post ---
    </article>
</body>
</html>




Come si può notare, i tag introdotti hanno un nome in strettissima attinenza con il proprio
contenuto; questo approccio risolve in modo elegante sia il problema dell’utilizzo dell’attributo class
con valore semantico, sia la riconoscibilità delle singole aree del documento da parte di un browser.
Ma non è tutto; l’introduzione di article, nav, header e altri tag che vedremo, impone anche
sostanziose novità al modo in cui lo user-agent deve comportarsi nell’interpretare questi elementi.


Contenuti in una bolla di sapone
Partiamo dal seguente esempio HTML4:

<html>
<body>
  <h1>I diari di viaggio:</h1>
  <h2>A spasso per il mondo alla scoperta di nuove culture:</h2>
  <h3>Giro turistico della Bretagna</h3>
  <p>lorem ipsum..</p>
  <h3>Alla scoperta del Kenya</h3>
  <p>lorem ipsum..</p>
  <h3>Cracovia e la Polonia misteriosa</h3>
  <p>lorem ipsum..</p>
  <p>tutti i viaggi sono completi di informazioni su alberghi e prezzi</p>
</body>
</html>




Se lo visualizziamo avremo un risultato assolutamente strutturato come questo:

Figura 3 (click per ingrandire) - Struttura del documento
(http://www.html.it/guide/esempi/html5/imgs/lezione_5/immagine_1.png)

Supponiamo ora di voler dividere i viaggi per continente. Con il modello attuale saremmo obbligati a
cambiare l’elemento h3 in h4 in modo da fare spazio alla nuova suddivisione:

<html>
<body>
  <h1>I diari di viaggio:</h1>
  <h2>A spasso per il mondo alla scoperta di nuove culture:</h2>
  <h3>Europa</h3>
  <h4>Giro turistico della Bretagna</h4>
  <p>lorem ipsum..</p>
  <h4>Cracovia e la Polonia misteriosa</h4>
  <p>lorem ipsum..</p>
  <h3>Africa</h3>
  <h4>Alla scoperta del Kenya</h4>
  <p>lorem ipsum..</p>
  <p>tutti i viaggi sono completi di informazioni su alberghi e prezzi</p>
</body>
</html>




Questo accade perché la gerarchia delle intestazioni è assoluta rispetto all’intero documento e ogni
tag <h*> è tenuto a rispettarla. Nella maggior parte dei casi però questo comportamento è fastidioso
in quanto è molto comune avere a che fare con contenuti che, come articoli o commenti, vorremmo
avessero una struttura indipendente dalla loro posizione nella pagina. In HTML5 questo è stato reso
possibile definendo una nuova tipologia di content model, chiamato ‘sectioning content’, al quale
appartengono elementi come article e section. All’interno di tag come quelli appena citati la vita
scorre come in una bolla di sapone, quindi l’utilizzo di un <h1> è considerato relativo alla sezione in
cui si trova.
Riprendiamo l’esempio precedente ed interpretiamolo in salsa HTML5:

<!doctype html>
<html>
<head>
  <title>I diari di viaggio</title>
</head>
<body>
  <header>
    <hgroup>
      <h1>I diari di viaggio:</h1>
      <h2>A spasso per il mondo alla scoperta di nuove culture:</h2>
    </hgroup>
  </header>
  <section>
    <h1>Europa</h1>
      <article>
        <h1>Giro turistico della Bretagna</h1>
        <p>lorem ipsum..</p>
      </article>
      <article>
        <h1>Cracovia e la Polonia misteriosa</h1>
        <p>lorem ipsum..</p>
      </article>
  </section>
  <section>
    <h1>Africa</h1>
    <article>
      <h1>Alla scoperta del Kenya</h1>
      <p>lorem ipsum..</p>
    </article>
  </section>
  <p>tutti i viaggi sono completi di informazioni su alberghi e prezzi</p>
</body>
</html>




Molto meglio! Ora i singoli componenti di questo documento sono atomici e possono essere spostati
all’interno della pagina senza dover cambiare la loro struttura interna. Inoltre, grazie a queste
divisioni, il browser riesce a discernere perfettamente il fatto che l’ultimo paragrafo non appartenga al
testo del viaggio in Kenia.

Diamo prova dell’atomicità creando un blocco dedicato all’ultimo articolo inserito: ‘Un week-end a
Barcellona’:

<!doctype html>
<html>
<head>
  <title>I diari di viaggio</title>
</head>
<body>
<header>
    <hgroup>
      <h1>I diari di viaggio:</h1>
      <h2>A spasso per il mondo alla scoperta di nuove culture:</h2>
    </hgroup>
  </header>
  <article>
    <h1>Un week-end a Barcellona</h1>
    <p>lorem ipsum..</p>
  </article>

<!-- resto della pagina -->




Anche grazie a questo content model l’HTML5 introduce un nuovo e preciso algoritmo per il calcolo
dell’outline del documento. La vista ad outline, tipica nei software di word processing e ancora non
presente nei browser, è utilissima nella navigazione dei contenuti di una pagina. Sperimentiamo
questa feature installando un’apposita estensione per Chromium
(https://chrome.google.com/extensions/detail/afoibpobokebhgfnknfndkgemglggomo):

Figura 4 (click per ingrandire) - Vista ad outline del documento




(http://www.html.it/guide/esempi/html5/imgs/lezione_5/immagine_2.png)

È interessante notare come l’algoritmo non solo identifichi correttamente i vari livelli di profondità, ma
per ognuno di essi sappia anche recuperare il titolo adeguato. Nell’HTML5 è vitale che il design della
pagina si rispecchi in una outline ordinata e coerente, questa infatti è la miglior cartina tornasole
possibile in merito al corretto utilizzo delle specifiche: ad esempio, in una prima revisione della
lezione, il codice HTML precedente mancava dell’elemento hgroup, utile a raggruppare elementi che
concorrono a formare un titolo. L’errore è stato individuato e corretto proprio grazie alla
visualizzazione di una outline errata:

Figura 5 (click per ingrandire) - Strutturazione corretta del documento
(http://www.html.it/guide/esempi/html5/imgs/lezione_5/immagine_3.png)
Concludiamo la trattazione di questo content model citando la presenza di alcuni elementi che, pur
seguendo le linee interpretative del 'sectioning content', devono essere ignorati dall'algoritmo di
outline. Tali tag sono definiti 'sectioning roots' ed il motivo della loro esclusione è legato al fatto che
essi non concorrono tanto alla struttura dei contenuti della pagina quanto all'arricchimento della
stessa. Fanno parte di questo elenco elementi come: blockquote, details, fieldset, figure e td.
Seppur esclusi dall'outline del documento, nulla vieta agli appartenenti di questo gruppo di avere una
propria outline interna.




   Panoramica sui content model e presentazione del primo
                       progetto guida


Lo scopo di questa lezione è duplice: da un lato riallacciarsi al tema del sectioning content per offrire
una visione di ampio respiro in merito alle differenti tipologie di disposizione di contenuto offerte
dall'HTML5, dall'altro iniziare la stesura del primo progetto guida.


Una panoramica completa
Esistono altri content model oltre a quelli trattati nella lezione precedente, alcuni implicitamente
presenti anche nelle vecchie specifiche, altri nuovi di zecca, per una rappresentazione grafica rimando
all'ottima infografica (http://www.whatwg.org/specs/web-apps/current-work/multipage/content-
models.html#kinds-of-content) dello stesso W3C, fra l'altro realizzata usando la sintassi SVG (provate
a scorrere il mouse sopra le varie aree dell'insieme).

Metadata content

Fanno parte di questa categoria tutti gli elementi utili alla definizione del documento nel suo
insieme: a livello di presentazione o di funzionamento.

Tag: base, command, link, meta, noscript, script, style, title.
Sectioning content

Ne abbiamo appena parlato: il gruppo contiene tutti quegli elementi studiati per ospitare
contenuti atomici e semanticamente ordinati. È importante utilizzare almeno un appartenente
alla categoria heading content all’interno di ognuno di questi tag, questo anche per non avere un
outline popolato da voci senza titolo (vedi immagine).

Tag: article, aside, nav, section.

Figura 6 (click per ingrandire) - Visualizzazione della struttura del documento




(http://www.html.it/guide/esempi/html5/imgs/lezione_6/immagine_4.png)

Heading content

Tutti gli appartenenti a questo gruppo servono per definire titoli. Interessante notare come se la
presenza di uno di questi elementi non sia associata ad un sectioning content questo venga definito
implicitamente.

Tag: h1, h2, h3, h4, h5, h6, hgroup

Phrasing content

Incorpora il testo del documento così come tutti i modificatori tipografici e visuali dello stesso.

Tag: a (solo se contiene phrasing content a sua volta), abbr, area (se figlio di un elemento map),
audio, b, bdi, bdo, br, button, canvas, cite, code, command, datalist, del (solo se contiene
phrasing content a sua volta), dfn, em, embed, i, iframe, img, input, ins (solo se contiene phrasing
content a sua volta), kbd, keygen, label, map (solo se contiene phrasing content a sua volta), mark,
math, meter,noscript, object, output, progress, q, ruby, s, samp, script, select, small, span,
strong, sub, sup, svg, textarea, time, var, video, wbr.

Embedded content

Ne fanno parte tutti quegli elementi che, come il nome del gruppo suggerisce, incorporano nella
pagina oggetti esterni.

Tag: audio, canvas, embed, iframe, img, math, object, svg, video.

Interactive content

Questa categoria comprende tutto ciò specificatamente studiato per interagire con l’utente.
Tag: a, audio (con l’attributo controls presente), button, details, embed, iframe, img (con
l’attributo usemap presente), input (con l’attributo type diverso da hidden), keygen, label, menu
(con l’attributotype="toolbar"), object (con l’attributo usemap presente), select, textarea, video
(i con l’attributo controls presente).

Come avrete notato ogni elemento può appartenere ad uno o più content models, anche a seconda
della sua configurazione degli attributi. Oltre ai gruppi citati, che sono i principali, va ricordato flow,
che raggruppa la quasi totalità degli elementi e alcuni gruppi creati specificatamente per i controlli dei
form, che vedremo più avanti.


Progetto guida - nel tag HEAD
Cogliamo l’occasione di questa panoramica alla struttura dell’HTML5 per gettare le prime fondamenta
del progetto guida: in questo capitolo vedremo come impostare e strutturare il contenitore della
pagina ed il tag head. Creiamo un file index.html ed iniziamo ad inserire i primi elementi:

<!doctype html>
<html lang="it">
<head>
  <title>We5! Il blog della guida HTML5</title>
</head>
</html>




Fin qui, eccezion fatta per il doctype, nessuna differenza rispetto alla versione 4 delle specifiche;
andiamo avanti aggiungendo alcuni tag meta e link:

...
<head>
  <title>We5! Il blog della guida HTML5</title>
  <link rel="stylesheet" href="monitor.css" media="screen">
  <link rel="stylesheet" href="printer.css" media="print">
  <link rel="stylesheet" href="phone_landscape.css" media="screen and (max-device-widt
h: 480px) and (orientation: landscape)">
  <link rel="stylesheet" href="phone_portrait.css"
  media="screen and (max-device-width: 480px) and (orientation: portrait)">
</head>
...




Incuriositi dalla strana sintassi degli attributi media degli ultimi due <link>? State osservando una
tipica media query: il CSS viene interpretato solamente se le condizioni dell’espressione sono valutate
come vere dallo user agent. Le media query, profetizzate già nel 1999, consentono di identificare il
corretto CSS per un dato device con un incredibile livello di dettaglio, alcuni esempi:

<!-- TV con scan dell’immagine progressiva -->
<link rel="stylesheet" href="progressive.css" media="tv and (scan: progressive)">
<!-- Stampanti con almeno 1000dpi --->
<link rel="stylesheet" href="printer.css" media="print and (min-resolution: 1000dpi)">


<!-- Retina display -->
<link rel="stylesheet" href="retina.css" media="screen and (-webkit-min-device-pixel-
ratio: 2)">
<!-- Device monocromatici (Kindle, etc..) -->
<link rel="stylesheet" href="mono.css" media="screen and (monochrome)">




Bene, completiamo questo capitolo aggiungendo icone e charset:

<head>
    <meta charset="utf-8">
    <!-- ..gli altri tag.. -->
    <link rel="icon" href="standard.gif" sizes="16x16" type="image/gif">
    <link rel="icon" href="iphone.png"   sizes="57x57" type="image/png">
    <link rel="icon" href="vector.svg"   sizes="any"   type="image/svg+xml">
</head>




Come potete notare è ora possibile specificare un attributo sizes per ogni icona, in questo modo lo
user agent può liberamente scegliere quale icona abbia le dimensioni più adatte. Ci sono due motivi
che giustificano l’inserimento della direttiva ‘charset’ in questo progetto: in primo luogo la nuova
sintassi è molto più succinta della passata, seppur ancora valida:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">




In seconda istanza è intenzione dell’autore sottolineare come sussistano reali rischi di sicurezza legati
all’assenza di questa direttiva.

Dalla prossima lezione analizzeremo nel dettaglio i singoli elementi che concorrono alla costituzione
della nuova impalcatura semantica del linguaggio.




                                              Header
Funzioni e dati tecnici
Il tag header serve a rappresentare "un gruppo di ausili introduttivi o di navigazione". Tale
definizione, seppure apparentemente vaga, contiene in sé i concetti chiave per comprendere appieno
la funzione di questo tag:

   1. L'elemento <header> è un contenitore per altri elementi.
   2. L'elemento <header> non va confuso con quella che è la testata/intestazione principale di un
      documento (quella che oggi si definisce in genere con il tag <h1>).
   3. La natura e gli scopi dell'elemento <header> non dipendono dalla sua posizione nel documento,
      ma dai suoi contenuti (ausili alla navigazione o elementi introduttivi).
   4. Il suo uso non è obbligatorio e in alcuni casi può risultare superfluo se non utilizzato in maniera
      appropriata.

<header>
 <h1>Questo è un titolo</h1>
 <h2>Questo è un sotto-titolo</h2>
 [...]
</header>




Header: esempi concreti
Riprendendo il nostro progetto guida, dove nella lezione precedente
(http://xhtml.html.it/guide/lezione/4966/panoramica-sui-content-model-e-presentazione-del-primo-
progetto-guida/) abbiamo definito il contenuto dell'<head>:

<head>
    <meta charset="utf-8">

    <title> We5! Il blog della guida HTML5 </title>

    <link rel="stylesheet" href="monitor.css" media="screen">
    <link rel="stylesheet" href="printer.css" media="print">
    <link rel="stylesheet" href="phone_landscape.css"
          media="screen and (max-device-width: 480px) and (orientation: landscape)">
    <link rel="stylesheet" href="phone_portrait.css"
          media="screen and (max-device-width: 480px) and (orientation: portrait)">

    <link rel="icon" href="standard.gif" sizes="16x16" type="image/gif">
    <link rel="apple-touch-icon" href="iphone.png"   sizes="57x57" type="image/png">
    <link rel="icon" href="vector.svg"   sizes="any"   type="image/svg+xml">
</head>




A questo punto possiamo iniziare a comporre il <body> del nostro documento partendo proprio con il
tag <header>, che con l'elemento <hgroup> (http://xhtml.html.it/guide/lezione/4973/hgroup/)
definisce il titolo principale del documento (del sito) e la cosiddetta tagline:

<header>
  <hgroup>
      <h1>We5! Il blog della guida HTML5</h1>
      <h2>Approfittiamo fin da oggi dei vantaggi delle specifiche HTML5!</h2>
  </hgroup>
</header>




Ma header non deve contenere necessariamente solo titoli <hn]]>! Se titolo e sottotitolo principali
sono certamente elementi introduttivi ai contenuti successivi, è naturalmente un ausilio di
navigazione una lista di link che andrà a formare la barra di navigazione principale del sito. Ecco come
possiamo completare la struttura del nostro primo <header>:

<header>
  <hgroup>
    <h1>We5! Il blog della guida HTML5</h1>
    <h2>Approfittiamo fin da oggi dei vantaggi delle specifiche HTML5!</h2>
  </hgroup>
  <nav>
<h1>Navigazione:</h1>
    <ul>
      <li><a href="/home">Home</a></li>
      <li><a href="/about">Gli autori</a></li>
      <li><a href="/refactoring">Il progetto four2five</a></li>
      <li><a href="/archives">Archivio</a></li>
    </ul>
  </nav>
</header>




Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta
nel nostro progetto:

Figura 8 - Struttura del documento: primo header




Abbiamo inserito una sezione di navigazione (http://xhtml.html.it/guide/lezione/4971/nav/) (<nav>
</nav>) introdotta da un elemento <h1> e strutturata con una lista non ordinata.

In realtà, il menu di navigazione non deve essere necessariamente inserito nell'<header>, nel nostro
esempio non poteva essere fatto altrimenti ma esistono numerosi tipi di layout in cui il menu di
navigazione può essere facilmente slegato dagli elementi introduttivi di intestazione del documento.

Il template del nostro progetto guida, lo accennavamo nelle precedenti lezioni, è relativo ad un blog.
Nel corpo del documento, ad un certo punto, trova posto una sezione che ospita i contenuti principali
della pagina, i post. Per definire semanticamente la sezione useremo il tag <section>
(http://xhtml.html.it/guide_preview/lezione/4969/section/); ciascun post sarà definito a livello
strutturale da un tag <article>(http://xhtml.html.it/guide_preview/lezione/4970/article/):
<section>
  <h1>L'ultimo post</h1>
  <article>
  [...]
  </article>
</section>




Si noti, innanzitutto, come il tag <h1> che fa da titolo principale alla sezione non sia racchiuso in un
elemento <header>. Ribadiamo: non è obbligatorio inserire i titoli <hn]]> all'interno di un
contenitore<header>.

A questo punto, dobbiamo definire due elementi fondamentali per la struttura di un post di blog: il
titolo e la data. Sono certamente ausili introduttivi, secondo la definizione da cui siamo partiti. é più
che legittimo e sensato, pertanto, racchiuderli in un tag <header>:

<section>
  <h1>L'ultimo post</h1>
  <article>
    <header>
      <time datetime="2010-11-22" pubdate>Lunedì 22 Novembre</time>
      <h2>Nuove scoperte sul tag video!</h2>
    </header>
    <p>
      [...]
    </p>
    </footer>
      [...]
    </footer>
  </article>
</section>




Ecco quindi come il nostro articolo potrebbe essere rappresentato graficamente:

Figura 9 - Struttura del documento: header degli articoli
I due scenari mostrati rendono bene l'idea rispetto agli utilizzi tipici dell'elemento <header>. La
specifica suggerisce come altri contesti d'uso la tavola dei contenuti di una sezione, un form di ricerca
o i loghi più rilevanti presenti nel documento.

Nella prossima lezione capiremo come utilizzare l'elemento <footer> e quale è la sua rilevanza
semantica all'interno di un template.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<header>                              9.0+ 3.0+ 3.1+ 5.0+ 10.0+




                                               Footer
Funzioni e dati tecnici
L'elemento <footer> deve contenere in genere informazioni sulla sezione che lo contiene come:

      i dati di chi ha scritto i contenuti;
      collegamenti ai documenti correlati;
      i dati di copyright;
      e così via...

Riguardo il suo apporto semantico ad una pagina web sembra essere tutto chiaro, ma più complesso
è il suo utilizzo a livello pratico:
Non necessariamente deve essere inserito solo alla fine di un documento.
      Quando contiene intere sezioni, esse rappresentano: appendici, indici, note, accordi di licenza e
      altri contenuti del genere.
      Non introduce una nuova sezione e quindi non è rilevante per l'outliner
      (http://it.wikipedia.org/wiki/Outliner).
      All'interno di una pagina web possono essere presenti diversi <footer> anche più di uno per lo
      stesso elemento.

<footer>
 <small>©2011 Autore contenuto. Design by Designer sito </small>
</footer>




Footer: esempi concreti
Riprendendo il nostro progetto guida, dopo aver definito definito il contenuto dell'<header>
(http://xhtml.html.it/guide/lezione/4967/header/), possiamo vedere come il <footer> chiuda il blog
distaccandosi dalle altre sezioni in modo molto naturale, racchiudendo al proprio interno una lista che
aggiunge informazioni riguardo l'autore della pagina e la data del suo ultimo aggiornamento; infine il
tag <small>ridefinito nella specifica dell'HTML 5 (http://www.whatwg.org/specs/web-apps/current-
work/multipage/text-level-semantics.html#the-small-element) racchiude il copyright della pagina:

<footer>
  <dl>
    <dt>Creato da</dt>
    <dd><address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganotti</a></add
ress></dd>
    <dt>Ultimo aggiornamento</dt>
    <dd><time datetime="2010-11-23" pubdate>Marted&igrave; 23 Novembre</time></dd>
    <dd>
  </dl>
<small>Tutti i contenuti sono prottetti dalla licenza creative commons</small>
</footer>




Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta
nel nostro progetto (figura 1):

Figura 10 - Struttura del documento: il footer principale
schema template html5 [footer]




Così come l'intero documento, ogni articolo del nostro blog avrà un <footer> contenente il nome
dell'autore ed altre eventuali informazioni aggiuntive:

<section>
  <h1>L'ultimo post</h1>
  <article>
    <header>
      [...]
    </header>
    <p>
      [...]
    </p>
      <footer>
          <dl>
           <dt>autore:</dt>
           <dd><address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganotti</a>
</address></dd>
         <dt>categoria: </dt>
         <dd><a href="categoria/multimedia">multimedia</a>,</dd>
         <dt>tags:      </dt>
           <dd><a href="tags/video">video</a>,</dd>
           <dd><a href="tags/canvas">canvas</a>,</dd>
           <dt>permalink: </dt>
           <dd><a href="2010/22/11/nuove-scoperte-sul-tag-video">permalink</a>,</dd>
           <dt>rank:</dt>
           <dd><meter value="3.0" min="0.0" max="5.0" optimum="5.0">ranked 3/5</meter></
dd>
          </dl>
      </footer>
</article>
</section>

È da notare la scelta di inserire le informazioni riguardanti l'autore dell'articolo all'interno del tag
<dl>; infatti nella specifica HTML5 questo elemento viene ridefinito come contenitore di metadati e
quindi semanticamente corretto all'interno del nostro <footer>.

Ecco quindi come il nostro articolo potrebbe essere rappresentato graficamente, tutte le informazioni
contenute nel <footer> per comodità abbiamo deciso di chiamarle metadati:

Figura 11 - Struttura del documento: footer degli articoli




L'elemento <footer> potrebbe essere inserito anche all'inizio di un documento subito dopo il <body>
oppure all'apertura di un tag <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/)
ma in questi casi non dovrebbe contenere elementi introduttivi riguardo il contenuto della sezione che
lo contiene; il suo uso in questa posizione potrebbe essere dovuto solamente a ragioni pratiche come
ad esempio la duplicazione del <footer> in fondo alla pagina quando i contenuti della stessa sono
molto lunghi:

<body>
<footer>
  <a href="#indice">Torna all'indice</a>
</footer>
<section>
  [Contenuti molto lunghi...]
<section>
<section>
  [Contenuti molto lunghi...]
<section>
<section>
[Contenuti molto lunghi...]
<section>
<footer>
  <a href="#indice">Torna all'indice</a>
</footer>
</body>




Nella prossima lezione parleremo del tag <section> e della sua importanza nel sezionare la pagina in
blocchi semanticamente distinti.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<footer>                               9.0+ 3.0+ 3.1+ 5.0+ 10.0+




                                                Section
Funzioni e dati tecnici
Il tag <section>, secondo la definizione presente nella specifica HTML5, rappresenta una sezione
generica di un documento o applicazione. L'elemento <section>, in questo contesto, individua
un raggruppamento tematico di contenuti,ed in genere contiene un titolo introduttivo.

Vediamo quindi quali sono i punti fondamentali da ricordare riguardo il suo utilizzo:

   1. l'elemento <section> non deve essere utilizzato in sostituzione del <div> per impostare
      graficamente la pagina; inoltre è fortemente consigliato utilizzare i <div> anche quando
      risultano più convenienti per gli script;
   2. l'elemento <section> non deve essere preferito all'elemento <article> quando i contenuti
      possono essere ripubblicati anche su altri siti;
   3. l'elemento <section> e l'elemento <article> non sono indipendenti ed esclusivi: possiamo
      avere sia un <article> all interno di un <section> che viceversa.

<article>
    <section>
        <h1>Titolo 1</h1>
        <p>Testo correlato al titolo 1.</p>
     </section>
     <section>
         <h1>Titolo 2</h1>
        <p>Testo correlato al titolo 2.</p>
    </section>
</article>




L'elemento <section> può essere utilizzato in combinazione con l'attributo cite attraverso il quale è
possibile specificare l'url dalla quale si stanno riportando i contenuti.
Section: esempi concreti
Come abbiamo visto nei capitoli precedenti, il codice del nostro progetto inizia a prendere una forma
più chiara e definita: infatti, dopo aver compreso l'utilità semantica
dell'<header>(http://xhtml.html.it/guide_preview/lezione/4967/header/) e del <footer>
(http://xhtml.html.it/guide_preview/lezione/4968/footer/), capiamo come utilizzare l'elemento
<section> all'interno del nostro blog.

Per strutturare la pagina raggruppando i contenuti correlati, in ordine decrescente incontriamo le
prime due grandi macrosezioni del blog: "l'ultimo post" e "i post meno recenti" contenuti quindi in
due<section>:

<section>
     <h1>L'ultimo post</h1>
      <article>
           [contenuto del post...]
           <section>
           [commenti...]
           </section>
      </article>
</section>
<section>
     <h1>Post meno recenti</h1>
      <article>
      [contenuto del post...]
      </article>
           <article>
      [contenuto del post...]
      </article>
      <article>
      [contenuto del post...]
      </article>
</section>




Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta
nel nostro progetto:

Figura 12 - Struttura del documento: sezioni principali
Nel nostro progetto le <section>, oltre a poter raggruppare i vari <article>
(http://xhtml.html.it/guide_preview/lezione/4970/article/), sono presenti anche all'interno del primo
<article> per suddividere i commenti dal contenuto del post. La sezione dei commenti a sua volta
contiene un'altra sezione contenente il form per l'inserimento di un nuovo commento:

<article>
      [contenuto del post...]
      <section>
          <article>
             [commento1...]
          </article>
          <article>
               [commento2...]
            </article>
            <article>
               [commento3...]
            </article>
            <section>
               [Inserisci un nuovo commento...]
           </section>
      </section>
   </section>
</article>


In questo modo il post è diviso in maniera molto netta rispetto ai propri contenuti solo con l'ausilio dei
tag HTML5, separando quindi i commenti che sono una sezione aggiuntiva eventualmente anche
eliminabile dall'argomento principale trattato all'interno dell'articolo.
Lo schema dell'articolo analizzato è quindi il seguente:

Figura 13 - Struttura del documento: suddivisione semantica del post




Il tag <section> rappresenta un elemento che viene considerato una sezione a sé stante dall'outliner
(http://it.wikipedia.org/wiki/Outliner) e quindi un blocco con dei contenuti univoci che necessitano di
un titolo (<hN>) che li riassuma. Come vedremo nelle prossime lezioni esistono anche altri elementi
nelle specifiche HTML5 che sono considerati "contenitori di sezionamento dei contenuti"
(http://xhtml.html.it/guide_preview/lezione/4965/un-nuovo-content-model/).


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<section>                             9.0+ 3.0+ 3.1+ 5.0+ 10.0+




                                               Article
Funzioni e dati tecnici
Il tag <article> rappresenta una sezione autonoma in un documento, pagina, applicazione o
sito; infatti è potenzialmente ridistribuibile o riutilizzabile, e quindi ripubblicabile in parte o
interamente in diverse pagine.
Esso può identificare il post di un forum, un articolo di una rivista o di un giornale, l'articolo di un
blog, un commento, un widget interattivo, o qualsiasi cosa che abbia un contenuto indipendente.

Prima di vedere un esempio concreto bisogna chiarire alcuni concetti riguardo il suo utilizzo:

   1. quando gli elementi <article> sono nidificati, gli <article> interni rappresentano gli articoli
      che sono in linea di principio relativi al contenuto dell'<article> esterno. Ad esempio, un blog
      che accetta commenti dagli utenti potrebbe rappresentarli come <article> figli annidati
      all'interno dell'elemento padre <article>;
   2. le informazioni relative all'autore dell'<article> non devono essere replicate all'interno degli
      elementi nidificati all'interno dello stesso;
   3. l'elemento <time> con l'attributo pubdate può essere utilizzato per definire la data di
      pubblicazione dell'<article>;
   4. l'elemento <section> e l'elemento <article> non sono indipendenti ed esclusivi: possiamo
      avere sia un <article> all interno di un <section> che viceversa.

<article>
    <header>
        <h1>Titolo articolo</h1>
        <time pubdate datetime="2011-10-09T14:28-08:00"></time></p>
    </header>
    <p>Contenuto dell'articolo</p>
    <footer>
        <p>Informazioni riguardo l'autore</p>
    </footer>
</article>


In sostanza, anche se nelle specifiche non è espresso, l'elemento <article> rappresenta una forma
particolare di <section> (http://xhtml.html.it/guide_preview/lezione/4969/section/).


Article: esempi concreti
Nella lezione precedente abbiamo diviso i contenuti centrali del blog che stiamo costruendo in due
<section>. All'interno della prima <section> possiamo trovare diversi tag <article>: il primo che
incontriamo è l'articolo vero e proprio con tanto di <header>
(http://xhtml.html.it/guide_preview/lezione/4967/header/) contenente il titolo dell'articolo e la data
di pubblicazione e <footer> che all'interno di un <dl> raccoglie i metadati riguardanti l'autore.

All'interno dell'<article> padre sono annidati ulteriori <article> contenenti i commenti all'articolo
racchiusi in una <section> che li rende semanticamente separati dal contenuto principale. Così come
i commenti, anche il form che permette di inserire un'ulteriore commento è inserito all'interno di una
<section>. Possiamo quindi facilmente immaginare come il contenuto del nostro <article> possa
essere citato o ripubblicato in altri blog indipendentemente dai commenti che ha ricevuto.

Ecco quindi il codice dell'<article> sopra descritto:

<section>
  <h1>L'ultimo post</h1><article>
      <header>
          <time datetime="2010-11-22" pubdate>Lunedì 22 Novembre</time>
          <h2>Nuove scoperte sul tag video!</h2>
       </header>
<p>
        Attraverso un utilizzo sapiente del tag canvas è possibile leggere uno stream
        di dati proveniente da un tag video e <mark>manipolarlo in tempo reale</mark>.
      </p>
      <footer>
           <dl>
                <dt>autore:    </dt>
                <dd><address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganott
i</a></address></dd>
                <dt>categoria: </dt>
                <dd><a href="categoria/multimedia">multimedia</a>,</dd>

               <dt>tags:      </dt>
               <dd><a href="tags/video">video</a>,</dd>
               <dd><a href="tags/canvas">canvas</a>,</dd>
               <dt>permalink: </dt>
               <dd><a href="2010/22/11/nuove-scoperte-sul-tag-video">permalink</a>,</dd
>
               <dt>rank:</dt>
               <dd><meter value="3.0" min="0.0" max="5.0" optimum="5.0">ranked 3/5</met
er></dd>
          </dl>
      </footer>
      <section>
          <h3>Commenti</h3>
          <article>
              <h4>
                   <time datetime="2010-11-22" pubdate>Lunedì 22 Novembre</time>
                   Angelo Imbelli ha scritto:
              </h4>
              <p>C'è un bell'esempio sulla rete: effetto ambi-light!</p>
              <footer>
                   <address><a href="mailto:ambelli@mbell.it">Angelo Imbelli</a></addre
ss>
               </footer>
           </article>
           <article>
               <h4>
                    <time datetime="2010-11-23" pubdate>Martedì 23 Novembre</time>
                   Sandro Paganotti ha scritto:
               </h4>
               <p>Bellissimo! Grazie per la segnalazione!</p>
               <footer>
                  <address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganott
i</a></address>
              </footer>
          </article>
           <section>
               <h4>Inserisci un nuovo commento:</h4>
               <form>
               [ campi form per inserire un nuovo commento]
               </form>
</section>
       </section>

  </article>
</section>




Nel seguente schema abbiamo realizzato graficamente come si presenta il nostro articolo
morfologicamente:

Figura 14 - Struttura del documento: suddivisione semantica degli articoli




C'è da notare che anche se il tag <article> rappresenta un elemento che viene considerato una
sezione a sé stante dall'outliner (http://it.wikipedia.org/wiki/Outliner) e quindi un blocco con dei
contenuti unici e un titolo univoco (quindi per ogni blocco avremmo potuto utilizzare un titolo
racchiuso in un <h1>), abbiamo preferito rispettare comunque l'ordine decrescente per i titoli in modo
da rendere i contenuti accessibili anche agli screen reader che al momento non hanno ancora
implementato l'algoritmo outliner.

Nella prossima lezione parleremo del tag <nav> e della sua fondamentale importanza all'interno di
una pagina in HTML5.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali
<article>                               9.0+ 3.0+ 3.1+ 5.0+ 10.0+



                                                    Nav
Funzioni e dati tecnici
Il tag <nav> è uno degli elementi introdotti nelle specifiche HTML5 di più facile comprensione.
Infatti, rappresenta una sezione di una pagina che contiene link (collegamenti) ad altre pagine o
a parti interne dello stesso documento; quindi, in breve, una sezione contenente link di
navigazione.

A questo punto potremmo potremmo porci una domanda: come mai un elemento così scontatamente
fondamentale è stato introdotto solamente adesso? La risposta potrebbe essere che, così come per i
tag visti nelle precedenti lezioni, finalmente si è cercato di incentivare l'uso di standard condivisi
proponendo elementi che possano aiutare gli sviluppatori proprio perché molto vicini ai modelli
mentali oramai assimilati dagli esperti e di semplice comprensione per i novizi del mestiere.

Per poter utilizzare correttamente l'elemento <nav> dobbiamo ricordare i seguenti punti:

   1. solo sezioni che sono costituite da grandi blocchi di navigazione sono appropriati per
      l'elemento <nav>;
   2. i link a pie' di pagina e le breadcumb non devono essere inseriti in una sezione <nav>;
   3. l'elemento <nav> non sostituisce i link inseriti in elementi come gli <ul> o gli <ol> ma deve
      racchiuderli.

<nav>
   <ul>
      <li>Questo   è   un   link</li>
      <li>Questo   è   un   link</li>
      <li>Questo   è   un   link</li>
      <li>Questo   è   un   link</li>
      [...]
   </ul>
</nav>




Nav: esempi concreti
Prima di spiegare in che modo l'elemento <nav> può essere inserito nel progetto che abbiamo preso
come base, riassumiamo brevemente i tag spiegati nelle lezioni precedenti:

      Con l'elemento <header> (http://xhtml.html.it/guide_preview/lezione/4967/header/) abbiamo
      indicato il titolo introduttivo del blog più i titoli dei singoli articoli.
      Con il <footer> (http://xhtml.html.it/guide_preview/lezione/4968/footer/) abbiamo racchiuso
      le informazioni relative agli autori dei contenuti, i metadati e il copyright.
      Con l'elemento <section> (http://xhtml.html.it/guide_preview/lezione/4969/section/) abbiamo
      strutturato la parte centrale della pagina dividendola in due sezioni semanticamente distinte.
      Infine abbiamo utilizzato <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/)
      per racchiudere i contenuti degli articoli.

A questo punto non possiamo che inserire i link presenti nell'<header> all'interno del tag <nav>:
<header>
  <hgroup>
    <h1>We5! Il blog della guida HTML5</h1>
    <h2>Approfittiamo fin da oggi dei vantaggi delle specifiche HTML5!</h2>
  </hgroup>
  <nav>
    <h1>Navigazione:</h1>
    <ul>
      <li><a href="/home">Home</a></li>
      <li><a href="/about">Gli autori</a></li>
      <li><a href="/refactoring">Il progetto four2five</a></li>
      <li><a href="/archives">Archivio</a></li>
    </ul>
  </nav>
</header>



Da notare la presenza del titolo <h1> che serve a specificare più dettagliatamente la funzione del
nostro blocco di link e a conferirgli un titolo valido anche per l'outliner. Il tag <nav>, infatti,
rappresenta un elemento che viene considerato una sezione a sé stante dall'outliner
(http://it.wikipedia.org/wiki/Outliner) e quindi un blocco con dei contenuti univoci che necessitano di
un titolo che li riassuma.

Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta
nel nostro progetto:

Figira 15 - Struttura del documento: visualizzazione grafica del tag nav




In realtà (come già specificato nel paragrafo <header>) il menu di navigazione non deve essere
necessariamente inserito nel <header>, nel nostro esempio non poteva essere fatto altrimenti ma
esistono numerosi tipi di layout in cui il menu di navigazione può essere facilmente slegato dagli
elementi introduttivi di intestazione del documento.

Nel nostro esempio l'elemento <nav> è presente anche nella colonna laterale del blog (<aside>) e
racchiude un menu che ha come link le categorie nelle quali sono inseriti i vari articoli:

<aside>
    <h1>Sidebar</h1>
    <section>
         <h2>Ricerca nel form:</h2>
         <form>
              [form di ricerca dei contenuti...]
         </form>
    </section>
    <nav>
         <h2>Categorie</h2>
         <ul>
              <li><a href="/categoria/multimedia">multimedia</a></li>
              <li><a href="/categoria/text">marcatori testuali</a></li>
              <li><a href="/categoria/form">forms</a></li>
         </ul>
    </nav>
</aside>


Per comprendere quale è la funzione dell'elemento <aside> che contiene il menu laterale non ci resta
quindi che leggere la prossima lezione.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<nav>                                 9.0+ 3.0+ 3.1+ 5.0+ 10.0+




                                               Aside
Funzioni e dati tecnici
L'elemento <aside> rappresenta una sezione di una pagina costituita da informazioni che sono
marginalmente correlate al contenuto dell'elemento padre che la contiene, e che potrebbero
essere considerate distinte da quest'ultimo. Questo è ciò che viene indicato nelle specifiche HTML5,
ma è facile immaginare l'utilità del tag <aside> semplicemente pensandolo come un contenitore di
approfondimentoin cui possiamo inserire gruppi di link, pubblicità, bookmark e così via.

<aside>
  <h1>Questi sono dei contenuti di approfondimento marginali rispetto al contenuto pri
ncipale</h1>
  <nav>
    <h2>Link a pagine correlate al contenuto</h2>
    <ul>
<li>Informazione correlata al contenuto</li>
    <li>Informazione correlata al contenuto</li>
    <li>Informazione correlata al contenuto</li>
    </ul>
  </nav>
  <section>
    <h2>Pubblicità</h2>
    [immagini pubblicitarie]
  <section>
</aside>




Aside: esempi concreti
Ritornando al nostro progetto guida, dopo aver definito il contenuto dell'elemento <nav>, possiamo
analizzare la parte di codice in cui abbiamo utilizzato il tag <aside>:

<aside>
  <h1>Sidebar</h1>
  <section>
    <h2>Ricerca nel form:</h2>
     <form name="ricerca" method="post" action="/search">
      <label> Parola chiave:
        <input type="search" autocomplete="on" placeholder="article, section, ..." name
="keyword" required                                                             maxleng
th="50">
      </label>
      <input type="submit" value="ricerca">
    </form>
  </section>
  <nav>
   <h2>Categorie</h2>
   <ul>
    <li><a href="/categoria/multimedia">multimedia</a></li>
    <li><a href="/categoria/text">marcatori testuali</a></li>
    <li><a href="/categoria/form">forms</a></li>
   </ul>
  </nav>
</aside>




Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta
nel nostro progetto:

Figura 16 - Struttura del documento: visualizzazione grafica del tag aside
Come possiamo notare, il form per la ricerca di parole chiave e i link alle categorie presenti nel nostro
blog non sono informazioni particolarmente rilevanti per il contenuto centrale della nostra pagina,
pertanto possiamo facilmente separarli con l'elemento <aside> che li qualifica come contenuti
marginali.

Nel nostro caso abbiamo utilizzato un <aside> per contenere la colonna sinistra del blog, ma in
realtà, visto che in diversi siti va di moda la presenza di footer molto grossi con diversi link,
consigliamo di utilizzare l'elemento <aside> in combinazione con il tag <nav> che potrebbe essere la
soluzione migliore per questa tipologia di contenuti dato che, come abbiamo potuto constatare nella
lezione dedicata al footer, ciò non è possibile farlo all'interno del tag <footer>.

Così come gli elementi <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/),
<section> (http://xhtml.html.it/guide_preview/lezione/4969/section/) e
<nav>(http://xhtml.html.it/guide_preview/lezione/4971/nav/) anche l'<aside> appartiene alla
categoria dei "contenitori di sezionamento dei contenuti"
(http://xhtml.html.it/guide_preview/lezione/4965/un-nuovo-content-model/) in quanto per l'outliner
necessita di un titolo che riassuma i propri contenuti.

Ricordiamo però che non è obbligatorio inserire un titolo per gli elementi che vengono considerati
delle sezioni a sé stanti dall'outliner, infatti in questo caso queste sezioni rimarrebbero senza titolo
ma non genererebbero nessun errore di validazione.

Vediamo quindi ora che abbiamo completato la struttura del blog come è l'outline del nostro
documento Outline (esempi/outline_progetto/outline.html).

Nella prossima lezione comprenderemo come usare l'elemento <hgroup> e la sua importanza per
l'outline di un documento in HTML5.


Tabella del supporto sui browser
Nuovi tag semantici e strutturali

<aside>                                9.0+ 3.0+ 3.1+ 5.0+ 10.0+




                                               Hgroup
Funzioni e dati tecnici
L'elemento <hgroup> rappresenta l'intestazione di una sezione. L'elemento viene utilizzato per
raggruppare un insieme di elementi h1-h6, quando il titolo ha più livelli, come sottotitoli, titoli
alternativi o slogan.

<hgroup>
  <h1>Questo è il titolo</h1>
  <h2>Questo è un sottotitolo</h2>
</hgroup>


La vera importanza del tag <hgroup> è che maschera l'outline dell'elemento padre che lo contiene;
infatti, l'algoritmo dell'outliner riconosce come un titolo solamente l'heading con il valore più alto e
considera tutti gli altri elementi sottotitoli.

Esempio:

<article datetime="2010-11-22" pubdate >
    <header>
       <hgroup>
          <h2>Le prospettive per il futuro del web</h2>
          <h1>L'HTML 5 rivoluzionerà il mondo del web</h1>
       </hgroup>
    </header>
    <p>Presto il web sarà pieno di siti e applicazioni che saranno in grado di mettere
 in crisi le più grandi case di desktop application...</p>
    <footer>
        <p>Pinco pallino</p>
    </footer>
</article>


Se facessimo l'outline della pagina HTML contenente questo <article>
(http://xhtml.html.it/guide_preview/lezione/4970/article/) ci restituirebbe come titolo della sezione
solamente l'<h1> contenuto nell'<hgroup> (non considerando l'<h2> anche se posto prima nel
codice) poiché è il tag con il valore più alto all'interno della sezione. Senza l'elemento <hgroup> i due
titoli verrebbero considerati entrambi sullo stesso livello d'importanza come titoli dell' <article>.

Nella prossima lezione parleremo del tag <mark> e della sua utilità nell'evidenziare parti
particolarmente importanti del testo.


Tabella del supporto sui browser
Nuovi tag semantici e strutturali
<hgroup>                               9.0+ 3.0+ 3.1+ 5.0+ 10.0+




                                                 Mark
Funzioni e dati tecnici
L'elemento <mark> rappresenta una parte di un testo segnato o evidenziato all'utente a causa
della sua rilevanza anche in un altri contesti. Esso, in un testo in prosa, indica un punto culminante
che non era originariamente presente, ma che è stato aggiunto per attirare l'attenzione del lettore su
una parte del testo che potrebbe non essere stata considerata rilevante dall'autore originale quando il
contenuto è stato scritto.

Questa definizione abbastanza complessa in realtà può essere semplificata di molto chiarendoci le
idee con un esempio: immaginiamo di cercare una determinata parola chiave in diverse pagine web e
che la parola che abbiamo cercato nel motore di ricerca, ci venga evidenziata all'interno della pagina
che andiamo a visualizzare; ciò che abbiamo appena descritto è la situazione ideale per l'utilizzo del
tag <mark>poiché con quest'ultimo dobbiamo racchiudere la parola ricercata segnalandola
graficamente all'utente.

<p>Senza <mark>plug in</mark> di terze parti il web potrebbe diventare
per noi sviluppatori più democratico; con le nuove API HTML5 non abbiamo più bisogno
di diversi <mark>plug in</mark> di terze parti che sino ad ora erano indispensabili
per i contenuti multimediali</p>




Allo stato attuale non esiste un tag HTML standard utilizzato per evidenziare le parole chiave agli
utenti che utilizzano i motori di ricerca per navigare: Google utilizza il tag <em>, Bing il tag <strong>,
Yahoo il tag <b> e così via. Si spera che con l'introduzione dell'elemento <mark> le cose possano
cambiare.

Nella prossima lezione parleremo del tag <time>, un'altra delle novità dell'HTML5.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<mark>                                 9.0+ 4.0+ 5.0+ 5.0+ 11.0+




                   Time e gli attributi pubdate e datetime
Funzioni e dati tecnici
L'elemento <time> rappresenta il tempo su un orologio di 24 ore, o una data precisa nel
calendario Gregoriano accompagnata opzionalmente con un orario e una differenza di fuso orario.

Questo elemento è inteso come un modo moderno per codificare le date e gli orari in maniera
leggibile anche per i computer. Ad esempio, i browser saranno in grado di offrire la possibilità di
aggiungere promemoria per i compleanni o gli eventi in programma in una web application che
funziona da calendario.

<p>Oggi pomeriggio penso che sarò lì per le <time>15:00</time></p>

Prima di inserire l'elemento <time> nelle nostre pagine in HTML5 dobbiamo comprendere quali sono
i contesti in cui è sconsigliato utilizzarlo:

      non bisogna inserire nel tag <time> le date che non possono essere determinate con
      precisione; ad esempio: "un giorno nel lontano inverno del '68","da quando è nato il primo
      uomo"...;
      non bisogna inserire nel tag <time> le date prima dell'introduzione del calendario Gregoriano.

L'elemento <time> può possedere l'attributo pubdate che è di tipo booleano; la sua presenza indica
che la data presente nel tag <time> è anche la data nella quale è stato scritto
l'<article>(http://xhtml.html.it/guide_preview/lezione/4970/article/) padre più vicino, e nel caso
non esistesse un <article> padre allora essa è riferita alla creazione dei contenuti dell'intero
documento.

Ovviamente un elemento che possiede l'attributo pubdate necessita di una data. Per ciascun
<article> può esserci solo un singolo tag <time> con pubdate e la stessa cosa vale per l'intero
documento.

Possiamo specificare in maniera più dettagliata una data aggiungendo l'attributo datetime:

      il valore dell'attributo deve essere una "stringa valida" (http://www.whatwg.org/specs/web-
      apps/current-work/multipage/common-microsyntaxes.html#valid-global-date-and-time-string)
      del tipo (ANNO-MESE-GIORNO-ORE:MINUTI:SECONDI.MILLISECONDI-FUSO ORARIO).
      se l'attributo datetime non è presente allora il contenuto testuale del'tag <time> deve essere
      una "stringa valida" (http://www.whatwg.org/specs/web-apps/current-
      work/multipage/common-microsyntaxes.html#valid-global-date-and-time-string).

<time pubdate datetime="2011-01-20">20 Gennaio</time>

Dobbiamo specificare che l'attributo pubdate in quanto di tipo booleano può essere inserito anche nel
seguente modo:

<time pubdate="pubdate" datetime="2011-01-20">20 Gennaio</time>

Nella prossima lezione vedremo in quali occorrenze utilizzare il tag <meter>.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<time>                               No    No    No    No    No




                                                Meter
Funzioni e dati tecnici
L'elemento <meter> rappresenta una misura scalare all'interno di un intervallo noto, o un
valore frazionario.
Il tag <meter> è anche utilizzato come un indicatore di livello.

<meter value="6" max="8">6 blocchi utilizzati (su un totale di 8)</meter>


Vediamo alcuni contesti in cui non deve essere utilizzato:

        quando indica lo stato di una barra di progresso;
        quando i valori rappresentati sono di tipo arbitrario a scalare; ad esempio non deve segnalare
        un peso o un'altezza a meno che non vi sia un valore massimo riconosciuto.

Esistono 6 attributi per determinare il valore semantico dell'elemento <meter>:

   1.   min: indica il valore limite minimo disponibile;
   2.   max: indica il valore limite massimo disponibile;
   3.   value: indica il valore dell'elemento;
   4.   low: indica un valore che viene considerato basso;
   5.   high: indica un valore che viene considerato alto;
   6.   optimum: indica un valore che viene considerato "ottimale"; se è più alto del valore specificato
        nell'attributo high indica che il valore più alto è il migliore,viceversa è più basso del valore
        specificato nell'attributo low indica che il valore più basso è il migliore.

Se non è specificato un valore minimo e un valore massimo questi sono di default rispettivamente 0 e
1.

Ad oggi l'unico browser che renderizza il tag <meter> è Google Chrome presentandolo graficamente
come una barra di progresso, quindi gli sviluppatori sono fortemente incoraggiati a specificare il suo
valore in formato testuale al suo interno.

Nella prossima lezione illustreremo come utilizzare l'elemento <progress> e la sua utilità nel caricare
i contenuti dei documenti web.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<meter>                                No    No    No    8.0+ 11.0+




                                             Progress
Funzioni e dati tecnici
L'elemento <progress> rappresenta lo stato di completamento di un compito e può essere di
due tipi:

        indeterminato: indica che il compito (task) è in fase di completamento, ma che non è chiaro
        quanto ancora resta da fare prima che l'operazione sia completata (ad esempio perché
        l'operazione è in attesa della risposta di un host remoto);
        determinato quando è possibile ricavare un numero quantificabile nel range da zero a un
        massimo, in modo da ottenere la percentuale di lavoro completato rispetto al totale(ad
        esempio un preloader di un'immagine).

Esistono due attributi che determinano lo stato di completamento dell'attivita del tag <progress>.
L'attributo value, che specifica la quantità di lavoro completata, e l'attributo max, che specifica la
quantità di lavoro richiesta in totale. Le unità sono arbitrarie e non specificate.

Tuttavia, i valori passati come attributi del tag <progress> non sono renderizzati e quindi
dovrebbero comunque essere inseriti in forma testuale nell'HTML in modo da poter fornire un
feedback più preciso agli utenti; inoltre questo elemento attualmente non viene renderizzato dalla
maggior parte dei browser ed è quindi ancora sconsigliato il suo utilizzo.

<section>
  <p>Caricamento: <progress id="mioLoader" max="100" value="30"><span>30</span>%</prog
ress></p>
</section>




Vediamone adesso un esempio concreto in questa demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_progress/progress.html) (funziona nelle più
recenti versioni di Opera e Google Chrome).

Gli attributi value e max, se presenti, devono essere valori validi (http://www.whatwg.org/specs/web-
apps/current-work/multipage/common-microsyntaxes.html#valid-floating-point-number).
L'attributovalue, se presente, deve avere un valore uguale o maggiore di zero e minore o uguale al
valore dell'attributo max, se presente, o 1.0, in caso contrario. L'attributo max, se presente, deve
avere un valore maggiore di zero.

Ovviamente, l'elemento <progress> deve essere utilizzato solamente per indicare lo stato in fase di
progressione di un compito; per indicare quantitativamente la misura di un'oggetto o di uno stato non
in progressione bisogna utilizzare l'elemento <meter>
(http://xhtml.html.it/guide_preview/lezione/4976/meter/).

Data la complessità dell'argomento e la costante variazione delle specifiche a riguardo consigliamo di
consultare il sito del WHATWG (http://www.whatwg.org/specs/web-apps/current-
work/multipage/the-button-element.html#the-progress-element) per un maggiore approfondimento.

Nella prossima lezione descriveremo brevemente gli altri tag semantici introdotti nella specifica
HTML5.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<progress>                             No    No    No    8.0+ 11.0+




                                              Altri tag


Abbiamo scelto di riassumere brevemente in un'unica lezione diversi tag in quanto il costante
aggiornamento delle specifiche non ci consente di poterli descrivere dettagliatamente, almeno sino a
quando queste ultime non saranno rilasciate ufficialmente; inoltre, ne sconsigliamo l'utilizzo
almeno sino a quando i browser non inizieranno a supportarli in maniera standard.
I tag <figure> e <figcaption>
Nell'elemento <figure> possiamo racchiudere dei contenuti, opzionalmente con una didascalia
(<figcaption>), che rappresentano delle singole unità indipendenti rispetto al contenuto principale;
ad esempio possiamo utilizzare l'elemento <figure> per annotare le illustrazioni, schemi, foto,
elenchi di codice, etc... ovvero tutto ciò che fa parte del contenuto principale ma che potrebbe essere
anche allontanato dallo stesso senza intaccarne il senso.

L'elemento <figcaption> quindi rappresenta una didascalia o una leggenda per l'elemento <figure>
padre.

Esempio:

<figure>
<img src="benevenuti.jpg" alt="">
  <figcaption>
    Foto di benvenuto
    <small>© Diritti riservati</small>
  </figcaption>
</figure>




È importante notare che l'attributo alt è vuoto poiché l'immagine è descritta nel tag <figcaption>
ed è stato usato il tag <small> per il copyright.


Il tag <embed>
L'elemento <embed> è già utilizzato da anni per inserire nel codice HTML contenuti interattivi o
multimediali (tipicamente Flash, Quicktime, etc.). Questo elemento, però, era stato deprecato nelle
specifiche HTML 4 in favore del tag <object>. Ora è stato reintrodotto perché, nonostante la potenza
delle nuove API HTML5, si pensa che al momento non tutto ciò che si riesce ad ottenere con plug-in di
terze parti possa essere realizzato in HTML5. Inoltre, si è cercato di mantenere la retrocompatibilità
con applicazioni basate sull'utilizzo di questo elemento.


Il tag <ruby>
Il tag <ruby> è usato per specificare le annotazioni Ruby, che vengono utilizzate nella tipografia
orientale in combinazione con il testo principale.


Il tag <wbr>
Il tag <wbr> definisce dove, in una parola, sarebbe opportuno aggiungere un a capo. Infatti, quando
una parola è lunga, utilizzando l'elemento <wbr> il browser comprenderà dove eventualmente sarà
possibile inserire un a capo.


I tag <command> e <menu>
Entrambi sono elementi molto interessanti: permettono di definire barre degli strumenti o menu di
scelta rapida per le nostre applicazioni, con le icone e i relativi comandi che possono essere eseguiti
da script.

Il tag <command> rappresenta un'azione che l'utente può richiamare in qualche modo. Esso è
visibile solo se inserito all'interno di un elemento <menu>. In caso contrario, non verrà visualizzato,
ma può essere utilizzato per specificare un tasto di scelta rapida.

Al momento nessun browser supporta questi tag.


I tag <details> e <summary>
I tag <details> e <summary> rappresentano un widget informativo da cui l'utente può ottenere
informazioni supplementari o controlli aggiuntivi. Nel tag <summary>,che è contenuto all interno del
tag<details>, deve essere inserita una sintesi del contenuto del widget o anche una legenda. I
contenuti dell'elemento <details> possono essere mostrati o meno dal browser grazie all'attributo
open, di tipo booleano. Anche questi tag non sono supportati ancora da nessun browser.


Il tag <keygen>
L'elemento <keygen> rappresenta un generatore di chiavi numeriche all'interno di un form. Quando
si effettua l'invio di un form contenente il tag <keygen>, la chiave privata viene memorizzata nel
keystore locale e la chiave pubblica viene confezionato e inviata al server.

L'elemento è già supportato da diversi browser ma manca il suo supporto in IE.


Il tag <output>
L'elemento <output> ci restituisce il risultato di un calcolo.


Tabella del supporto sui browser

Nuovi tag semantici e strutturali

<figure>                               9.0+ 4.0+ Nightly build Nightly build 11.0+
<figcaption>                           9.0+ 4.0+ Nightly build Nightly build 11.0+
<ruby>                                 5.5+ No      5.0+          5.0+          No
<wbr>                                  No     No    No            No            No
<command>                              No     No    No            No            No
<menu>                                 No     No    No            No            No
<details>                              No     No    No            No            No
<summary>                              No     No    No            No            No
<keygen>                               No     1.0+ 2.0+           2.0+          7.0+
<output>                               No     4.0+ Nightly build Nightly build 9.0+




                        I form in HTML5: una panoramica


Quando Javascript fu introdotto nelle pagine web, fu subito implementato per assolvere a due
compiti: il rollover delle immagini e la validazione dei form.

Mentre il primo era un mero problema visuale (e in parte anche di usabilità), il secondo utlizzo
comune di Javascript era necessario perché permetteva di eliminare (o per meglio dire arginare)
l'invio di form mal compilati o con errori, evitando all'utente l'attesa non necessaria tra il l'invio,
l'eventuale fallita validazione dei dati inviati e il conseguente reload della pagina.

Ovviamente nessun programmatore esperto si fida di quello che "arriva" dai form. Si rende
necessario, quindi, il controllo lato server dei dati inviati. Nonostante questo, l'implementazione di
controlli client side è una prassi comune.

Con HTML5 avviene per la validazione e in generale per l'interazione tramite i moduli, quello che è
accaduto con elementi come video e audio. La specifica introduce funzionalità e tecniche che
permettono allo sviluppatore da affidarsi unicamente al linguaggio di markup, senza dove ricorrere a
Javascript o a plugin esterni.

Quello che andremo a descrivere nelle prossime lezioni era originariamente parte della specifica del
WHATWG chiamata Web Form 2.0 (http://www.whatwg.org/specs/web-forms/current-work/),
aggiornamento della specifica del W3C denominata Web Form 1.0
(http://www.w3.org/TR/html4/interact/forms.html).

Attualmente Web Form 2.0 è un progetto chiuso in quanto ora si lavora sulla parte dedicata ai form
(http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html) della specifica HTML5.

Lavorare con i form è stato finora molto semplice: si inizia tutto con il tag <form>, si prosegue con gli
<input type="text">, a volte con gli <input type="password"> e magari con una <textarea> o
una<select> o <input type="radio">, e si finisce sempre con <input type="submit">.

Questi tipi di input, però, non si adattano alla varietà di dati possibilmente richiesti. Spesso ci
troviamo a richiedere indirizzi e-mail, numeri di telefono, date e molti altri tipi di dati ancora.

Non solo, ci sono anche le validazioni di consistenza derivate da questi dati, per esempio il fatto che
l'indirizzo e-mail deve contenere la @ o che le date devono essere in un formato preciso, oltre a
validazioni dipresenza e obbligatorietà. È soprattutto a questo livello, come si accennava, che
entravano in gioco Javascript e le validazioni client side.

Tutto questo con HTML5 finisce grazie all'inserimento nella specifica di nuovi type come email,
number, range, search e di diversi nuovi attributi come placeholder, autofocus, require, min, max,
etc.

Questi nuovi tipi di input e attributi sono molto apprezzati dalla comunità dei programmatori perch´
oltre a fornire un aumento dell'accessibilità dei form (anche in ambito mobile) sono pronti per l'uso.

Non tutti funzionano perfettamente su tutti i browser, ma senza nessun hack, senza Javascript che ne
controlla il funzionamento, degradano bene, anche in preistorici browser.

Possiamo allora iniziare.



    Nuovi attributi per i form: autofocus, placeholder e form
I tipi di attributi
Prima di iniziare cominciamo con il dire che esistono diversi tipi di attributi per i tag HTML. Nel
particolare:

       Attributi booleani: questi tipi di attributi hanno due stati, lo stato vero e lo stato falso. Il
       primo è rappresentato dalla presenza dell'attributo mentre il secondo, lo stato falso, è
       rappresentato dalla sua assenza. Il valore dell'attributo, quindi, non è necessario (es.
       disabled) anche se sono accettati comunque alcuni valori. Nello specifico possiamo
       valorizzarlo con una stringa vuota (es.disabled="") oppure con il nome dell'attributo senza
spazi iniziali o finali (es. disabled=disabled o disabled="disabled").
       Attributi enumerati: questi tipi di attributi possono prendere come valore un insieme finito
       di parole chiavi (es. l'attributo type per il tag ul può assumere i valori disc, square e
       circle). A volte le parole chiave possono avere sinonimi. Una parola chiave potrebbe essere
       anche "", cioè il valore nullo.
       Attributi generici: questo tipo di attributo, invece, può prendere qualsiasi valore. Esempi di
       questi attributi sono class, id e placeholder.

Dopo questa premessa vediamo subito all'opera i primi tre dei nuovi attributi per i form definiti in
HTML5: autofocus, placeholder e form.


autofocus
L'attributo autofocus è un attributo booleano e serve a impostare il focus su uno specifico
elemento del form appena la pagina è caricata. Un esempio canonico è quello della home page di
Google: appena viene caricata il focus è automaticamente impostato sul campo per la ricerca.

Ovviamente solo un elemento per pagina può avere l'attributo autofocus.

Questo attributo deve essere usato con parsimonia in quanto alcuni utenti, come quelli che usano la
barra spaziatrice per scorrere la pagina, potrebbero trovare questa costrizione fastidiosa, preferendo
un atteggiamento più soft.

È per questo motivo che autofocus dovrebbe essere usato solo nelle pagine che contengono
solamente (o principalmente) form (come per esempio pagine di login o di ricerca).

Esempi d'uso dell'attributo autofocus

Ecco comunque un esempio pratico:

<form action="/" method="get">
  <input type="text" name="myname" id="myid" autofocus>
  <input type="submit" value="Invia">
</form>




Alternative per l'implementazione dell'attributo autofocus

Per far sì che l'attributo autofocus funzioni su tutti i browser è sufficiente modificare il codice in
questo modo:

<form action="/" method="get">
  <input type="text" name="myname" id="myid" autofocus >
  <script>
    if (!("autofocus" in document.createElement("input")))
    {
      document.getElementById("myid").focus();
    }
  </script>
  <input type="submit" value="Invia" >
</form>
placeholder
Il valore dell'attributo placeholder è visualizzato all'interno di un input, o di una textarea, fin
quando il campo è vuoto e non guadagna il focus (tramite il click o spostandosi su di esso il tasto
Tab).

Semanticamente l'attributo placeholder dovrebbe essere valorizzato con valori accettabili dal form,
dovrebbe, in altre parole, contenere un esempio di ciò che l'utente andrà a scrivere nel campo.

Spesso il placeholder viene utilizzato erroneamente al posto della label descrivendo quindi cosa
dovrebbero inserire.

Esempi d'uso dell'attributo placeholder

All'interno del nostro progetto guida si fa molto uso dell'attributo placeholder. Per esempio:

<form name="ricerca" method="post" action="/search">
  <label> Parola chiave:
    <input type="search" autocomplete="on" placeholder="article, section, ..." name="k
eyword" required maxlength="50">
  </label>
  <input type="submit" value="ricerca">
</form>




Creando un risultato come nella seguente immagine:

Figura 17 - Resa visiva di un input con l'attributo placeholder




Alternative per l'implementazione dell'attributo placeholder

Non sarà semplice scrivere una funzione che faccia funzionare il placeholder su tutti i browser e per
tutti gli input, ma data la natura dell'attributo non dovrebbe essere nemmeno necessario.

Ad ogni modo, se volessimo farlo funzionare su tutti i browser, avremmo diverse soluzioni, alcune più
semplici altre più complesse.

Proponiamo qui l'alternativa proposta da Andrew January sul suo blog
(http://www.morethannothing.co.uk/2010/01/placeholder-text-in-html5-a-js-fallback/).

Nello script placeholder.js
(http://www.html.it/guide/esempi/html5/esempi/lezione_placeholder/placeholder.js) abbiamo
modificato i commenti mettendoli in italiano di modo che sia di più facile comprensione. Per farlo
funzionare si ricordi di includere la libreria jQuery nella pagina.


form
Un nuovo attributo che si può inserire in tutti gli input è appunto form, anche se sfortunatamente
non è molto supportato e non esiste una vera e propria alternativa.

Questo nuovo attributo serve per specificare a quale form, o a quali form, l'input fa riferimento.
Richiede come valore l'id del form a cui vogliamo che faccia riferimento, o nel caso di più form, gli
idseparati da uno spazio, " ".

Esempi d'uso dell'attributo form

Ecco comunque un esempio pratico:

<form action="/" method="get" id="myFormId">
        <input type="text" name="myname">
        <input type="submit" value="Invia">
</form>
<input type="text" name="mysurname" form="myFormId">



Nonostante l'input con id mysurname sia fuori dal form, inviando il form inviamo anche il suo valore
grazie al suo attributo form.

L'uso di questo attributo è sconsigliato a meno di non conoscere con certezza il browser dell'utente
che utilizzarà la nostra applicazione.

Alternative per l'implementazione dell'attributo form

Come detto nella descrizione dell'attributo, non c'è una vera e propria alternativa. Siamo di fronte a
due soluzioni:

       La prima è quella di agire sul DOM e spostare gli elementi interessati all'interno del form.
       La seconda è quella di assegnare una funzione al submit del form che cerchi i valori degli input
       interessati e per ognuno di questi inserire nel DOM degli <input type="hidden"> e quindi
       procedere con l'invio.


Tabella del supporto sui browser

Form: nuovi attributi

autofocus                No    4.0+ 4.0+          2.0+          9.0+
placeholder              No    4.0+ 4.0+          2.0+          11.0+
form                     No    4.0+ Nightly build Nightly build 9.0+




                Nuovi attributi dei form per la validazione


La validazione dei form è forse l'argomento relativo ai form più importante. Vediamo subito in
dettaglio che cosa ci propone la nuova specifica HTML5.


required
required è un attributo booleano e serve a rendere obbligatoria la compilazione dell'elemento
a cui è applicato. La condizione viene valutata al submit del form.

Ecco un esempio di utilizzo:
<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Messaggio:
    <textarea name="messaggio" placeholder="Scrivi qui il tuo messaggio (max 300 carat
teri)" maxlength="300" required></textarea>
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




autocomplete
Anche se questo attributo non è esattamente un attributo per le validazioni, abbiamo deciso di
inserirlo in questa lezione in quanto previene un comportamento dei browser non sempre voluto:
spesso i browser riempiono i campi da inserire in maniera automatica.

Questo comportamento è nella maggior parte dei casi un comportamento comodo, però in alcuni casi
è fastidioso. Si pensi per esempio ai campi password o ai campi del codice della banca: probabilmente
non vogliamo che il browser li completi in automatico.

Ecco che arriva in nostro soccorso l'attributo autocomplete che è un attributo enumerato. In
particolare i valori che accetta sono:

      on: indica che il valore non è particolarmente sensibile e che il browser può compilarlo in
      maniera automatica;
      off: indica che il valore è particolarmente sensibile o con un tempo di scadenza (il codice di
      attivazione di un servizio, per esempio) e che quindi l'utente deve inserirlo manualmente ogni
      volta che lo compila;
      nessun valore: indica in questo caso di usare il valore di default scelto dal browser
      (normalmente on).

Ecco un esempio di utilizzo:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Nick:
    <input type="text" name="nickname" autocomplete="on"
        required pattern="[a-z]{1}[a-z_]{2,19}"
        title="Un nickname è composto da lettere minuscole e '_'; Sono consentiti da 3
 a 20 caratteri."
        placeholder="your_nickname">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




multiple
In molti casi abbiamo bisogno che l'utente possa inserire più valori per lo stesso input (per
esempio se gli stiamo chiedendo gli indirizzi e-mail di amici a cui inviare un invito).

Ecco che arriva in nostro soccorso l'attributo multiple che è un attributo booleano.

Un esempio di utilizzo:

<form>
  <label>eMail a cui inviare l'invito:
    <input type="email" multiple name="friendEmail"
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>




pattern
In molti casi abbiamo bisogno di validare un determinato input verificando che il valore inserito
sottostia a determinate regole di creazione (per esempio potremmo volere che il campo password non
contenga spazi).

Possiamo contare in questi casi sull'attributo pattern.

Il valore di pattern, se specificato, deve essere una espressione regolare valida (http://www.ecma-
international.org/publications/standards/Ecma-262.htm).

Se viene indicato l'attributo pattern bisognerebbe indicare anche il title per dare una descrizione
del formato richiesto, altrimenti il messaggio di errore sarà generico e probabilmente di poco aiuto.

Ecco un esempio di utilizzo:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Nick:
    <input type="text" name="nickname" autocomplete="on"
        required pattern="[a-z]{1}[a-z_]{2,19}"
        title="Un nickname è composto da lettere minuscole e '_'; Sono consentiti da 3
 a 20 caratteri."
        placeholder="your_nickname">
  </label>
  [...]
  <input type="reset"      value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




In questo esempio si sta richiedendo che lo username sia una parola in minuscolo composta solo da
lettere e da "_", di una lunghezza minima di 3 caratteri e massima di 20 e che non cominci con "_"


min e max
I valori min e max descrivono rispettivamente il valore minimo e massimo consentito.

Il valore di max deve essere maggiore del valore di min se indicato.

Questi attributi si applicano sia alle date (come detetime
(http://xhtml.html.it/guide_preview/lezione/4986/nuovi-tipi-di-input-per-la-gestione-delle-date/),
date(http://xhtml.html.it/guide_preview/lezione/4986/nuovi-tipi-di-input-per-la-gestione-delle-
date/), month (http://xhtml.html.it/guide_preview/lezione/4986/nuovi-tipi-di-input-per-la-gestione-
delle-date/)) sia ai numeri (number (http://xhtml.html.it/guide_preview/lezione/4987/input-type-
number/) e range (http://xhtml.html.it/guide_preview/lezione/4988/input-type-range/)). Per
maggiore dettagli rimandiamo alle lezioni che trattano in maniera specifica questi nuovi tipi di input.

Ecco un esempio di utilizzo:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Età:
    <input type="number" name="age" min="13" max="130" step="1">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




In questo caso stiamo chiedendo un'età compresa tra i 13 e i 130 anni (estremi compresi).


step
Il valore step definisce la distanza che intercorre tra un valore e il successivo. Definisce, in altre
parole, la granularità dei valori permessi.

Il valore di step deve essere un valore positivo non nullo.

Questo attributo si applica sia alle date (come detetime, date, month) sia ai numeri (number e
range).

Ecco un esempio di utilizzo:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Età:
    <input type="number" name="age" min="13" max="130" step="1">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




In questo caso stiamo chiedendo solo valori interi.


novalidate
Questo attributo si applica al tag form e permette di saltare tutte le validazioni dei tag che da esso
discendono.

novalidate è un attributo booleano.

Ecco un esempio di utilizzo:

<form novalidate>
  <label>Età:
    <input type="email" name="myEmail" required>
  </label>
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>



In questo caso non verrà controllato che il campo email sia in un formato valido né che sia presente.


Tabella del supporto sui browser

Form: nuovi attributi

autocomplete             No    No    No            No           10.6+
min, max                 No    No    Nightly build Nightly build 9.0+
multiple                 No    3.6+ 4.0+           2.0+         11.0+
pattern                  No    4.0+ 4.0+           2.0+         9.0+
required                 No    4.0+ 4.0+           2.0+         9.0+
step                     No    No    4.0+          2.0+         9.0+




                                          Input type: tel


È possibile utilizzare l'elemento input con type=tel per creare un campo adatto all'inserimento
di numeri di telefono.

A differenza degli input di tipo email (http://xhtml.html.it/guide_preview/lezione/4985/input-type-
email/) e url (http://xhtml.html.it/guide_preview/lezione/4984/input-type-url/), questo tipo non
impone un particolare formato. Ciò è intenzionale. In pratica, i campi di numero di telefono sono
campi liberi, perché, a livello intenzionale, i numeri possono essere scritti in diversi modi. È comunque
possibile usare l'attributo pattern (http://xhtml.html.it/guide_preview/lezione/4981/nuovi-attributi-
dei-form-per-la-validazione/) per forzare un determinato formato.


I dispositivi mobili e il type tel
I dispositivi mobili possono presentare tastiere personalizzate per facilitare l'inserimento come
mostrato nelle immagini che seguono (la prima è relativa a iPhone/iOS, mentre la seconda a un
sistema Android).

Figura 18 - Tastiera iPhone
Figura 19 - Tastiera Android




Esempi d'uso
L'esempio è molto semplice, eccolo:

<form>
  <label>Inserisci il tuo numero di telefono:
    <input type="tel" name="myTelephone">
  </label>
  <input type="submit" value="Invia" >
</form>




Questo codice produce visivamente un normale <input type="text">.


Tabella del supporto sui browser

Form: nuovi tipi di input
tel                           No    4.0+ 4.0+ 2.0+ 11.0+




                                      Input type: search


È possibile utilizzare l'elemento input con type=search per creare un campo di ricerca. Questo
campo è, ovviamente, un campo libero nel senso che non impone nessun pattern.


Safari su Mac OS X e il type search
Nella maggior parte dei browser non c'è alcuna differenza tra un campo di tipo text e un campo
search, ma in Safari su Mac OS X abbiamo un comportamento particolare:

      1. Visivamente l'input ha i bordi arrotondati.
      2. Se scriviamo qualcosa nel campo compare una piccola X sulla destra che, se cliccata, svuota il
         campo.

Figura 1




Esempi d'uso
Ecco un semplice esempio per implementare questo tipo di input:

<form name="ricerca" method="post" action="/search">
  <label> Parola chiave:
    <input type="search" autocomplete="on" placeholder="article, section, ..." name="k
eyword" required maxlength="50">
  </label>
  <input type="submit" value="Ricerca">
</form>




Come detto precedentemente, questo codice nella maggior parte dei browser produce visivamente un
normale <input type="text">.


Tabella del supporto sui browser

Form: nuovi tipi di input


search                        No    4.0+ 2.0+ 2.0+ 11.0+




                                         Input type: url


Si usa l'elemento input con type=url per creare un campo destinato all'inserimento di un
indirizzo web.

Il tipo url, se specificato, dovrebbe rappresentare l'inserimento di un URL assoluto, ovvero nel
formato http://www.sito.com/etc.... Nel caso in cui il valore inserito non sia valido, viene
sollevata, nei browser che supportano il tipo url, un'eccezione che non riconosce il pattern.


I dispositivi mobili e il type url
I dispositivi mobili possono presentare tastiere personalizzate per facilitare l'inserimento. iPhone
modifica la sua tastiera eliminando la barra spaziatrice e mettendo il punto, la slash e l'estensione
".com" come visualizzato nella figura sottostante. Android, invece, visualizza attualmente la tastiera
standard.

Figura 21 - Tastiera iPhone con un campo di tipo url




Esempi d'uso
L'esempio è molto semplice, eccolo:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label> Www:
    <input type="url" name="url" autocomplete="on" placeholder="http://mywebsite.com">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




Produce visivamente un normale <input type="text">.


Tabella del supporto sui browser

Form: nuovi tipi di input

url                         No    4.0+ 4.0+ 2.0+ 9.0+
Input type: email


L'elemento input con type=email viene usato per creare un campo per inserire un indirizzo e-
mail.

L'input con tipo email, se specificato, dovrebbe rappresentare l'inserimento di indirizzi e-mail. Una
fondamentale condizione di validità, dunque, sarà rappresentata dalla presenza del simbolo @. Nel
caso in cui il valore inserito non sia valido viene sollevata un'eccezione.


I dispositivi mobili e il type email
I dispositivi mobili possono presentare, anche in questo caso, tastiere ad hoc. iPhone modifica la sua
tastiera mostrando la chiocciola e il punto come visualizzato in figura 22. Android attualmente
visualizza la tastiera standard.

Figura 22 - Tastiera iPhone con un campo di tipo email




Esempi d'uso
Anche per questo tipo di input presentiamo un piccolo snippet di codice:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label> Email:
    <input type="email" name="email" autocomplete="on" placeholder="email@domain.ext">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




Il codice visto produce visivamente un normale <input type="text">.
Tabella del supporto sui browser

Form: nuovi tipi di input

email                         No    4.0+ 4.0+ 2.0+ 9.0+




                Nuovi tipi di input per la gestione delle date


Tutti i programmatori prima o poi hanno avuto a che fare con la gestione delle date, con tutti i
problemi che ne conseguono. HTML5 mette a disposizione nuovi tipi di input concepiti per facilitare il
compito dei programmatori nell'inserimento di date da parte degli utenti.

Dal momento che ci sono diversi tipi di date di cui potremmo aver bisogno, ecco che sono stati
implementati nella specifica diversi tipologie. In particolare:

        datetime: gestisce sia la data che l'ora (con fuso orario);
        datetime-local: gestisce sia la data che l'ora (senza fuso orario);
        date: gestisce le date;
        month: gestisce i mesi;
        week: gestisce le settimane;
        time: gestisce l'ora.

Per tutti questi input abbiamo degli attributi specifici che sono:

        min: che rappresenta il minimo valore accettato;
        max: che rappresenta il massimo valore accettato;
        step: che rappresenta la granulosità dei valori accettati.

Vediamoli nel dettaglio.


datetime
Come detto, serve per permettere l'inserimento di date e ore in un solo colpo. Visivamente nei
browser che lo supportano (pochi per ora) abbiamo la generazione di un datepicker in cui abbiamo la
possibilità di selezionare un giorno e con l'opzione di mettere anche l'ora, come nell'immagine qui
sotto. Ecco cosa avviene quando clicchiamo su quella che sembra essere una select (lo screenshot è
di Opera 11):

Figura 23 - Input datetime su Opera
Nei sistemi che non supportato il tipo datetime, viene generato un normale input di testo.

Vediamo un esempio di codice

<form>
  <label>Quando sei disponibile per un incontro?
    <input type="datetime" name="mydatetime">
  </label>
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>




Gli attributi specifici

      min: il valore di questo attributo deve essere una data e ora con il fuso orario valida
      (http://developers.whatwg.org/common-microsyntaxes.html#valid-global-date-and-time-
      string).
      max: il valore di questo attributo deve essere una data e ora con il fuso orario valida
      (http://developers.whatwg.org/common-microsyntaxes.html#valid-global-date-and-time-
      string) e deve essere maggiore del valore dell'attributo min se specificato.
      step: il valore di questo attributo deve essere un intero e rappresenta i secondi. Il valore di
      default è di 60 secondi.


datetime-local
È del tutto simile a datetime, con l'unica differenza che non vengono passate informazioni sul fuso
orario.

Ecco come appare su Opera 11:

Figura 24 - Input datetime-local su Opera
Nei sistemi che non supportato il datetime-local viene generato un normale input type="text".

Esempio:

<form>
  <label>Quando sei disponibile per un incontro?
    <input type="datetime-local" name="mydatetime">
  </label>
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>



Gli attributi specifici

      min: il valore di questo attributo deve essere una data e ora valida
      (http://developers.whatwg.org/common-microsyntaxes.html#valid-local-date-and-time-
      string).
      max: il valore di questo attributo deve essere una data e ora valida
      (http://developers.whatwg.org/common-microsyntaxes.html#valid-local-date-and-time-string)
      e deve essere maggiore del valore dell'attributo min se specificato.
      step: il valore di questo attributo deve essere espresso in secondi. Il valore di default è di 60
      secondi.


date
Serve per inserire una data. Nei browser che lo supportano si ottiene un datepicker in cui abbiamo la
possibilità di selezionare un giorno:

Figura 25 - Input date su Opera
Nei sistemi che non supportato il tipo date viene generato un normale campo di testo.

Esempio:

<form>
  <label>Quando sei disponibile per un incontro?
    <input type="date" name="mydatetime">
  </label>
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>




Gli attributi specifici

      min: il valore di questo attributo deve essere una data valida
      (http://developers.whatwg.org/common-microsyntaxes.html#valid-date-string).
      max: il valore di questo attributo deve essere una data valida
      (http://developers.whatwg.org/common-microsyntaxes.html#valid-date-string) e deve essere
      maggiore del valore dell'attributo min se specificato.
      step: il valore di questo attributo deve essere espresso in giorni. Il valore di default è di 1
      giorno.


month
Serve per permettere di selezionare un mese dell'anno. In figura 4 il datepicker per selezionare un
mese generato su Opera 11:

Figura 26 - Input month su Opera




Nei sistemi che non supportato month viene generato un normale campo di testo.

Esempio

<form>
  <label>Quando sei disponibile per un incontro?
    <input type="month" name="mydatetime">
  </label>
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>
Gli attributi specifici

       min: il valore di questo attributo deve essere un mese valido
       (http://developers.whatwg.org/common-microsyntaxes.html#valid-month-string).
       max: il valore di questo attributo deve essere una mese valido
       (http://developers.whatwg.org/common-microsyntaxes.html#valid-month-string) e deve
       essere maggiore del valore dell'attributo min se specificato.
       step: il valore di questo attributo deve essere espresso in mesi. Il valore di default è di 1
       mese.


week
Viene usato per la selezione di una determinata settimana dell'anno (composta da anno - numero di
settimana). In figura 5 il widget prodotto dall'inserimento di questo tipo di input su Opera 11:

Figura 27 - Input week su Opera




Nei sistemi che non supportato week viene creato un normale campo di testo.

Esempio:

<form>
  <label>Quando sei disponibile per un incontro?
    <input type="week" name="mydatetime">
  </label>
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia">
</form>


Gli attributi specifici

       min: il valore di questo attributo deve essere una settimana valida
       (http://developers.whatwg.org/common-microsyntaxes.html#valid-week-string).
       max: il valore di questo attributo deve essere una settimana valida
       (http://developers.whatwg.org/common-microsyntaxes.html#valid-week-string) e deve essere
       maggiore del valore dell'attributo minse specificato.
       step: il valore di questo attributo deve essere espressa in settimane. Il valore di default è di 1
       settimana.


time
Serve per selezionare e inserire una determinata ora del giorno. Ancora una schermata da Opera 11:
Figura 28 - Input time su Opera




Nei sistemi che non supportato il time genera un normale input type="text".

Vediamo un esempio di codice

<form>
  <label>Quando sei disponibile per un incontro?
    <input type="time" name="mydatetime">
  </label>
  <input type="reset" value="Resetta la form">
  <input type="submit" value="Invia">
</form>




Gli attributi specifici

       min: il valore di questo attributo deve essere una ora valida
       (http://developers.whatwg.org/common-microsyntaxes.html#valid-time-string).
       max: il valore di questo attributo deve essere una ora valida
       (http://developers.whatwg.org/common-microsyntaxes.html#valid-time-string) e deve essere
       maggiore del valore dell'attributo min se specificato.
       step: il valore di questo attributo deve essere espresso in secondi. Il valore di default è di 60
       secondi.


Tabella del supporto sui browser

Form: nuovi tipi di input

datetime                     No    No    Parziale Parziale 9.0+
date                         No    No    Parziale Parziale 9.0+
month                        No    No    Parziale Parziale 9.0+
week                         No    No    Parziale Parziale 9.0+
time                         No    No    Parziale Parziale 9.0+
datetime-local               No    No    Parziale Parziale 9.0+




                                   Input type: number


È possibile utilizzare l'elemento input con type=number per creare un campo destinato
all'inserimento di un numero.


I dispositivi mobili e il type number
I dispositivi mobili possono presentare tastiere personalizzate per facilitare l'inserimento come
mostrato nelle immagini che seguono (fanno riferimento, rispettivamente, a iPhone/iOS e Android).
Figura 29 - Tastiera iPhone con un input di tipo number




Figura 30 - Tastiera Android con un input di tipo number




Attributi specifici per il type number
HTML5 mette a disposizione un set di attributi specifici per i campi di tipo number. Servono a
specificare delle limitazioni per il valore di questo attributo. Questi attributi sono min, max e step.

Attributo min

Specifica il minimo valore permesso. La sintassi è semplice: min="1" permette solo l'inserimento di
numeri positivi. I valori permessi sono, ovviamente, numeri.

Attributo max

Specifica il massimo valore permesso. max="10" permette solo l'inserimento di numeri inferiori o
uguali a 10. Il valore di questo attributo deve essere maggiore del valore dell'attributo min (se
specificato).

Attributo step
L'attributo step indica la granulosità che deve avere il valore, limitando i valori permessi. Il valore di
step se specificato deve essere un numero (anche non intero) maggiore di zero oppure la stringa
"any" (che equivale a non inserire l'attributo).

La sintassi è anche in questo caso molto semplice: step=3 influenza i valori permettendo valori come
-3, 0, 3, 6 ma non -1 o 2.


Esempi d'uso
Un esempio potrebbe avere questa forma:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Età:
    <input type="number" name="age" min="13" max="130" step="1">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




In questo caso stiamo chiedendo di inserire un'età compresa tra i 13 e i 130 anni (estremi compresi)
e i valori accettati sono interi.

Nella maggior parte dei browser si produce attualmente un normale <input type="text">, ma nei
browser che supportano number abbiamo:

Figura 31 - Un input di tipo number




Tabella del supporto sui browser

Form: nuovi tipi di input

number                       No    No     4.0+ 2.0+ 9.0+




                                      Input type: range


Molto simile semanticamente all'input type=number
(http://xhtml.html.it/guide_preview/lezione/4987/input-type-number/), questo nuovo tipo di input
permette agli utenti di inserire un numero tramite uno slider.


Attributi specifici
HTML5 mette a disposizione un set di attributi specifici per il tipo range (che sono gli stessi del
type=number): servono a specificare delle limitazioni per il valore di questo attributo. Questi attributi
sono min,max e step.

        min: specifica il minimo valore permesso. Esempio: min="1", che permette solo di passare
        numeri da 1 in su.
        max
        : specifica il massimo valore permesso. Esempio: max="10", che permette solo di inviare
        numeri inferiori o uguali a 10. Il valore di questo attributo deve essere maggiore del valore
        dell'attributo min (se specificato).
        step: indica la granulosità che deve avere il value limitando i possibili valori passati. Il valore
        di step se specificato deve essere un numero (anche non intero) maggiore di zero oppure la
        stringa "any" (che equivale a non inserire l'attributo). Esempio: step=3, che influenza i valori
        inseriti passando valori come -3, 0, 3, 6.


Esempi d'uso
L'esempio è molto semplice, eccolo:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Voto:
    <input type="range" name="voto" min="0" max="5" step="1">
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




Ecco come appare un input di tipo range sui browser che lo supportano:

Figura 32 - Un input di tipo range




Tabella del supporto sui browser

Form: nuovi tipi di input

range                         No     No    4.0+ 2.0+ 9.0+




                                          Input type: color


L'elemento input con type=color dovrebbe creare un color picker, quel tipo particolare di widget
utile per la selezione di un colore a partire da una palette di colori. Una volta selezionato il
colore, il campo passa alla nostra pagina di ricezione un colore RGB esadecimale composto da 6 cifre.

Questo tipo input ad oggi non è molto supportato. Per la sua implementazione sui nostri siti si può
però ricorrere ad una qualsiasi delle tante soluzioni Javascript disponibili.
Opera e il type color
Nella maggior parte dei browser non c'è alcuna differenza tra un campo di tipo text e un campo
color, ma su Opera abbiamo un comportamento particolare:

   1. Visivamente abbiamo una select particolare con i colori (esempio in figura 33).
   2. Se clicchiamo abbiamo una scelta dei colori base (esempio in figura 34).
   3. Se clicchiamo su "Altro" richiamiamo il color picker di sistema (esempio su Opera su Mac OS X
      in figura 35).

Figura 33 - Input color su Opera: stato iniziale




Figura 34 - Input color su Opera: selezione del colore




Figura 35 - Input color su Opera: color-picker di sistema




Esempi d'uso
Un input di tipo color si crea così:

<form>
  <label>Seleziona il colore di sfondo:
<input type="color" name="mybackground">
  </label>
  <input type="reset" value="Resetta la form">
  <input type="submit" value="Invia">
</form>




Tabella del supporto sui browser

Form: nuovi tipi di input

color                        No    No    Parziale Parziale 11.0+




                                  Cosa sono le datalist?


L'elemento HTML5 <datalist> è utilizzato per fornire una funzione di "completamento automatico"
ad un elemento del form. Permette, in altre parole, di fornire all'utente un elenco di opzioni
predefinite da cui scegliere, offrendo al contempo la possibilità di inserire un valore non presente
inizialmente nella lista.

Mette insieme i vantaggi di una select e di un input di testo. Sfortunatamente questo tag molto
utile è al momento poco supportato dai browser (solo Opera lo supporta).


Datalist su Opera
Nella maggior parte dei browser il codice relativo all'elemento datalist viene completamente ignorato
ma su Opera abbiamo il seguente comportamento:

   1. Visivamente abbiamo solo il nostro input e la datalist non è renderizzata in nessun modo
      (figura 36).
   2. Se clicchiamo sull'input a cui la datalist è collegata, vengono presentate le opzioni della
      datalist (figura 37).
   3. Se clicchiamo su un'opzione, l'input a cui la datalist è collegato assume il valore dell'opzione
      (figura 38).

Figura 36 - Datalist su Opera: stato iniziale




Figura 37 - Datalist su Opera: opzioni della datalist




Figura 38 - Datalist su Opera: valorizzazione dell'opzione
Esempi d'uso
Per utilizzare datalist prima di tutto dobbiamo scrivere un input a cui collegare la nostra datalist.
Per collegare l'input alla datalist basta impostare l'attributo list dell'input con l'id della datalist.

Per ogni suggerimento che vogliamo dare all'utente facciamo discendere da datalist un tag option,
mettendo il suggerimento nell'attributo value. Il value dell'option non deve essere vuoto e l'option
non deve essere settata su disabled per essere visualizzata.

Vediamo il codice:

<form name="commenti" method="post" action="/141/comments">
  [...]
  <label>Stato d'animo:
    <input type="text" name="mood" placeholder="felice, triste, incuriosito, ..." list
="stato-danimo">
    <datalist id="stato-danimo">
      <option value="triste">
      <option value="annoiato">
      <option value="curioso">
      <option value="felice">
      <option value="entusiasta!">
    </datalist>
  </label>
  [...]
  <input type="reset" value="Resetta il form">
  <input type="submit" value="Invia il commento">
</form>




Tabella del supporto sui browser

Form: nuovi tipi di input

datalist                    No    4.0+ No      No    9.0+




                               La potenza dei microdati
Introduzione: semantica e rich snippet
Leggendo questa guida dovrebbe essere chiaro che un punto focale di HTML5 è la semantica.

HTML5 ha introdotto infatti diversi tag semantici (come header, article o nav) che permettono di
strutturare il contenuto secondo una logica, appunto, semantica. Ma questa suddivisione non assolve
a tutte le necessità semantiche di cui il web ha bisogno.

L'obbiettivo è quello di dare la possibilità a programmi come crawler dei motori di ricerca o screen
reader di comprendere il significato del testo. Queste informazioni sono accessibili da questi
programmi e rimangono (attualmente) invisibili per l'utente. Entrano qui in gioco i cosiddetti
microdati.

Ecco come Google li descrive, all'interno della Guida di Strumenti per i Webmaster:

      "La specifica dei microdati HTML5 è un modo per assegnare etichette ai contenuti al fine
      di descrivere un tipo specifico di informazioni (ad esempio recensioni, informazioni su
      persone o eventi). Ogni tipo di informazione descrive uno specifico tipo di elemento,
      come una persona, un evento o una recensione. Ad esempio, un evento ha proprietà
      quali il luogo, l'ora di inizio, il nome e la categoria."

Attualmente, la cosa forse più interessante relativa ai microdati è in effetti uscita fuori dai laboratori
di Google: si tratta dei cosiddetti rich snippet. I rich snippet sono risultati della ricerca di Google in
cui, oltre alle comuni informazioni, compaiono altri dati allegati, come nelle immagini qui sotto:

Figura 39 - Rich snippet relativo a un hotel




Figura 40 - Rich snippet con informazioni personali




I microdata in pratica
Applicare i microdati è semplice: per ogni tag HTML possiamo specificare degli attributi che ci
permettono di definire gli oggetti semantici.

Prima di tutto dobbiamo applicare a un elemento radice (cioè un elemento che contiene tutte le
informazioni che vogliamo specificare) itemscope e itemtype.

itemscope definisce l'elemento a cui è applicato è un contenitore dell'oggetto che andremo a
descrivere.

itemtype definisce il vocabolario che specifica il tipo di oggetto che andremo a descrivere.

Per finire, sugli elementi che discendono dall'elemento radice specifichiamo l'attributo itemprop che
definisce la proprietà che verrà valorizzata con il testo contenuto nel tag.

Ecco un esempio semplice:

<div itemscope itemtype="http://data-vocabulary.org/Person">My name is
  <span itemprop="name">Simone Bonati</span> and my nickname is
  <span itemprop="nickname">svarione</span> on several site (like twitter).
  Here is my twitter account:
  <a href="http://www.example.com" itemprop="url">http://twitter.com/svarione</a> I li
ve in
  Milano, Italy and work as
  <span itemprop="role">web developer</span>.
</div>



Possiamo anche nidificare gli oggetti. Ecco un esempio:

<div itemscope itemtype="http://data-vocabulary.org/Person">My name is
  <span itemprop="name">Simone Bonati</span> and my nickname is
  <span itemprop="nickname">svarione</span> on several site (like twitter).
  Here is my twitter account:
  <a href="http://www.example.com" itemprop="url">http://twitter.com/svarione</a> I li
ve in
  <span itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address">
    <span itemprop="locality">Milano</span>,
    <span itemprop="country-name">Italy</span>
  </span> and work as
  <span itemprop="role">web developer</span>.
</div>



I vocabolari
Per sfruttare al massimo la potenza dei microdati e per ottenere i rich snippet dobbiamo usare i
vocabolari (che specifichiamo, come abbiamo visto, con itemtype) supportati da Google.

I vocabolari descrivono l'insieme di proprietà che possono essere definite per un determinato oggetto.

I vocabolari più popolari supportati da Google sono:

      Breadcrumbs: questo vocabolario serve a rappresentare un insieme di link che può aiutare un
      utente a comprendere e navigare all'interno del sito.
      Businesses and organizations: questo vocabolario definisce una società, un negozio e più in
      generale un luogo.
      People: questo vocabolario definisce una persona.
      Address: questo vocabolario definisce un indirizzo.
      Events: questo vocabolario definisce un evento (con informazioni come il titolo, la data e il
      luogo).
      Product: questo vocabolario definisce un prodotto.
      Offer: questo vocabolario definisce un'offerta.
      Offer-aggregate: questo vocabolario definisce un aggregato di offerte (con prezzo minimo,
      prezzo massimo, etc).
      Recipes: questo vocabolario definisce una ricetta.
      Review: questo vocabolario definisce una singola recensione.
      Review-aggregate: questo vocabolario definisce una recensione aggregata.
      Rating: questo vocabolario definisce una valutazione.


Link utili
Google: informazioni sui microdati (http://www.google.com/support/webmasters/bin/answer.py?
hlrm=en&answer=176035)

Vocabolario Breadcumbs (http://data-vocabulary.org/Breadcrumb)
Vocabolario Businesses and Organizations (http://data-vocabulary.org/Organization)

Vocabolario People (http://data-vocabulary.org/People)

Vocabolario Address (http://data-vocabulary.org/Address)

Vocabolario Events (http://data-vocabulary.org/Event)

Vocabolario Products (http://data-vocabulary.org/Product)

Vocabolario Offer (http://data-vocabulary.org/Offer)

Vocabolario Aggregate (http://data-vocabulary.org/Offer-aggregate)

Vocabolario Recipes (http://data-vocabulary.org/Recipes)

Vocabolario Reviews (http://data-vocabulary.org/Review)

Vocabolario Review-aggregate (http://data-vocabulary.org/Review-aggregate)

Vocabolario Rating (http://data-vocabulary.org/Rating)




                        Nuova linfa alle applicazioni web


L’acronimo alla base di HTML è chiaro, ‘HyperText Markup Language’; ipertesto: un insieme di
documenti messi in relazione tra loro da parole chiave. La tecnologia in questione dovrebbe essere
quindi utile per strutturare collezioni di contenuti legati fra di loro in modo più o meno oggettivo;
Wikipedia ne è il classico e più calzante esempio. Lascia invece un po’ più perplessi realizzare che
l’HTML è sempre più la base di vere e proprie applicazioni web: google docs, Google Maps,
Meebo, solo per citarne alcune, dove la forma ipertestuale è decisamente marginale quando non
completamente assente. Come già abbiamo avuto modo di anticipare, l’HTML5 nasce anche per
meglio indirizzare questo crescente filone di realtà web, mettendo a disposizione dello sviluppatore
una nutrita serie di nuovissime API studiate per dare adito alle più ardite applicazioni all’interno di un
browser. È un po’ come se il prematuro sogno di Marc Andressen
(http://www.forbes.com/forbes/1997/1201/6012308a_print.html) stesse per avverarsi.


La risposta ad un bisogno molto sentito
Lo sforzo profuso prima dal gruppo WHAT e poi dal W3C è stato parzialmente accentuato dalla
necessità di porre un freno al dilagare di tecnologie, parallele all’HTML, sorte per rispondere ai nuovi
bisogni delle applicazioni web. Il motivo principale di questo comportamento è da ricercarsi nella
necessità di mantenere unito il controllo sullo standard e, conseguentemente, di evitare che
l’insediamento in pianta stabile di soluzioni non interne al consorzio possa complicare di fatto la
gestione dell’intero sistema.

Flash ed il cugino Flex sono due ottimi esempi della tendenza intrapresa dal web in tal senso, seguono
Google Gears, Google O3D e un’infinita di altre estensioni, ognuna delle quali cerca di riempire un
vuoto più o meno piccolo percepito dagli utenti della rete.


I punti chiave dell’offerta
Ecco un elenco esaustivo delle API trattate nelle prossime lezioni della guida. E` possibile suddividere
tale insieme sommariamente in due categorie: nella prima, multimedialità, trovano spazio le API
studiate per gestire ed incanalare nuovi strumenti di comunicazione, come video, audio e grafica
bi/tridimensionale; nella seconda, che invece potremmo chiamare arricchimento, si posizionano le
nuove funzionalità studiate per avvicinare l’esperienza di fruizione di una web application a quella di
un normale programma eseguito in una finestra del desktop.

Multimedialità

      Gestione di flussi video (il tag <video> e le relative API);
      Gestione di flussi audio (il tag <audio> e le relative API);
      Gestione di grafica libera bi/tridimensionale (il tag <canvas> e le relative API);
      Grafica vettoriale e notazioni matematiche (i tag <svg>, <math> e le relative API).

Arricchimento

      Applicazioni web offline (file .manifest e API di sincronizzazione);
      Memorizzazione di informazioni sul browser (WebSQL e LocalStorage API);
      Javascript asincrono e parallelo (Web Workers API);
      Comunicazioni bidirezionali tra client e server (Web Socket API);
      Drag and Drop;
      Utilizzo di informazioni georeferenziate (GeoLocation API).

Oltre a questo elenco, meritano di essere citate e brevemente esplorate alcune funzionalità che,
seppur non rivoluzionarie, promettono di semplificare alcuni aspetti dello sviluppo odierno di
applicazioni web.

Manipolare la cronologia: le History API

Tutti conosciamo il meccanismo per navigare all’interno della cronologia del browser:

alert("La cronologia contiene " + history.length + " documenti." );
// torna al documento precedente
history.back();




Meno invece sanno che di questo oggetto history, seppur supportato dalla totalità degli user-agent,
non esiste traccia nelle attuali specifiche HTML. Con l’avvento della versione 5 il W3C ha deciso
diincludere le API per la navigazione della cronologia all’interno del futuro standard; l’occasione
si è rivelata ghiotta anche per un interessante arricchimento delle funzionalità. In particolare è ora
possibile creare nuovi elementi della cronologia associando loro un payoff di dati, come ad esempio
un hash chiave-valore, che poi potrà essere recuperato attraverso l’utilizzo del tasti di navigazione del
browser. Vediamone un esempio:

<!doctype html>
<html>
<head>
  <title>Messaggi dalla storia:</title>
  <script>
    historyInject = function(){
      message = prompt('Che messaggio vuoi salvare in history?');
      history.pushState(message, document.title + " '" + message + "'");
    };
onpopstate = function(event){
       document.getElementById("messagelist").
       insertAdjacentHTML('beforeend',"<li>"+event.state+"</li>");
    }
  </script>
</head>
<body>
    <h1>Lista dei messaggi presenti nella cronologia:</h1>
    Premi il tasto back del browser o <a href="javascript:historyInject();return false
;">
    carica un nuovo messaggio.</a>
    <ul id="messagelist"></ul>
</body>
</html>




La funzione historyInject crea, attraverso il metodo pushState, un nuovo elemento nella
cronologia che contiene il messaggio generato dall’utente. Successivamente, la pressione del tasto
back del browser viene intercettata dall’handler onpopstate che recupera il messaggio e lo aggiunge
all’elenco a fondo pagina. Con questo meccanismo diviene possibile simulare una normale
navigazione con i pulsanti back e forward anche all’interno di applicazioni web che fanno uso intensivo
di Javascript, come ad esempio la maggior parte delle realizzazioni sviluppate con il framework Ext.JS
(http://dev.sencha.com/deploy/dev/examples/).

Associare protocolli e MIME type alla propria web application

Supponiamo di aver sviluppato una web application per inviare e ricevere sms. Le specifiche HTML5
introducono la possibilità di registrare la propria applicazione come gestore preposto ad uno specifico
protocollo; nel nostro caso sarebbe quindi auspicabile che qualunque link nel formato:

<a href="sms://+39396819577">Manda un SMS a Sandro Paganotti</a>




convogli l’utente alla nostra applicazione a prescindere dal sito in cui si trova il link, un po’ come
succede con il client di posta al click di link col protocollo ‘mailto’. Registrare un protocollo è molto
semplice:

<!doctype html>
<html>
<head>
  <title>TuttiSMS: Il servizio per inviare e ricevere SMS</title>
  <script>
  registraProtocollo = function(){
     navigator.registerProtocolHandler(
       'sms', 'http://localhost/invia_sms?dest=%s', 'TuttiSMS');
  };
</script>
</head>
<body onload="registraProtocollo();">
</body>
</html>
Caricando la pagina in un browser che supporta questa funzionalità, noi abbiamo usato Firefox 3.5,
verremo notificati della volontà da parte dell’applicazione web di registrare il protocollo sms e
potremo decidere se consentire o meno l’operazione:

Figura 41 (click per ingrandire) - Associazione del protocollo su Firefox




(http://www.html.it/guide/esempi/html5/imgs/lezione_api/1.jpg)

Una volta accettata questa associazione, ad ogni link nel formato: ‘sms://qualsiasi_cosa’ seguirà
un redirect automatico all’indirizzo specificato come parametro nella funzione
registerProtocolHandler, nel nostro caso: ‘http://localhost/invia_sms?dest=%s’, con la
particella ‘%s’ sostituita con l’intero link in questione. Ecco un esempio di risultato:

http://localhost/invia_sms?dest=sms%3A%2F%2F%2B39396819577




E’ possibile seguire esattamente la stessa procedura anche per registrare uno specifico MIME type
utilizzando la funzione gemella: registerContentHandler.


Un progetto ambizioso
Chiudiamo la lezione gettando le basi per lo sviluppo del progetto guida associato al prossimo gruppo
di lezioni: FiveBoard, una lavagna interattiva basata su canvas. Chiaramente l’obiettivo del progetto
è quello di fornire un pretesto per poter esplorare le novità introdotte dall’HTML5 in un contesto un
po’ più ampio rispetto a quello solitamente offerto. Proprio per questo alcune scelte potranno essere
opinabili in un ottica di efficenza o di strutturazione dell’architettura.

Bene, cominciamo creando una cartella di progetto, ‘fiveboard’, contenente un file ‘index.html’ e una
cartella ‘js’ con un ulteriore file ‘application.js’:

Figura 42 - Vista delle cartelle
Ora impostiamo la pagina html perché richiami il file Javascript:

<!doctype html>
<html lang='it'>
<head>
  <meta charset="utf-8">
  <title>FiveBoard: uno spazio per gli appunti.</title>
  <script src="js/application.js" defer></script>
</head>
</html>




L’attributo defer, poco supportato perché definito in modo poco chiaro nelle specifiche HTML4
(http://www.w3.org/TR/html401/interact/scripts.html#h-18.2.1), assume nella versione 5 un
significato più delineato:

      “(se) [..] l'attributo defer è presente, allora lo script viene eseguito quando la pagina ha
      finito di essere processata.”

In questo nuovo contesto è interessante l’utilizzo di defer per velocizzare il caricamento della pagina
posticipando in seconda battuta il load del file Javascript. Da notare, prima di proseguire, anche
l’aggiunta nelle specifiche dell’attributo async che causa il caricamento dello script in parallelo, o
asincrono, da qui il nome, rispetto a quello della pagina.

Prima di concludere sinceriamoci del funzionamento del nostro impianto aggiungendo al file
Javascript:

alert("pronti per cominciare!");




e caricando il tutto all’interno di un browser:

Figura 43 (click per ingrandire) - Risultato dell'operazione
(http://www.html.it/guide/esempi/html5/imgs/lezione_api/3.jpg)


Codice degli esempi
Prima di proseguire, potete scaricare per una migliore consultazione il pacchetto zip
(http://www.html.it/guide/esempi/html5/esempi/esempi_html5_api.zip) che contiene il codice di tutti
gli esempi che vedremo nelle lezioni che seguono.




                  Applicazioni web offline (file .manifest)


Con le Offline API è possibile specificare in un apposito file, detto manifest, un elenco di asset
(pagine web, filmati Flash, immagini, fogli di stile CSS, Javascript e quant’altro può finire in una
pagina web) dei quali vogliamo che il browser conservi copia locale. Tali oggetti, dopo la prima
sessione di navigazione online, resteranno quindi accessibili anche in assenza di una connessione di
rete. In questo modo è possibile creare applicazioni web perfettamente funzionanti anche
offline. Il primo passo per ottenere questo risultato è aggiungere l’attributo manifest all’elemento
html segnalando così allo user agent l’esistenza di un file preposto alla memorizzazione offline:

<!doctype html>
<html lang='it' manifest='fiveboard.manifest'>
...




A questo punto deve essere creato un file con MIME type ‘text/cache-manifest’, dal nome
specificato nel valore dell’attributo, contenente l’elenco dei documenti per i quali si richiede la
memorizzazione offline. Ad esempio, considerando il nostro progetto guida, il file potrebbe risultare:

CACHE MANIFEST
index.html
js/application.js
Per fare in modo che il file venga servito con il corretto MIME type ci sono molte soluzioni, tutte,
purtroppo, molto legate al tipo di webserver che sostiene la pagina. Nel caso si tratti del popolare
Apache la soluzione più veloce consiste nel creare un file ‘.htaccess’ nella cartella principale del
progetto contenente la seguente istruzione:

AddType text/cache-manifest manifest




Una volta soddisfatte queste condizioni è possibile verificare il buon funzionamento delle API
utilizzando un browser come Chromium e caricando la pagina assicurandosi di avere i Developer Tools
attivati (CTRL + SHIFT + J) e posizionati sulla sezione ‘Console’ (figura 1):

Figura 1 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_offline/1.jpg)

Aggiornando nuovamente il contenuto della pagina riceveremo conferma della corretta
memorizzazione in locale dei documenti specificati nel file manifest, ora disponibili anche per
consultazioni in assenza di rete (figura 2):

Figura 2 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_offline/2.jpg)


Andare in profondità
Il file .manifest di cui abbiamo accennato nella sezione precedente, offre una serie di interessanti
funzionalità aggiuntive; in primo luogo è possibile indicare al browser percorsi di fallback, da
utilizzare nel caso in cui durante la navigazione offline si necessiti di risorse non disponibili:

CACHE MANIFEST
index.html
FALLBACK:
news.html offline.html




In questo esempio, il tentativo di navigazione verso la pagina ‘news.html’ verrà risolto, in caso di
browser in stato offline, servendo la pagina ‘offline.html’. In caso fosse necessario convogliare risorse
multiple si può anche far ricorso a wildcard, ad esempio:

CACHE MANIFEST
index.html
FALLBACK:
news.html offline.html
news/* offline.html




Così facendo anche la navigazione verso una pagina di dettaglio news, come ad esempio ‘news/
10/12/2010/nuovi-progressi-al-cern’ verrà servita in assenza di rete con il contenuto di ‘offline.html’.
Usando la sezione ‘NETWORK’ è invece possibile notificare risorse che devono sempre essere
recuperate online:

CACHE MANIFEST
index.html
NETWORK:
saldo_conto_corrente.php
Le API associate
La gestione della cache introduce tutta una serie di eventi e proprietà che consentono di acquistare
controllo su ogni passo della procedura, vediamoli in questo esempio (offline.html), che stampa a
video un nuovo elemento di un elenco puntato per ogni evento intercettato:

<!doctype html>
<html lang='it' manifest='offline.manifest'>
<head>
  <title>Tutti i passi della cache</title>
  <script>
  // Funzione di servizio:
  say = function(message){
    document.getElementById("cache-steps").
    insertAdjacentHTML('beforeend',"<li>"+message+";</li>");
  }
  clear = function(message){
    document.getElementById("cache-steps").innerHTML = "";
  }

  // L'oggetto che controlla la cache
  var appCache = window.applicationCache;

  // Nuovi eventi disponbili con le Offline API
  appCache.addEventListener('checking', function(ev) {
    say("Controllo, se posso, che il .manifest online non sia cambiato");
  }, false);

  appCache.addEventListener('noupdate', function(ev) {
    say("Il .manifest non è cambiato");
  }, false);


  appCache.addEventListener('downloading', function(ev) {
    say("Inizio a scaricare i file listati dal manifest che non ho già in cache");
  }, false);


  appCache.addEventListener('progress', function(ev) {
    say("Scarico una risorsa");
  }, false);


  appCache.addEventListener('cached', function(ev) {
    say("Tutte le risorse sono state scaricate");
  }, false);


  appCache.addEventListener('updateready', function(ev) {
    say("Ho scaricato una nuova versione della cache e sono pronto "+
    "a sostituirla alla precedente");
  }, false);

  appCache.addEventListener('obsolete', function(ev) {
say("Ho cercato il .manifest online ottenendo un 404 o 410 come risposta " +
    "probabilmente il sito non supporta più funzionalità di " +
    "caching, cancello la cache in locale.");
  }, false);

  appCache.addEventListener('error', function(ev) {
    say("Ops, si è verificato un errore");
  }, false);
  </script>
</head>
<body>
  <button onclick="clear();appCache.update();">Forza il controllo della cache</button>


  <ul id="cache-steps">
  </ul>
</body>
</html>




Proviamo ad eseguire questa pagina in Chromium senza aver preventivamente creato il file
‘offline.manifest’ (figura 3):

Figura 3




Creiamo ora un semplice ‘offline.manifest’ come segue,

CACHE MANIFEST
offline.html


e ricarichiamo la pagina (figura 4):

Figura 4
Ricarichiamo nuovamente la pagina per certificare l’avvenuto cache delle risorse (figura 5):

Figura 5




Bene, da qui si dipanano tutta una serie di comportamenti interessanti come ad esempio: modificare
il file .manifest e forzare il controllo della cache, oppure rimuovere il file .manifest e ricaricare la
pagina. Provando ognuno di essi con lo script che abbiamo appena steso sarà facile identificare i vari
eventi coinvolti. Ecco la demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_offline/offline.html).


Conclusioni
Come avete potuto notare le attività per far in modo che il nostro progetto guida benefici delle Offline
API sono state semplici e di pochissimo impatto sull’architettura della pagina. E` importante però
ricordare che man mano andremo ad aggiungere risorse al progetto (immagini, librerie, etc...) sarà
necessario includerle nel file .manifest pena il malfunzionamento dello stesso in assenza di
connessione.


Tabella del supporto sui browser
API e Web Applications

Applicazioni web offline (.manifest) No    3.5+ 4.0+ 2.0+ 10.6+




                                 Indexed Database API


Queste API nascono per dare la possibilità di creare e manipolare un database di ispirazione NoSQL
memorizzato all'interno del browser dell'utente. Ogni database, identificato da un nome, può
contenere un numero arbitrario di Object Store, letteralmente contenitori di oggetti: ad esempio un
ipotetico database ‘libreria' potrebbe ospitare i seguenti Object Store: Libri, Clienti. Sia ‘Libri' che
‘Clienti' non sono altro che strutture paragonabili ad array associativi ordinati, dove ogni coppia
chiave-valore rappresenta un oggetto, quindi in questo caso o un libro o un cliente.

All'interno di un Object Store è possibile eseguire le normali operazioni di inserimento, modifica,
eliminazione e ricerca; le API sono state pensate per ragionare con record in un formato JSON-like
ma nulla vieta di memorizzare oggetti Javascript di fattura diversa, purché serializzabili.


Come funzionano le API
Utilizziamo Chromium per un tour operativo di questo interessante set di API; iniziamo preparando un
documento HTML5 e creando il database:

<!doctype html>
<html>
<head>
  <title> WebLibreria: gestionale per librerie </title>
  <script>
  setup = function(){
   if ('webkitIndexedDB' in window){
    indexedDB = webkitIndexedDB;
    IDBCursor = webkitIDBCursor;
    IDBKeyRange = webkitIDBKeyRange;
    IDBTransaction = webkitIDBTransaction;
  }else if ('moz_indexedDB' in window){
    indexedDB = moz_indexedDB;
  }
 }
 init = function(){
   setup();
    var request = indexedDB.open("WebLibreria", "Il gestionale per librerie");
 }
 </script>
</head>
<body onload="init();">
</body>
</html>
Essendo, ad oggi, queste features ancora sperimentali Chromium (e Firefox) prefissano le classi di
riferimento con la particella ‘webkit': la funzione ‘setup' serve solamente per creare degli alias che
abbiano nomi più aderenti alle specifiche. Mano a mano che queste API si avvicineranno allo status di
standard assisteremo alla rimozione di questi prefissi e potremo commentare la funzione setup. La
creazione del database avviene attraverso l'istruzione ‘indexedDB.open', che accetta come parametro
il nome e la descrizione dell'oggetto che stiamo creando; in caso un database con lo stesso nome e lo
stesso dominio di origine sia già presente nel browser allora verrà utilizzato quest'ultimo.

A questo punto possiamo intercettare gli eventi di avvenuta creazione della connessione e di errore
durante la procedura gestendoli nel modo che meglio ci aggrada:

init = function(){
  setup();
  var request = indexedDB.open("WebLibreria", "Il gestionale per librerie");
  request.onsuccess = function(){console.log("Connessione Ok!");}
  request.onerror = function(){console.log("Ops, errore");}
}


Eseguiamo la pagina all'interno di Chromium prestando attenzione alla linguetta 'console' all'interno
dei Developer Tools (CTRL + SHIFT + J) e avremo conferma dell'avvenuta connessione/creazione al
database (figura 1):

Figura 1 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_database/1.jpg)

Ora che abbiamo stabilito una connessione è essenziale determinare se l'utente sia alla sua prima
visita, perché in questo caso è necessario procedere con la creazione degli object store e della
struttura necessari. Per gestire questo meccanismo le API mettono a disposizione il concetto di
versione, impostata a null per un database appena creato, che può essere utilizzata proprio capire
quando sia necessario apportare modifiche alla struttura. Ad esempio:

...
init = function(){
  setup();
  var request = indexedDB.open("WebLibreria", "Il gestionale per librerie");
  request.onsuccess = controllaVersione;
  request.onerror = function(){console.log("Ops, errore");}
  }
controllaVersione = function(event){
  window.database = event.result;
if(database.version != "1.0"){
    var request = database.setVersion("1.0");
    request.onsuccess = aggiornaLoSchema;
    request.onerror = function(){console.log("Ops, errore");}
  }else{
      // il database è già aggiornato alla versione più recente
  }
}
aggiornaLoSchema = function(){
console.log("Qui posso aggiornare lo schema");
}
...


La funzione aggiornaLoSchema viene invocata solamente nel caso in cui la versione del database sul
browser dell'utente sia diversa da quella attesa dal javascript (in questo caso la ‘1.0'); in particolare
registrare una funzione sul callback ‘onsuccess' del metodo setVersion è l'unico modo in cui sia
consentito dalle specifiche modificare la struttura del database.

Se ora provate questo codice all'interno del browser, nella linguetta console vedrete comparire la
scritta ‘Qui posso aggiornare lo schema', se poi ricaricate nuovamente la pagina invece noterete che
tale messaggio scompare: il database è infatti già nella versione “1.0”, quindi il flusso del programma
transita attraverso il blocco else, che è al momento vuoto. Per cancellare il database creato e poter
così ripetere la procedura è necessario svuotare completamente la cache, chiudere Chromium e
riaprirlo (figura 2):

Figura 2 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_database/2.jpg)

A questo punto creiamo la struttura del database: per prima cosa dobbiamo aprire una transazione,
successivamente istruiamo il database alla creazione dell'object store ‘libri', con chiave sul campo
‘isbn', e di due indici, rispettivamente per i campi ‘titolo' e ‘autore'. Approfittiamone anche per creare
la funzione recuperaLibri, che verrà invocata sia al completamento della struttura sia nel caso il
database sia già alla versione desiderata:

...
controllaVersione = function(event){
  window.database = event.result;
  if(database.version != "1.0"){
      var request = database.setVersion("1.0");
      request.onsuccess = aggiornaLoSchema;
      request.onerror = function(){console.log("Ops, errore");}
}else{
      recuperaLibri();
  }
}
aggiornaLoSchema = function(){
  window.transazione = event.result;
  transazione.oncomplete = recuperaLibri;
  transazione.onabort = function(){console.log("Ops, errore");}
  var libri = database.createObjectStore("libri", "isbn", false);
  var titolo = libri.createIndex("titolo", "titolo", false);
  var autore = libri.createIndex("autore", "autore", false);
}
recuperaLibri = function(){
  // recupera l'elenco dei libri e stampali a video
}
...


La funzione createObjectStore richiede 3 parametri, di cui solamente il primo, il nome, obbligatorio;
il secondo parametro rappresenta il nome della proprietà del record che vogliamo funga da chiave
all'interno dell'object store. Il terzo parametro imposta invece la proprietà autoincrement; nel caso
non sia presente una chiave nel record in inserimento e autoincrement = true, una chiave verrà
generata in automatico. Le due chiamate alla funzione createIndex provvedono alla creazione di due
indici, utili per ricerche e query, sui campi ‘titolo' e ‘autore'; il terzo parametro impostato a
falseconsente la presenza di valori duplicati. Ora che abbiamo creato la struttura del database
stampiamo a video l'elenco, per ora vuoto, dei libri registrati e creiamo un piccola form per
l'inserimento di un nuovo volume:

...
recuperaLibri = function(){
  window.transazione = database.transaction(["libri"],
    IDBTransaction.READ_WRITE, 0);
  var request = transazione.objectStore("libri").openCursor();
  request.onsuccess = stampaLibri;
  request.onerror = function(){console.log("Ops, errore");}
}
stampaLibri = function(){
  var cursor = event.result;
  if( cursor != null){
      document.getElementById("catalogo_libri").insertAdjacentHTML('beforeend',
        "<li>" + cursor.value.autore + ": " + cursor.value.titolo +
        " (ISBN: "+ cursor.value.isbn +"); </li>");
      cursor.continue();
  }
}
aggiungiLibro = function(){
// aggiungiamo un nuovo libro all'object store
}
</script>
</head>
<body onload="init();">
  <h1> WebLibreria: gestionale per librerie</h1>
<section>
  <h1>Elenco dei libri</h1>
    <ul id="catalogo_libri">
    </ul>
  </section>
  <aside>
    <h1>Aggiungi un nuovo libro</h1>
    <form name="aggiungi_libro" onsubmit="aggiungiLibro(this); return false;">
       <fieldset name="info_libro">
         <legend>Dati richiesti:</legend>
         <label>Titolo:
           <input name="titolo" type="text" required placeholder="es: Dalla terra alla
luna">
         </label>
         <label>Autore:
           <input name="autore" type="text" required placeholder="es: Jules Verne">
         </label>
         <label>ISBN:
           <input name="isbn" type="text" required placeholder="es: 8862221320">
         </label>
         <input type="submit" value="Aggiungi">
       </fieldset>
    </form>
  </aside>
</body>
</html>


Possiamo tranquillamente tralasciare la spiegazione della nuova porzione di codice HTML aggiunto:
trattasi semplicemente di un form che all'invio chiama una funzione, ancora da sviluppare, per
l'aggiunta di un nuovo libro. Molto più interessanti sono invece recuperaLibri e stampaLibri. In
recuperaLibri viene creata una nuova transazione che coinvolge l'object store ‘libri'; il secondo
parametro indica il tipo di transazione: purtroppo lettura/scrittura (IDBTransaction.READ_WRITE) è
l'unica opzione supportata ad oggi; il terzo valore rappresenta invece il timeout della transazione: lo 0
utilizzato significa mai. Vediamo l'istruzione successiva:

var request = transazione.objectStore("libri").openCursor();


La funzione openCursor inizializza un puntatore, detto anche cursore, al primo record dell'object
store ‘libri'. La funzione stampaLibri, che viene chiamata appena il cursore è stato creato e popola
una lista con i libri in catalogo, agisce come una specie di ciclo, infatti il metodo continue non fa
nient'altro che richiamare nuovamente stampaLibri, posizionando però il cursore al record successivo;
questo fino a quando non si giunge alla fine dei risultati di ricerca, a quel punto il valore del cursore
diviene null e un apposito if si preoccupa dell'abbandono della funzione.

Completiamo il nostro progetto con la funzione aggiungiLibro:

...
aggiungiLibro = function(data){
  var elements = data.elements
  window.transazione = database.transaction(["libri"],
    IDBTransaction.READ_WRITE, 0);
var request = transazione.objectStore("libri").put({
    titolo: elements['titolo'].value,
    autore: elements['autore'].value,
    isbn: elements[ 'isbn'].value
  }, elements[ 'isbn'].value));
  request.onsuccess = function(){pulisciLista(); recuperaLibri();}
  request.onerror = function(){console.log("Ops, errore");}
}
pulisciLista = function(){
  document.getElementById("catalogo_libri").innerHTML ="";
}
</script>


Il metodo interessante in questo caso è il put, che si preoccupa di aggiungere al database, previa
apertura di una transazione appropriata, un nuovo record con gli elementi ricevuti dal form (da notare
il secondo parametro, corrispondente alla chiave del record). Da notare che esiste anche un metodo
add che differisce nell'impedire la creazione di un record la cui chiave sia già presente nel database.

Eseguiamo un ultima volta l'applicazione, proviamo ad inserire un paio di libri ed ammiriamone il
risultato (figura 3):

Figura 3 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_database/3.jpg)


Una funzione di ricerca
Aggiungiamo questa funzione al progetto:

...
ricercaLibro = function(autore){
  pulisciLista();
  window.transazione = database.transaction(["libri"],
    IDBTransaction.READ_WRITE, 0);
  var request = transazione.objectStore("libri").index('autore').openCursor(
IDBKeyRange.bound(autore,autore+"z",true,true), IDBCursor.NEXT);
  request.onsuccess = stampaLibri;
  request.onerror = function(){console.log("Ops, errore");}
}
</script>


L'unica differenza rispetto alla già analizzata recuperaLibri sta nel fatto che, in questo caso, con il
metodo ‘index(‘autore')' indichiamo al database che la selezione dovrà essere fatta utilizzando
come discriminante l'indice ‘autore' creato sull'omonimo campo. In particolare tale selezione dovrà
recuperare tutti i record il cui autore inizia con una stringa passata al metodo. Per provare questa
funzione di ricerca creiamo un nuovo frammento HTML in coda alla form di inserimento:

...
  </form>
  <h1>Cerca un libro per autore</h1>
  <form name="cerca_per_autore"
     onsubmit="ricercaLibro(this.elements['keyword'].value); return false;">
    <fieldset name="campi_ricerca">
      <legend>Ricerca per autore:</legend>
      <label>Inizio del nome:
         <input name="keyword" type="search" required placeholder="es: Franc">
      </label>
      <input type="submit" value="Ricerca">
    </fieldset>
  </form>
</aside>
...


Ricarichiamo l'applicazione e proviamo ad inserire una valida stringa di ricerca (figura 4):

Figura 4 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_database/4.jpg)


Conclusione
Le Indexed Database API sono uno strumento estremamente potente che consentono di beneficiare di
un vero e proprio database all'interno del browser dell'utente. Le stesse funzionalità, anche se in una
sintassi più orientata a SQL, venivano offerte anche dalle promettenti Web SQL Database API
(http://www.w3.org/TR/webdatabase/), abbandonate dal 4 dicembre 2010 per motivi di sicurezza e
di, ironia della sorte, troppa uniformità di implementazione da parte degli user-agent (avevano tutti
scelto SQLlite come back-end per questo tipo di specifiche).

Prima di passare alla prossima lezione ricordo che le stesse API che abbiamo usato in forma asincrona
all'interno di questo progetto sono disponibili anche nel formato sincrono; per maggiori informazioni
in questo senso rimando alla documentazione ufficiale (http://www.w3.org/TR/IndexedDB/).

Per testare il tutto potete partire dalla demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_indexeddb/indexeddb.html).


Tabella del supporto sui browser

API e Web Applications

Indexed Database API        9.0+ (parziale) 4.0+ (parziale) 5.0+ (parziale) 8.0+ (parziale) No




                                      WebStorage API
Le WebStorage API nascono per risolvere due problematiche tipiche dei cookies; la loro limitata
dimensione massima (tipicamente 4K) e l’impossibilità di avere cookies differenziati tra due
diverse sessioni di navigazione sullo stesso dominio dallo stesso browser. Il secondo punto si
esplicita molto bene cercando di mantenere aperti contemporaneamente due account Gmail sullo
stesso browser, ogni navigazione sul primo comporterà il logout del secondo e viceversa.

I problemi in questione sono stati risolti creando due nuovi oggetti. sessionStorage consente di
avere un meccanismo di persistenza dei dati distinto per ogni sessione di navigazione in ogni finestra,
o tab, del browser. Usando sessionStorage sarebbe quindi possibile coordinare l’apertura
contemporanea di due distinti account GMail sullo stesso browser. localStorage mantiene il
comportamento del cookie essendo comune a tutte finestre del browser che condividono lo stesso
dominio. Entrambi sono inoltre stati studiati per ospitare molti più dati, almeno 5Mb, ed essere
persistenti anche alla chiusura ed alla riapertura del browser.


Le specifiche
Le API si compongono di una singola interfaccia Storage. localStorage e sessionStorage
implementano entrambi questa interfaccia e quindi dispongono dello stesso set di metodi anche se
chiaramente restano due oggetti distinti. Le principali operazioni possono essere eseguite come su di
un normale array associativo:

localStorage.nome          = 'Sandro';
localStorage.cognome         = 'Paganotti';




o con l’ausilio di metodi dedicati:

localStorage.setItem('nome','Sandro');
localStorage.getItem('nome'); // ritorna 'Sandro'




Se però ci accingiamo ad inserire valori diversi da stringhe il comportamento diverge: in un oggetto
come localStorage o sessionStorage è consentita la memorizzazione di soli contenuti testuali:

localStorage.ordini = Array("1","2","3");
localStorage.ordini; // ritorna "1,2,3"




È quindi necessario ricorrere a stratagemmi, come la serializzazione su JSON, per ottenere l’effetto
desiderato:

localStorage.ordini = JSON.stringify(Array(1,2,3));
JSON.parse(localStorage.ordini); // ritorna un array di 3 elementi: [1,2,3]




Chromium mette a disposizione degli sviluppatori web che intendono utilizzare queste API la linguetta
‘Storage’ dei Developer Tools (raggiungibili da menu o con la combinazione di tasti Control - Shift -
J e visibile in figura 1):

Figura 1 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_webstorage/1.jpg)


Un esempio
Utilizziamo il progetto guida per implementare una meccanica di salvataggio dei dati basata su
localStorage. Non avendo ancora sviluppato la gestione della lavagna ci concentreremo sul
memorizzare e visualizzare stringhe inserite dall’utente attraverso una textarea, iniziamo con il
markup completando il file index.html:

<!doctype html>
<html lang='it' manifest='fiveboard.manifest'>
<head>
  <meta charset="utf-8">
  <title>FiveBoard: uno spazio per gli appunti.</title>
  <script src="js/application.js" defer></script>
</head>
<body>
  <hgroup>
    <h1>Dì qualcosa </h1>
    <h2>FiveBoard ricorda tutto</h2>
  </hgroup>
  <form name="form_da_ricordare">
    <menu type="toolbar">
      <button type="button" onclick="salvaIlDato(this.form.elements['testo_da_ricordar
e'].value);">
      Memorizza quanto scritto
      </button>
      <button type="button" onclick="recuperaIlDato(this.form.elements['testo_da_ricor
dare']);">
      Recupera l'ultimo testo memorizzato
    </button>
    </menu>
    <label>Cosa hai in mente?
      <textarea name="testo_da_ricordare" required autofocus
      placeholder="La lista della spesa, il teatro di questa sera ..."></textarea>
    </label>
  </form>
</body>
</html>
Dato che questo progetto si configura più come una web application che come un sito web,
implementiamo una classica struttura di controllo desktop-oriented: la toolbar. All’interno di questa,
due semplici pulsanti gestiscono le azioni di memorizzazione e recupero del testo salvato attraverso
una textarea; l’attributo ‘autofocus’ indica allo user-agent di portare il cursore in posizione di
inserimento testo nell’istante di completo caricamento della pagina.

Ora, nel file application.js completiamo l’esempio introducendo le funzioni di salvataggio e di recupero
informazioni basate su localStorage:

salvaIlDato = function(info_da_salvare){
   localStorage.ultimo_pensiero = info_da_salvare;
   alert("Memorizzazione effettuata");
};

recuperaIlDato = function(elemento){
   if(confirm("Sostituire il contenuto attuale con l'ultimo pensiero memorizzato?")){
     elemento.value = localStorage.ultimo_pensiero;
   }
};




Bene, non ci resta che provare l’esempio
(http://www.html.it/guide/esempi/html5/esempi/lezione_webstorage/fiveboard/index.html)
all’interno di Chromium e constatare il suo semplice ma efficace funzionamento, capace di persistere
l’informazione anche a seguito di un riavvio del browser.


Tabella del supporto sui browser

API e Web Applications

WebStorage                  8.0+ 3.6+ 3.1+ 2.0+ 10.5+




                                     Web Workers API


I Web Workers nascono per consentire l’esecuzione di porzioni di codice Javascript in modo
asincrono, senza intaccare le performance della pagina web in visualizzazione. I Web Workers,
nient’altro che file Javascript, possono essere comparati a dei thread che la pagina web può lanciare e
con i quali può dialogare attraverso semplici metodi. Ogni WebWorker può eseguire a sua volta altri
WebWorkers ed ognuno di essi può effettuare operazioni di I/O, calcoli complessi e quant’altro.

Le API in questione prevedono che un WebWorker possa essere generato come oggetto della classe
Worker o SharedWorker: nel primo caso la sua esecuzione sarà limitata alla specifica sessione di
navigazione all’interno della finestra (o tab) del browser che l’ha invocato; nel secondo invece
ogni sessione di navigazione che condivide la stessa origine (lo stesso dominio) potrà
connettersi e scambiare messaggi con il medesimo worker. In questo modo lo SharedWorker
assume il ruolo di coordinatore, ottimo per, ad esempio, propagare su tutte le finestre del browser
puntate su di un particolare dominio un messaggio ricevuto dal server.


Le specifiche
Per creare un Worker l’istruzione da eseguire è decisamente semplice:

// pagina principale
bot_noioso = new Worker('bot_noioso.js');




Lo script invocato, ‘bot_noioso.js’, verrà eseguito in asincrono e potrà inviare messaggi verso la
pagina principale attraverso la funzione postMessage:

// bot_noioso.js
postMessage('Perch´ la terra è tonda?');




Per ricevere questi messaggi è necessario registrare una funzione all’handler ‘onmessage’ del worker
contenuto nella variabile bot_noioso: chiaramente utilizzando la stessa variabile è possibile inviare,
sempre tramite postMessage messaggi al worker.

// pagina principale
bot_noioso = new Worker('bot_noioso.js');
bot_noioso.onmessage = function(event){
  bot_noioso.postMessage(prompt(event.data));
}




Allo stesso modo è possibile registrare l’handler ‘onmessage’ anche all’interno del file del worker:

// bot_noioso.js
postMessage('Perch´ la terra è tonda?');
onmessage = function(event){
  if(event.data != null){
    postMessage('Perch´:' + event.data + ' ?');
  }
}




Ora, se lanciamo Chromium potremo sincerarci del buon funzionamento dello script (figura 1):

Figura 1 - Testo
Le API dello SharedWorker differiscono in modo sensibile rispetto a queste, anche se ovviamente
mantengono immutato il funzionamento di base; per poterle trattare con la dovuta cura costruiamo
un piccolo esempio che le utilizzi.

Ecco la demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_webworkers/webworker.html).


Un esempio
Continuiamo con il progetto guida; l’obiettivo di questo capitolo è beneficiare delle feature di uno
SharedWorker per implementare una dashboard di controllo, che amministri le sessioni di
Fiveboard aperte contemporaneamente in un browser. Per raggiungere questo (ambizioso) bersaglio
creeremo un file ‘js/hub.js’, presso il quale tutte le finestre attive del progetto dovranno registrarsi,
che convoglierà le informazioni da e verso una console ‘dashboard.html’. Ecco uno schema
dell’architettura (figura 2):

Figura 2 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/2.jpg)

Le comunicazioni da e verso lo SharedWorker transitano attraverso oggetti chiamati MessagePort,
nell’immagine qui sopra sono esplicitate 4 MessagePort. Una MessagePort è utilizzata implicitamente
anche dall’oggetto Worker del capitolo precedente e quindi non ci si deve sorprendere nell’apprendere
che il meccanismo di funzionamento di uno SharedWorker è esattamente lo stesso, fatto salvo che in
questo caso è necessario specificare la porta attraverso la quale si vuole inviare o ricevere il
messaggio.

// Invio di un messaggio ad uno SharedWorker
worker.port.postMessage('...');

// Ricezione di un messaggio da uno SharedWorker
worker.port.onmessage = function(evento){ ... }


// Dall’interno di uno SharedWorker: invio di un messaggio
messageport_del_destinatario.postMessage('...');


// Dall’interno di uno SharedWorker: ricezione di un messaggio
messageport_del_mittente.onmessage = function(event){ ... }




Bene, ecco in breve come dovranno comportarsi i 3 componenti che andremo a definire:

      Index.html
           Connettersi allo SharedWorker;
           Presentarsi come Client e specificare il proprio nome (il titolo del documento);
      Dashboard.html
           Connettersi allo SharedWorker;
           Presentarsi come dashboard e ricevere informazioni su tutti i Client già registrati;
             Essere notificata ad ogni nuova registrazione di un Client;
Mantenere a video un elenco dei Client registrati;
         Hub.js (SharedWorker)
               Registrare ogni Client e notificare, se presente, la dashboard;
               Registrare la dashboard ed inviarle tutti i Client registrati fino a quel momento.

Lo scambio di messaggi tra le varie parti avverrà tramite stringhe di testo nel formato
“chiave:valore”, come ad esempio “registra_client:Documento di prova”; questa scelta non è
dettata dalle specifiche, che sono abbastanza lasche in tal senso, ma da una semplice convenzione
adottata anche da alcuni esempi del W3C (http://www.whatwg.org/specs/web-apps/current-
work/complete/workers.html#shared-state-using-a-shared-worker).

Bene, possiamo partire; iniziamo dal file ‘application.js’ che dovrà assumere questo aspetto:

salvaIlDato = function(info_da_salvare){
   localStorage.setItem("fb_" + titolo_fiveboard,info_da_salvare);
   alert("Memorizzazione effettuata");
};
recuperaIlDato = function(elemento){
   if(confirm("Sostituire il contenuto attuale con l'ultimo pensiero memorizzato?")){
     elemento.value = localStorage.getItem("fb_" + titolo_fiveboard);
   }
};
var titolo_fiveboard = null;
window.onload = function(){
   var worker = new SharedWorker('js/hub.js');
   worker.port.onmessage = function(evento){
     nome_comando        = evento.data.split(":")[0]
     valore_comando = evento.data.substr(nome_comando.length + 1);
     console.log("Ricevuto comando: " + nome_comando);
     switch (nome_comando){
       case 'pronto':
       titolo_fiveboard = prompt("Seleziona il titolo per questa FiveBoard");
       document.title = "FB: " + titolo_fiveboard;
       worker.port.postMessage("registra_client:" + titolo_fiveboard);
        break;
        }
    }
}




Al caricamento della pagina viene attivata la funzione collegata a window.onload: questa crea il
collegamento con lo SharedWorker (se il worker non è già presente verrà caricato in memoria e
lanciato) e definisce una funzione di ‘ascolto’ nella quale, per ogni messaggio ricevuto, esegue
particolari azioni a seconda della chiave estratta. In questo momento la funzione reagisce alla sola
chiave ‘pronto’ chiedendo all’utente un titolo del documento ed inviando il risultato al worker con la
chiave ‘registra_client’.

Il titolo del documento viene anche utilizzato nelle funzioni di salvataggio e di recupero del dato, per
differenziare fra di loro le variabili memorizzate su LocalStorage in modo da evitare collisioni durante
l’apertura contemporanea di più index.html.

Ora definiamo hub.js:
var fiveboards_registrate = new Array();
var dashboard = null;

processa_il_messaggio = function(evento){
  nome_comando = evento.data.split(":")[0]
  valore_comando = evento.data.substr(nome_comando.length + 1);
  switch (nome_comando){
    case 'registra_client':
       fiveboards_registrate[valore_comando]=evento.target;
       if(dashboard != null){
         dashboard.postMessage("nuova_fiveboard:" + valore_comando);
    }
    break;
    case 'registra_dashboard':
       dashboard = evento.target;
       for(fiveboard in fiveboards_registrate){
         evento.target.postMessage("nuova_fiveboard:" + fiveboard);
    }
break;
  }
}
onconnect = function(nuova_finestra){
  var port = nuova_finestra.ports[0];
  port.onmessage = processa_il_messaggio;
  port.postMessage("pronto");
}




L’handler onconnect viene invocato ogni qualvolta una pagina tenta di aprire una connessione verso il
worker; nella funzione si delega al metodo ‘processa_il_messaggio’ la gestione dei futuri scambi tra
in worker e la pagina; a quest’ultima è inoltre segnalato il concludersi delle operazioni con un
messaggio ‘pronto’.

La funzione processa_il_messaggio interpreta e gestisce le due chiavi registra_client e
registra_dashboard: nel primo caso aggiunge la MessagePort di comunicazione con il client ad una
collezione fiveboards_registrate e comunica alla dashboard, se presente, la nuova aggiunta
tramite un messaggio con chiave nuova_fiveboard. Nel secondo caso registra la MessagePort della
dashboard (attraversoevento.target, che corrisponde ad evento.ports[0]) e comunica alla stessa
tutti client finora memorizzati attraverso un ciclo sulla collezione fiveboards_registrate e ad un
messaggio con la già nota chiave nuova_fiveboard.

Perfetto, ora non ci resta che definire markup e Javascript di ‘dashboard.html’:

<html lang='it'>
<head>
  <meta charset="utf-8">
  <title>Five(Dash)Board: tutto sotto controllo!.</title>
  <script>
  var worker = null;

  getInfo = function(title){
    // to be defined...
}

  init = function(){
    worker = new SharedWorker('js/hub.js');
    worker.port.onmessage = function(evento){
       nome_comando      = evento.data.split(":")[0]
       valore_comando = evento.data.substr(nome_comando.length + 1);
       console.log("Ricevuto comando: " + nome_comando);
       switch (nome_comando){
         case 'pronto':
           worker.port.postMessage("registra_dashboard")
         break;
         case 'nuova_fiveboard':
           document.getElementById("elenco_fiveboard").insertAdjacentHTML('beforeend',
             "<li>" +
             "Titolo: " + valore_comando + " " +
             "(<a href='javascript:getInfo("" + valore_comando +"");'>" +
             "più informazioni" + "</a>)" +
             "</li>");
         break;
         }
    }
  }
  </script>
</head>
<body onload="init();">
  <h1>FiveBoard:</h1>
  <ol id="elenco_fiveboard">
  </ol>
</body>
</html>




Il listato è largamente autoesplicativo sulla base di quanto già enunciato: in questo caso le chiavi
gestite sono ‘pronto’ e ‘nuova_fiveboard’: mentre la prima attiva il meccanismo di registrazione
(registra_dashboard) appena visto in hub.js, la seconda si incarica di popolare una lista ordinata
ogniqualvolta la pagina riceve una notifica di registrazione di una nuova fiveboard.

Eseguiamo il tutto in Chromium e godiamoci il risultato dei nostri sforzi (figura 3):

Figura 3 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/3.jpg)

È possibile verificare il tutto in questa demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_webworkers/fiveboard/index.html).


Più informazioni per i più ardimentosi
Vi siete chiesti a cosa debba servire il link ‘più informazioni’ e la funzione getInfo ad esso collegata?
L’idea è questa: al click sul comando il sistema deve mostrare all’utente il testo inserito ed il testo
memorizzato per l’istanza selezionata. Per raggiungere l’obiettivo ecco come interverremo:

   1. La dashboard chiede allo SharedWorker maggiori_informazioni per una data finestra;
   2. Lo SharedWorker mette in diretta comunicazione la finestra in oggetto e la dashboard;
   3. La finestra in oggetto comunica alla dashboard testo inserito e testo memorizzato.

Ecco uno schema delle comunicazioni (figura 4):

Figura 4 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/4.jpg)

Il punto 2 è decisamente il più interessante e si avvale di MessageChannel: trattasi di un
semplicissimo oggetto, nato all’interno delle API di comunicazione HTML5
(http://www.w3.org/TR/html5/comms.html), contenente due MessagePort ed istruito per fare in
modo che ogni messaggio in ingresso alla porta1 venga recapitato alla porta2 e viceversa.

Tutto quello che hub.js deve fare è quindi creare un MessageChannel e dare i due capi del filo (le due
MessagePort) rispettivamente alla dashboard ed al client interessato; per fare questo risulta molto
comodo il secondo argomento del metodo postMessage, che consente appunto di specificare un array
di porte da allegare ad un messaggio.

Tutto chiaro? No? Vediamo le modifiche apportate al progetto:

// file dashboard.html
// invio della richiesta allo SharedWorker (punto 1)
  getInfo = function(title){
    worker.port.postMessage("maggiori_informazioni:" + title);
  }

  // una nuova chiave da gestire.
    case 'attendi_testo':
      evento.ports[0].onmessage = function(e){
         alert(e.data);
      }
  break;

// file hub.js

  // una nuova chiave da gestire, creazione del canale ed invio delle porte di
  // comunicazione alla dashboard ed al client interessato (punto 2)
  case 'maggiori_informazioni':
  var channel = new MessageChannel();
  dashboard.postMessage("attendi_testo",[channel.port1]);
  fiveboards_registrate[valore_comando].postMessage("richiedi_testo",
  [channel.port2]);
  break;

// file application.js

  // una nuova chiave da gestire, invio delle informazioni richieste attraverso la
  // MessagePort ricevuta dall’evento (non la MessagePort di comunicazione col worker)

  // (punto 3)
  case 'richiedi_testo':
    evento.ports[0].postMessage(
      "testo corrente:" + document.forms['form_da_ricordare'].elements
      ['testo_da_ricordare'].value + "n" +
       "testo memorizzato:" + localStorage.getItem("fb_" + titolo_fiveboard)
    );
    break;




Eseguiamo il tutto in Chromium ed ammiriamone il risultato (figura 5):

Figura 5 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/5.jpg)


Conclusioni
I Web Worker sono il classico esempio di API semplici solo in apparenza; più ci si addentra in utilizzi
concreti più si scoprono possibilità e nuove metodologie di sviluppo. È importante ricordare infatti che
sono previsti meccanismi di interazione tra i Web Workers, l’ApplicationCache, e i WebSocket, e che
ogni WebWorker può invocarne altri.

Sono diretta conseguenza di questa intrinseca flessibilità delle API gli innumerevoli scenari di utilizzo
ipotizzabili oltre a quello già mostrato, come ad esempio svolgere calcoli complessi (ogni Worker può
girare su di un core differente) quali rendering o image detection in tempo reale, centralizzare le
comunicazioni con il server attraverso uno SharedWorker e un WebSocket oppure monitorare processi
browser-side.


Tabella del supporto sui browser

API e Web Applications

WebWorkers                 No     3.6+ 4.0+ 2.0+ 10.6+




                                      WebSockets API


Le WebSockets API introducono, nella loro estrema semplicità, una funzionalità tra le più attese ed
emulate: la possibilità di stabilire e mantenere una connessione dati tra browser e server
remotosulla quale far transitare messaggi in entrambe le direzioni. Le attuali specifiche, che lasciano
ben poco spazio per implementazioni del genere, hanno, nel corso degli anni, dato luogo a
workaround più o meno esotici tra i quali l'utilizzo di socket in Flash pilotati via Javascript e della
famosa tecnica di long polling (l'utilizzo continuo di chiamate AJAX mantenute aperte fino alla
ricezione del dato o al tempo di timeout). Le nuove API offrono invece un meccanismo ben più
semplice grazie all'oggetto WebSocket, al metodo send e all'evento onmessage.

Prima di passare alla visione delle specifiche e al dovuto esempio di implementazione è importante
ricordare che questa tecnologia non consente di creare connessioni verso altri, ben conosciuti
protocolli, come ad esempio telnet, SMTP, IRC, etc., per due distinti motivi: in primo luogo lo user
agent implementa una policy che blocca l'accesso verso porte riservate a servizi conosciuti (fanno
eccezione solo la 80 e la 443). In seconda istanza le comunicazioni viaggiano all'interno di un nuovo e
specifico protocollo, chiamato con molta fantasia 'The WebSocket Protocol
(http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03)' che richiama per certi aspetti,
soprattutto durante l'handshake
(http://en.wikipedia.org/wiki/WebSockets#WebSocket_Protocol_Handshake), una conversazione
HTTP.


Le specifche
L'utilizzo di queste API è assolutamente didascalico, ci troviamo di fronte infatti a poco più di tre
metodi; vediamoli insieme:

var echo_service = new WebSocket('ws://echo.websocket.org');



La creazione di un nuovo WebSocket richiede come unico parametro obbligatorio l'url verso la
quale si vuole stabilire la connessione. Il protocollo può essere ws o wss, dove il secondo indica la
richiesta di una connessione sicura. Opzionalmente è possibile passare al costruttore anche una
stringa o un array di sub-protocolli: valori arbitrari utili per comunicare al server un elenco di servizi
che l'oggetto in costruzione può supportare. Ad esempio un server di chat potrebbe rispondere solo a
richieste con protocollo 'server_di_chat', e via dicendo...

echo_service.onmessage = function(event){
  alert(event.data);
}


Una volta creato un nuovo WebSocket, il funzionamento dello stesso diventa praticamente identico,
nella forma, a quello già esposto per la comunicazione tra Worker: la funzione associata
all'handleronmessage viene invocata ogniqualvolta dal server proviene un messaggio,

echo_service.onopen = function(){
  echo_service.send("hello!");
}


mentre la funzione send provvede all'invio, verso il server remoto, del testo passato come
argomento. Da notare che l'invio deve essere necessariamente subordinato alla condizione di
avvenuta connessione, notificata tramite l'evento onopen. Esistono altri eventi all'interno del ciclo vita
di un WebSocket: onclose e onerror; vediamoli insieme completando l'esempio:

<!doctype html>
<html>
<head>
  <title> WebSocket: Echo Server </title>
  <script>
  append = function(text){
     document.getElementById("eventi_websocket").insertAdjacentHTML('beforeend',
     "<li>" + text + ";</li>"
  );
  }
  window.onload = function(){
    var echo_service = new WebSocket('ws://echo.websocket.org');
    echo_service.onmessage = function(event){
      append("messaggio ricevuto")
      alert(event.data);
      echo_service.close();
    }
    echo_service.onopen = function(){
      append("connessione effettuata")
      echo_service.send("hello!");
    }
    echo_service.onclose = function(){
      append("connessione chiusa");
    }
    echo_service.onerror = function(){
      append("errore nella connessione");
    }
  }
  </script>
</head>
<body>
  <ul id="eventi_websocket">
  </ul>
</body>
</html>


In questo esempio è stato introdotto anche il metodo close, utile per terminare una connessione.
Eseguiamo l'esempio all'interno di Chromium (figura 1):

Figura 1 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/1.jpg)

Prima di proseguire è bene ricordare che il server utilizzato per questo esempio (demo
(esempi/lezione_websockets/websocket.html)): echo.websocket.org ha la peculiarità, come il nome
stesso suggerisce, di rispondere ad ogni messaggio con lo stesso testo ricevuto.


Un esempio
Nel prossimo capitolo estenderemo il progetto guida in modo che ogni FiveBoard sia connessa ad
un server centrale; creeremo quindi un viewer: una pagina html capace di connettersi ad una
FiveBoard registrata presso il server e leggerne il testo mano mano esso viene digitato. Ecco lo
schema (figura 2):

Figura 2 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/2.jpg)

Per poter raggiungere questo obiettivo è necessario scrivere anche del codice in un linguaggio server-
side. Tale codice servirà per creare e istruire il WebSocket Server, al quale le varie pagine dovranno
connettersi; ai fini di questo esempio non è necessario cercare un servizio di hosting sul quale
installare il codice del WebSocket Server: la nostra macchina di sviluppo andrà benissimo. Per questa
implementazione utilizzeremo Ruby (http://ruby.html.it), un linguaggio di programmazione elegante
e conciso. L'installazione dell'interprete Ruby è veramente facile ed il codice che utilizzeremo molto
leggibile. Per prima cosa colleghiamoci al portale ufficiale: http://www.ruby-lang.org/it/downloads/
(http://www.ruby-lang.org/it/downloads/) e selezioniamo la procedura di installazione dedicata al
nostro sistema operativo, quindi apriamo una console di comando (a volte chiamata anche terminale)
e digitiamo:

gem install em-websocket


Per installare la libreria necessaria allo sviluppo del WebSocket Server (per alcuni sistemi operativi è
necessario anteporre sudo all'istruzione).

Creiamo ora un file 'websocket_server.rb' e modifichiamone il contenuto come segue:

require 'rubygems'
require 'em-websocket'

EventMachine.run {
  @channels = Hash.new {|h,k| h[k] = EM::Channel.new }
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do
|
  ws|


    ws.onopen do
      sid = nil
      fiveboard_channel = nil

    ws.onmessage do |msg|
      command, value = msg.split(":", 2);
      case command
        when 'registra'
          fiveboard_channel = @channels[value]
          sid = fiveboard_channel.subscribe { |txt| ws.send(txt) }
        when 'aggiorna'
          fiveboard_channel.push('testo:' + value)
      end
    end


    ws.onclose do
      fiveboard_channel.unsubscribe(sid)
    end
  end
end
puts "Il server è correttamente partito"
}



Seppur possiate essere non abituati a questo linguaggio il codice è tutto sommato comprensibile e
succinto, ecco la spiegazione dell'algoritmo:

      Con l'istruzione WebSocket.start(.. l'applicazione si mette in attesa di connessioni websocket
      sulla porta 8080; ogni connessione in ingresso viene memorizzata nella variabile ws e causa
      l'esecuzione delle successive istruzioni (quelle comprese nell'attiguo blocco do..end).
Alla ricezione di un messaggio attraverso una connessione ws (ws.onmessage) il server si
       comporta dividendo il testo ricevuto secondo la solita convenzione 'comando:valore' ed agendo
       in modo diverso a seconda che il comando sia 'registra' o 'aggiorna'.
       Nel caso il messaggio sia 'registra:titolo_del_documento' il server aggiungerà la
       connessione attuale ad un canale che porta il nome del valore del messaggio (in questo caso
       'titolo_del_documento'). In questo modo tutte le pagine che vorranno 'osservare' il
       documento 'A' non dovranno far altro che inviare al WebSocket Server il messaggio
       'registra:A'.
       Nel caso il messaggio sia 'aggiorna:testo_del_documento' il server si comporterà
       semplicemente inviando lungo il canale associato alla connessione corrente il valore del
       messaggio (in questo caso 'testo_del_documento'), propagandolo in questo modo a tutte le
       connessioni registrate al canale.
       Infine con l'istruzione ws.onclose do... si gestisce, in caso di disconnessione del client, la
       rimozione di ws dal canale presso il quale si era registrata.

Eseguiamo il server portandoci con il terminale nella posizione dello script e digitando:

ruby websocket_server.rb


Un messaggio, 'Il server è correttamente partito', dovrebbe confermare la bontà del nostro operato.
Dedichiamoci ora alle API Javascript ed alla loro implementazione, per prima cosa editiamo il file
'js/application.js' per fare in modo che ogni FiveBoard si registri presso il server ed invii segnali di
aggiornamento ad ogni modifica del testo, ecco il codice da inserire all'interno della funzione
window.onload:

// all'interno di window.onload, js/application.js
// queste due istruzioni sono state spostate dalla loro precedente posizione
titolo_fiveboard = prompt("Seleziona il titolo per questa FiveBoard");
document.title = "FB: " + titolo_fiveboard;

// creazione di un nuovo socket verso il server Ruby
websocket = new WebSocket('ws://0.0.0.0:8080');
websocket.onopen = function(){
  // invio del comando 'registra'
    websocket.send("registra:" + titolo_fiveboard);
}

// ad ogni variazione di input segue l'invio del comando 'aggiorna' /
/ verso il server Ruby
document.forms['form_da_ricordare'].elements['testo_da_ricordare'].oninput = function
(event){
websocket.send("aggiorna:" + event.target.value);
}


Anche in questo caso l'implementazione risulta abbastanza leggibile; unico appunto da fare
sull'evento oninput, anch'esso novità introdotta dalle specifiche HTML5, che viene invocato ad ogni
attività di input (pressione di un tasto, copia ed incolla, drag and drop,...) sull'elemento in oggetto.

Completiamo l'esempio con la creazione della semplicissima pagina 'viewer.html':

<!doctype html>
<html>
<head>
  <title>FiveBoard Viewer</title>
  <script>
    window.onload = function(){
       var documento_da_visionare = prompt("Inserisci il nome del documento che vuoi
       osservare");
       var websocket = new WebSocket('ws://0.0.0.0:8080');
       websocket.onopen = function(){
         document.title = "VB: " + documento_da_visionare;
         websocket.send("registra:" + documento_da_visionare);
       }
       websocket.onmessage = function(evento){
         nome_comando    = evento.data.split(":")[0]
         valore_comando = evento.data.substr(nome_comando.length + 1);
         switch (nome_comando){
           case 'testo':
             document.getElementById('documento_in_visione').value = valore_comando;
           break;
         }
       }
    }
  </script>
</head>
<body>
  <textarea id="documento_in_visione" readonly>Aspettando il primo aggiornamento...</
textarea>
</body>
</html>



In questo caso la pagina è istruita nel reagire alla ricezione di un messaggio da parte del WebSocket
Server; il valore ricevuto viene infatti gestito con la solita convenzione 'comando:valore' e, nel caso il
comando sia 'testo', il valore viene inserito all'interno di una textarea preposta.

Bene, eseguiamo una prova di funzionamento
(http://www.html.it/guide/esempi/html5/esempi/lezione_websockets/fiveboard/index.html):
sincerandoci di aver lanciato il WebSocket Server navighiamo prima sulla pagina 'index.html' e
creiamo un nuovo documento 'esempio1', quindi apriamo una nuova finestra e puntiamola all'indirizzo
di 'viewer.html': alla richiesta del nome del documento inseriamo anche qui 'esempio1' in modo da
legare il viewer alla fiveboard.

Ora proviamo a digitare alcuni caratteri sulla prima finestra e noteremo che, attraverso il server,
questi sono propagati in tempo reale alla seconda! Ovviamente il tutto funzionerebbe anche se la
conversazione avvenisse tra due macchine distinte attraverso un server remoto; nell'immagine
seguente si può notare una fiveboard aperta sull'iPad ed il suo corrispondente viewer visibile su di un
portatile; in alto i messaggi di log del WebSocket Server (figura 3).

Figura 3 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/3.jpg)


Conclusioni
I WebSocket, sono nella loro estrema semplicità, degli strumenti incredibilmente potenti; a riprova di
questo fatto la rete ha già incominciato ad offrire interessanti prospettive di utilizzo nonostante
l'innegabile giovinezza di queste API. Tra le soluzioni degne di nota merita sicuramente una citazione
Pusher (http://pusherapp.com/), un servizio che offre la possibilità di gestire eventi real-time
attraverso l'utilizzo di WebSocket. Un po' più artigianale, ma altrettanto valido è http://jsterm.com/
(http://jsterm.com/), un'applicazione che consente di collegarsi a server remoti utilizzando un proxy,
scritto in node.js, tra il protocollo WebSocket e Telnet.

Recentemente lo sviluppo delle API è stato frenato dalla scoperta di una seria vulnerabilità
[documento PDF] (http://www.adambarth.com/experimental/websocket.pdf) legata al potenziale
comportamento di un caching proxy che può essere indotto, attraverso l'utilizzo di WebSocket ad-hoc,
a modificare il contenuto delle informazioni consegnate ad altri client. La vulnerabilità è stata
correttamente risolta dalla versione 0.7 del protocollo ma sfortunatamente le versioni 4 e 5 di Firefox,
rilasciate prima del fix, hanno i websocket disabilitati per default; la versione 6 del browser dovrebbe
però ripristinare il funzionamento out-of-the-box di questa interessantissima feature.


Tabella del supporto sui browser

API e Web Applications


WebSockets                 No    4.0+ (parziale) 5.0+ 7.0+ 11.0+ (parziale)




                                      WebSockets API


Le WebSockets API introducono, nella loro estrema semplicità, una funzionalità tra le più attese ed
emulate: la possibilità di stabilire e mantenere una connessione dati tra browser e server
remotosulla quale far transitare messaggi in entrambe le direzioni. Le attuali specifiche, che lasciano
ben poco spazio per implementazioni del genere, hanno, nel corso degli anni, dato luogo a
workaround più o meno esotici tra i quali l'utilizzo di socket in Flash pilotati via Javascript e della
famosa tecnica di long polling (l'utilizzo continuo di chiamate AJAX mantenute aperte fino alla
ricezione del dato o al tempo di timeout). Le nuove API offrono invece un meccanismo ben più
semplice grazie all'oggetto WebSocket, al metodo send e all'evento onmessage.

Prima di passare alla visione delle specifiche e al dovuto esempio di implementazione è importante
ricordare che questa tecnologia non consente di creare connessioni verso altri, ben conosciuti
protocolli, come ad esempio telnet, SMTP, IRC, etc., per due distinti motivi: in primo luogo lo user
agent implementa una policy che blocca l'accesso verso porte riservate a servizi conosciuti (fanno
eccezione solo la 80 e la 443). In seconda istanza le comunicazioni viaggiano all'interno di un nuovo e
specifico protocollo, chiamato con molta fantasia 'The WebSocket Protocol
(http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03)' che richiama per certi aspetti,
soprattutto durante l'handshake
(http://en.wikipedia.org/wiki/WebSockets#WebSocket_Protocol_Handshake), una conversazione
HTTP.


Le specifche
L'utilizzo di queste API è assolutamente didascalico, ci troviamo di fronte infatti a poco più di tre
metodi; vediamoli insieme:

var echo_service = new WebSocket('ws://echo.websocket.org');


La creazione di un nuovo WebSocket richiede come unico parametro obbligatorio l'url verso la
quale si vuole stabilire la connessione. Il protocollo può essere ws o wss, dove il secondo indica la
richiesta di una connessione sicura. Opzionalmente è possibile passare al costruttore anche una
stringa o un array di sub-protocolli: valori arbitrari utili per comunicare al server un elenco di servizi
che l'oggetto in costruzione può supportare. Ad esempio un server di chat potrebbe rispondere solo a
richieste con protocollo 'server_di_chat', e via dicendo...

echo_service.onmessage = function(event){
  alert(event.data);
}


Una volta creato un nuovo WebSocket, il funzionamento dello stesso diventa praticamente identico,
nella forma, a quello già esposto per la comunicazione tra Worker: la funzione associata
all'handleronmessage viene invocata ogniqualvolta dal server proviene un messaggio,

echo_service.onopen = function(){
  echo_service.send("hello!");
}


mentre la funzione send provvede all'invio, verso il server remoto, del testo passato come
argomento. Da notare che l'invio deve essere necessariamente subordinato alla condizione di
avvenuta connessione, notificata tramite l'evento onopen. Esistono altri eventi all'interno del ciclo vita
di un WebSocket: onclose e onerror; vediamoli insieme completando l'esempio:

<!doctype html>
<html>
<head>
  <title> WebSocket: Echo Server </title>
  <script>
  append = function(text){
     document.getElementById("eventi_websocket").insertAdjacentHTML('beforeend',
     "<li>" + text + ";</li>"
  );
  }
  window.onload = function(){
    var echo_service = new WebSocket('ws://echo.websocket.org');
    echo_service.onmessage = function(event){
      append("messaggio ricevuto")
      alert(event.data);
      echo_service.close();
    }
    echo_service.onopen = function(){
      append("connessione effettuata")
      echo_service.send("hello!");
    }
    echo_service.onclose = function(){
      append("connessione chiusa");
    }
    echo_service.onerror = function(){
      append("errore nella connessione");
    }
  }
  </script>
</head>
<body>
  <ul id="eventi_websocket">
  </ul>
</body>
</html>


In questo esempio è stato introdotto anche il metodo close, utile per terminare una connessione.
Eseguiamo l'esempio all'interno di Chromium (figura 1):

Figura 1 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/1.jpg)

Prima di proseguire è bene ricordare che il server utilizzato per questo esempio (demo
(esempi/lezione_websockets/websocket.html)): echo.websocket.org ha la peculiarità, come il nome
stesso suggerisce, di rispondere ad ogni messaggio con lo stesso testo ricevuto.


Un esempio
Nel prossimo capitolo estenderemo il progetto guida in modo che ogni FiveBoard sia connessa ad
un server centrale; creeremo quindi un viewer: una pagina html capace di connettersi ad una
FiveBoard registrata presso il server e leggerne il testo mano mano esso viene digitato. Ecco lo
schema (figura 2):

Figura 2 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/2.jpg)

Per poter raggiungere questo obiettivo è necessario scrivere anche del codice in un linguaggio server-
side. Tale codice servirà per creare e istruire il WebSocket Server, al quale le varie pagine dovranno
connettersi; ai fini di questo esempio non è necessario cercare un servizio di hosting sul quale
installare il codice del WebSocket Server: la nostra macchina di sviluppo andrà benissimo. Per questa
implementazione utilizzeremo Ruby (http://ruby.html.it), un linguaggio di programmazione elegante
e conciso. L'installazione dell'interprete Ruby è veramente facile ed il codice che utilizzeremo molto
leggibile. Per prima cosa colleghiamoci al portale ufficiale: http://www.ruby-lang.org/it/downloads/
(http://www.ruby-lang.org/it/downloads/) e selezioniamo la procedura di installazione dedicata al
nostro sistema operativo, quindi apriamo una console di comando (a volte chiamata anche terminale)
e digitiamo:

gem install em-websocket


Per installare la libreria necessaria allo sviluppo del WebSocket Server (per alcuni sistemi operativi è
necessario anteporre sudo all'istruzione).

Creiamo ora un file 'websocket_server.rb' e modifichiamone il contenuto come segue:

require 'rubygems'
require 'em-websocket'

EventMachine.run {
  @channels = Hash.new {|h,k| h[k] = EM::Channel.new }
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do
|
  ws|

    ws.onopen do
      sid = nil
      fiveboard_channel = nil

    ws.onmessage do |msg|
      command, value = msg.split(":", 2);
      case command
        when 'registra'
          fiveboard_channel = @channels[value]
          sid = fiveboard_channel.subscribe { |txt| ws.send(txt) }
        when 'aggiorna'
          fiveboard_channel.push('testo:' + value)
      end
    end

    ws.onclose do
      fiveboard_channel.unsubscribe(sid)
    end
  end
end
puts "Il server è correttamente partito"
}


Seppur possiate essere non abituati a questo linguaggio il codice è tutto sommato comprensibile e
succinto, ecco la spiegazione dell'algoritmo:

      Con l'istruzione WebSocket.start(.. l'applicazione si mette in attesa di connessioni websocket
      sulla porta 8080; ogni connessione in ingresso viene memorizzata nella variabile ws e causa
      l'esecuzione delle successive istruzioni (quelle comprese nell'attiguo blocco do..end).
Alla ricezione di un messaggio attraverso una connessione ws (ws.onmessage) il server si
      comporta dividendo il testo ricevuto secondo la solita convenzione 'comando:valore' ed agendo
      in modo diverso a seconda che il comando sia 'registra' o 'aggiorna'.
      Nel caso il messaggio sia 'registra:titolo_del_documento' il server aggiungerà la
      connessione attuale ad un canale che porta il nome del valore del messaggio (in questo caso
      'titolo_del_documento'). In questo modo tutte le pagine che vorranno 'osservare' il
      documento 'A' non dovranno far altro che inviare al WebSocket Server il messaggio
      'registra:A'.
      Nel caso il messaggio sia 'aggiorna:testo_del_documento' il server si comporterà
      semplicemente inviando lungo il canale associato alla connessione corrente il valore del
      messaggio (in questo caso 'testo_del_documento'), propagandolo in questo modo a tutte le
      connessioni registrate al canale.
      Infine con l'istruzione ws.onclose do... si gestisce, in caso di disconnessione del client, la
      rimozione di ws dal canale presso il quale si era registrata.

Eseguiamo il server portandoci con il terminale nella posizione dello script e digitando:

ruby websocket_server.rb


Un messaggio, 'Il server è correttamente partito', dovrebbe confermare la bontà del nostro operato.
Dedichiamoci ora alle API Javascript ed alla loro implementazione, per prima cosa editiamo il file
'js/application.js' per fare in modo che ogni FiveBoard si registri presso il server ed invii segnali di
aggiornamento ad ogni modifica del testo, ecco il codice da inserire all'interno della funzione
window.onload:

// all'interno di window.onload, js/application.js
// queste due istruzioni sono state spostate dalla loro precedente posizione
titolo_fiveboard = prompt("Seleziona il titolo per questa FiveBoard");
document.title = "FB: " + titolo_fiveboard;

// creazione di un nuovo socket verso il server Ruby
websocket = new WebSocket('ws://0.0.0.0:8080');
websocket.onopen = function(){
  // invio del comando 'registra'
  websocket.send("registra:" + titolo_fiveboard);
}


// ad ogni variazione di input segue l'invio del comando 'aggiorna' /
/ verso il server Ruby
document.forms['form_da_ricordare'].elements['testo_da_ricordare'].oninput = function
(event){
websocket.send("aggiorna:" + event.target.value);
}


Anche in questo caso l'implementazione risulta abbastanza leggibile; unico appunto da fare
sull'evento oninput, anch'esso novità introdotta dalle specifiche HTML5, che viene invocato ad ogni
attività di input (pressione di un tasto, copia ed incolla, drag and drop,...) sull'elemento in oggetto.

Completiamo l'esempio con la creazione della semplicissima pagina 'viewer.html':

<!doctype html>
<html>
<head>
  <title>FiveBoard Viewer</title>
  <script>
    window.onload = function(){
       var documento_da_visionare = prompt("Inserisci il nome del documento che vuoi
       osservare");
       var websocket = new WebSocket('ws://0.0.0.0:8080');
       websocket.onopen = function(){
          document.title = "VB: " + documento_da_visionare;
          websocket.send("registra:" + documento_da_visionare);
       }
       websocket.onmessage = function(evento){
         nome_comando    = evento.data.split(":")[0]
         valore_comando = evento.data.substr(nome_comando.length + 1);
         switch (nome_comando){
           case 'testo':
             document.getElementById('documento_in_visione').value = valore_comando;
           break;
         }
       }
    }
  </script>
</head>
<body>
  <textarea id="documento_in_visione" readonly>Aspettando il primo aggiornamento...</
textarea>
</body>
</html>


In questo caso la pagina è istruita nel reagire alla ricezione di un messaggio da parte del WebSocket
Server; il valore ricevuto viene infatti gestito con la solita convenzione 'comando:valore' e, nel caso il
comando sia 'testo', il valore viene inserito all'interno di una textarea preposta.

Bene, eseguiamo una prova di funzionamento
(http://www.html.it/guide/esempi/html5/esempi/lezione_websockets/fiveboard/index.html):
sincerandoci di aver lanciato il WebSocket Server navighiamo prima sulla pagina 'index.html' e
creiamo un nuovo documento 'esempio1', quindi apriamo una nuova finestra e puntiamola all'indirizzo
di 'viewer.html': alla richiesta del nome del documento inseriamo anche qui 'esempio1' in modo da
legare il viewer alla fiveboard.

Ora proviamo a digitare alcuni caratteri sulla prima finestra e noteremo che, attraverso il server,
questi sono propagati in tempo reale alla seconda! Ovviamente il tutto funzionerebbe anche se la
conversazione avvenisse tra due macchine distinte attraverso un server remoto; nell'immagine
seguente si può notare una fiveboard aperta sull'iPad ed il suo corrispondente viewer visibile su di un
portatile; in alto i messaggi di log del WebSocket Server (figura 3).

Figura 3 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/3.jpg)


Conclusioni
I WebSocket, sono nella loro estrema semplicità, degli strumenti incredibilmente potenti; a riprova di
questo fatto la rete ha già incominciato ad offrire interessanti prospettive di utilizzo nonostante
l'innegabile giovinezza di queste API. Tra le soluzioni degne di nota merita sicuramente una citazione
Pusher (http://pusherapp.com/), un servizio che offre la possibilità di gestire eventi real-time
attraverso l'utilizzo di WebSocket. Un po' più artigianale, ma altrettanto valido è http://jsterm.com/
(http://jsterm.com/), un'applicazione che consente di collegarsi a server remoti utilizzando un proxy,
scritto in node.js, tra il protocollo WebSocket e Telnet.

Recentemente lo sviluppo delle API è stato frenato dalla scoperta di una seria vulnerabilità
[documento PDF] (http://www.adambarth.com/experimental/websocket.pdf) legata al potenziale
comportamento di un caching proxy che può essere indotto, attraverso l'utilizzo di WebSocket ad-hoc,
a modificare il contenuto delle informazioni consegnate ad altri client. La vulnerabilità è stata
correttamente risolta dalla versione 0.7 del protocollo ma sfortunatamente le versioni 4 e 5 di Firefox,
rilasciate prima del fix, hanno i websocket disabilitati per default; la versione 6 del browser dovrebbe
però ripristinare il funzionamento out-of-the-box di questa interessantissima feature.


Tabella del supporto sui browser

API e Web Applications


WebSockets                 No    4.0+ (parziale) 5.0+ 7.0+ 11.0+ (parziale)




                                       Drag and Drop
La soluzione al Drag and Drop proposta dalle specifiche HTML5
(http://dev.w3.org/html5/spec/editing.html#dnd) va ben oltre la metafora classica emulata in modo
eccelso (http://jqueryui.com/demos/draggable/) da librerie come JQurey e similari. Certo, anche quel
tipo di comportamento è possibile, ma stiamo solo raschiando la superficie di una feature ben più
complessa, articolata e potente. Un assaggio più consistente delle reali capacità del Drag and Drop è
offerto dalla versione HTML5 del famoso client di posta Gmail: trascinando un file al di sopra della
pagina per la composizione di un nuovo messaggio si evidenza una zona verde, rilasciando il
documento al di sopra di tale zona possiamo assistere all’upload dello stesso; ed il tutto funziona
anche con più file contemporaneamente! Prima di passare ad una analisi delle specifiche è bene
quindi capire che questo meccanismo non è stato studiato solo per riordinare liste o spostare oggetti
in un carrello ma deve essere inteso come vero e proprio sistema di input sia per informazioni interne
alla pagina sia esterne.


Le specifiche
Gli attori coinvolti in queste API sono essenzialmente tre, e non è necessario che siano tutti presenti
ad ogni implementazione. Stiamo parlando dell’oggetto che subisce il drag, della struttura che
contiene i dati che intendiamo trasferire attraverso il drop, e dell’oggetto che accetta il drop.
Ecco come funziona una sessione completa di Drag and Drop:

<ul id="elementi_da_trascinare" ondragstart="iniziaTrascinamento(event)">
   <li draggable="true" data-icona="http://bit.ly/h5Khyt" data-valore="0.5">50 Cents</l
i>
   <li draggable="true" data-icona="http://bit.ly/gnyFfh" data-valore="1" > 1 Euro </li
>
   <li draggable="true" data-icona="http://bit.ly/eXq6y5" data-valore="2" > 2 Euro </li
>
   <li draggable="true" data-icona="http://bit.ly/hiftBg" data-valore="10"> 10 Euro </l
i>
</ul>




In primo luogo è necessario l’utilizzo dell’attributo globale draggable="true" per specificare gli
elementi che vogliamo rendere trascinabili; utilizziamo inoltre i campi data-* per salvare informazioni
che potranno tornare utili più tardi, come l’url dell’icona della moneta e il suo valore numerico. Il
passo successivo consiste nel definire la funzione associata all’evento che notifica l’inizio di un
trascinamento (ondragstart), nel nostro caso l’evento è stato associato all’ul, in quanto sottende
l’intero elenco degli elementi con il comportamento drag abilitato.

<script>
  iniziaTrascinamento = function(evento){
    evento.dataTransfer.setData("text", event.target.dataset.valore);
    evento.dataTransfer.effectAllowed = 'copy';
    icona = document.createElement('img');
      icona.src = evento.target.dataset.icona;
      evento.dataTransfer.setDragImage(icona, -10, -10);
  }
</script>




Nell’argomento evento lo user-agent si premura di fornirci tutto il necessario per gestire il
trascinamento dell’elemento. Nella prima riga della funzione viene invocata la struttura
dataTransfer, atta a contenere i dati che vogliamo siano disponibili all’oggetto incaricato del drop;
con il metodo setData richiediamo poi l’inserimento di un nuovo valore all’interno di tale struttura.
Stando alle specifiche potremmo identificare questo valore con un’etichetta (usando il primo
argomento, ad esempio ‘valuta’), in questo modo saremmo poi in grado di istruire l’area di drop ad
accettare solo il rilascio di elementi con tale etichetta; nella realtà questa funzionalità non è ancora
supportata in modo sufficiente (http://code.google.com/p/chromium/issues/detail?id=31037) e
funziona solo con etichette text o url. Come secondo argomento alla chiamata specifichiamo invece il
contenuto del campo ‘data-valore’ dell’elemento che sta subendo il trascinamento.

Nella riga successiva, con l’istruzione evento.effectAllowed = 'copy' segnaliamo allo user-agent
che l’azione di drag è da intendersi come copia e non, ad esempio, come spostamento (in quel caso
sarebbe stato 'move'). È importante notare che tale segnalazione non implica di per sé nessun
cambiamento alla dinamica dello user-agent nei confronti di questo drag and drop (se non qualche
accortezza grafica, come vedremo poi) ma ci da ancora una volta la possibilità di filtrare in fase di
drop gli elementi che rilasciamo accettando, ad esempio, solamente azioni di tipo 'copy'.

Le due righe di codice seguenti servono invece per creare una immagine avente come src il campo
data-icona dell’elemento che stiamo trascinando; infine con l’ultima
istruzione,evento.dataTransfer.setDragImage(icona, -10, -10), comunichiamo al dataTransfer
di usare l’immagine appena generata come oggetto da visualizzare durante il trascinamento.

Se proviamo ad includere i due frammenti di codice in una classica struttura HTML5 e ad eseguirli con
Chromium avremo questo effetto (figura 1):

Figura 1




Bene, ora creiamo l’oggetto di drop e gestiamo l’azione di rilascio:

...
</ul>
  <img     id="portamonete"
              dropzone="copy s:text"
              draggable="false"
              src="http://bit.ly/gZH4H5"
              ondragenter="ingressoInZonaDiDrop(event)"
              ondragleave="uscitaZonaDiDrop(event)"
              ondragover="trascinamentoInZonaDiDrop(event)"
ondrop="rilascioDellOggettoTrascinato(event)"
  >
  <p> Il portamonete contiene: <output id="sommatoria">0</output> euro</p>




Così come ogni elemento HTML può essere impostato come trascinabile grazie all’attributo
draggable=true, anche il ruolo di area di drop può essere delegato a qualsiasi tag. In questo caso
abbiamo scelto di utilizzare un’immagine di un portamonete nel quale riporremo i nostri risparmi.
Diamo una scorsa agli attributi che abbiamo impostato:

          dropzone: con questo attributo, purtroppo ancora poco supportato, è possibile specificare in
          modo sintetico ed efficace le varie tipologie di dati per i quali l’area di drop è abilitata. I
          primi comandi, separati da uno spazio, indicano le tipologie di effectAllowed supportate (in
          questo caso solamente copy) mentre le restanti istruzioni, nel formato s:testo o f:testo
          rappresentano in caso di s:testole etichette supportate, o i mime-type supportati nel caso di
          f:testo. La particella s indica che il contenuto del dataTransfer deve essere di natura
          testuale (come nell’esempio che stiamo studiando), mentre la particella f implica che l’area di
          drop accetti solamente file, come ad esempio succede nel caso dell’aggiunta di allegati alla
          Gmail. Validi esempi di valorizzazione di questo attributo sono i seguenti:

-- Accetta copy per file di tipo png e jpeg
dropzone="copy f:image/png f:image/jpeg"
-- Accetta move e copy per etichette "valuta"
dropzone="move copy s:valuta"




          draggable: conosciamo già questo attributo; in ogni caso è importante segnalare che qui è
          stato utilizzato per informare lo user-agent di non rendere trascinabile questa
          immagine. Infatti nella maggioranza dei browser le immagini lo sono di default;
          ondragenter, ondragmove e ondragleave: questi tre eventi vengono invocati quando
          l’elemento viene trascinato all’interno di un’area di drop (ondragenter), mosso
          all’interno della stessa(ondragmove) e infine spostato nuovamente all’esterno dell’area
          (ondragleave);
          ondrop: l’evento chiave dell’intero processo, viene invocato al rilascio di un elemento al di
          sopra di un area di drop.

Ottimo! Non ci resta che completare questa panoramica delle specifiche osservando una possibile
implementazione degli eventi appena descritti:

...
      ingressoInZonaDiDrop = function(evento){
          evento.target.src = "http://bit.ly/gRo6cc";
          evento.preventDefault();
      }


      uscitaZonaDiDrop = function(evento){
        evento.target.src = "http://bit.ly/gZH4H5";
      }


      trascinamentoInZonaDiDrop = function(evento){
        evento.dataTransfer.dropEffect = 'copy';
evento.preventDefault();
    }

    rilascioDellOggettoTrascinato = function(evento){
        sommatoria = document.getElementById("sommatoria");
        sommatoria.innerHTML = parseFloat(sommatoria.innerHTML) + parseFloat


(evento.dataTransfer.getData("text"));
      evento.target.src = "http://bit.ly/gZH4H5";
    }
...




L’istruzione che merita maggiore attenzione è sicuramente preventDefault() che, se posizionata
all’interno di un evento, impedisce al browser di attivare il proprio comportamento di default. In
questo caso la funzione in questione è stata utilizzata in ingressoInZonaDiDrop per impedire che il
browser negasse l’accesso al drop (comportamento di default) ed in trascinamentoInZonaDiDrop per
evitare l’animazione di ‘ritorno’ degli elementi draggati al loro rilascio.

Per il resto le due funzioni associate a ondragenter e ondragleave si preoccupano solamente di
alternare, tra chiusa e aperta, l’immagine del portamonete all’ingresso (o uscita) di un elemento
trascinato al di sopra di esso. trascinamentoInZonaDiDrop deve invece preoccuparsi nuovamente di
segnalare il supporto al solo effetto copy; questo accorgimento non sarebbe necessario con un pieno
supporto dell’attributo dropzone.

Infine la funzione legata all’evento ondrop contiene l’importante metodo getData(etichetta),
indispensabile per recuperare dal dataTransfer l’ultimo oggetto inserito con l’etichetta richiesta.
Eseguiamo il tutto in Chromium notando anche come l’icona del mouse si modifichi durante l’azione di
drag and drop ad indicare la copia (figura 2):

Figura 2 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_drag/2.jpg)

Ecco la demo (http://www.html.it/guide/esempi/html5/esempi/lezione_drag/demo.html).


Un esempio
Visto che non abbiamo ancora sperimento il drag and drop di file, utilizziamo il progetto guida per
fare alcune prove; in particolare implementiamo la possibilità di rilasciare un file di testo al di
sopra di una textarea per fare in modo che in questa vi si riversi il contenuto. Questo esempio ci
darà anche l’occasione di toccare con mano la potenza delle nuove File API
(http://www.w3.org/TR/FileAPI/), in particolare approfondiremo gli oggetti Javascript File, FileList
e FileReader: ottimi compagni nel caso sia necessario accedere agli attributi o al contenuto di un file
selezionato (o trascinato) per l’upload.

Incominciamo modificando il markup del file index.html per attivare una drop area sulla textarea:

<label>Cosa hai in mente?
  <textarea name="testo_da_ricordare" required autofocus
  placeholder="La lista della spesa, il teatro di questa sera ..."
  dropzone="copy f:text/plain"
  ondragover="consentiIlDrop(event)"
  ondrop="caricaIlContenutoTestuale(event,this)">
  </textarea>
</label>
Fin qui niente di strano, la parte divertente è tutta racchiusa nella definizione delle due funzioni.
Vediamole insieme nel file 'application.js':

consentiIlDrop = function(evento){
  evento.dataTransfer.dropEffect = 'copy';
  evento.preventDefault();
}

caricaIlContenutoTestuale = function(evento,element){
  var files = evento.dataTransfer.files;
  var reader = new FileReader();
  reader.onload = function(evento){
    element.value = element.value + evento.target.result;
  }
  for(file in files){
    reader.readAsBinaryString(files[file]);
  }
}




La funzione caricaIlContenutoTestuale merita un approfondimento: il metodo files chiamato su
dataTransfer ritorna un elenco di File racchiusi in un oggetto di tipo FileList, che per i nostri
scopi è assimilabile ad un array.

Successivamente, nella variabile reader, viene caricata un istanza di FileReader, una classe
predisposta esclusivamente alla lettura del contenuto degli oggetti di tipo File. Nelle righe successive
viene impostata l’azione associata all’evento onload di reader, che viene invocato al completamento
dell’azione di lettura di un file; all’interno di questa azione il contenuto testuale del file
(evento.target.result) è concatenato al testo già presente nella textarea. L’ultimo frammento di
questa funzione cicla su tutti i file presenti in files; successivamente su ognuno di essi viene
richiesta la lettura al reader attraverso il metodo readAsBinaryString.

Anche per questo esempio abbiamo preparato una demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_drag/fiveboard/index.html). Funziona al
momento solo su Chromium. Per il test basta creare un file di testo, salvarlo (magari sul desktop) e
trascinarlo nella textarea.


Tabella del supporto sui browser

API e Web Applications

Drag and Drop                 No   3.5+ 3.2+ 2.0+ No




                                       Geolocation API


Le Geolocation Api non sono contenute all’interno dell’HTML5 ma fanno parte di quell’insieme di
documenti (http://dev.w3.org/geo/api/spec-source.html) che gravitano attorno alle specifiche; la loro
funzionalità è abbastanza semplice, definire una struttura dati atta a contenere vari dati
geospaziali e impostare alcune funzioni di accesso a tali dati. Nessuna menzione viene fatta in
merito ai meccanismi che il browser deve utilizzare per recuperare questi dati, ogni piattaforma è
infatti tenuta ad utilizzare al meglio le informazioni provenienti dal device. Su di un dispositivo mobile
di ultima generazione avremo quindi un set di coordinate provenienti dal sensore GPS, mentre su di
un portatile potremo avvalerci del posizionamento legato all’ip della connessione internet.

Per proteggere la privacy dell’utente è inoltre fatto divieto di utilizzo delle suddette informazioni senza
preventivo consenso esplicito.


Le specifiche
Esistono praticamente solo due metodi a disposizione, fra l’altro entrambi utili allo stesso scopo:
ottenere la posizione corrente. La differenza tra i due, getCurrentPosition e watchPosition, va
ricercata nella loro periodicità, mentre il primo metodo fornisce il dato una sola volta, il secondo si
attiva automaticamente ogniqualvolta la posizione cambi, ad esempio perché l’utente sta
passeggiando.

La sintassi per invocare questi metodi è la seguente:

navigator.geolocation.getCurrentPosition(inCasoDiSuccesso, opzInCasoDiErrore, opzioni)
;
navigator.geolocation.watchPosition(inCasoDiSuccesso, opzInCasoDiErrore, opzioni);




Il primo argomento contiene i riferimenti ad una funzione da eseguire al corretto recupero delle
informazioni geospaziali, il secondo argomento, opzionale, punta invece ad una funzione che verrà
invocata in caso di errore. L’ultimo parametro, anch’esso facoltativo, può essere utilizzato per
specificare alcune opzioni utilizzando una struttura {opzione1: valore1, ...}. Le opzioni disponibili
sono 3:

      enableHighAccuracy (true/false): questo flag può essere utilizzato per notificare allo user-
      agent la necessità o meno di ottenere dati il più accurati possibile. L’opzione è stata introdotta
      in quanto il recupero di informazioni più dettagliate richiede di solito maggiore dispendio di
      tempo ed energia e, in particolare su device mobile, potrebbe risultare fastidiosa per l’utente;
      timeout (millisecondi): l’opzione rappresenta il tempo massimo concesso al browser per
      recuperare la posizione dell’utente. In caso di fallimento verrà invocata la funzione associata al
      secondo argomento;
      maximuAge (millisecondi): indica al browser di effettuare una ricerca preventiva nella cache
      di un dato geospaziale non più vecchio dei millisecondi specificati. Se disponibile tale dato verrà
      restituito come posizione corrente, altrimenti verrà eseguita la procedura classica.

La funzione invocata in caso di successo deve accettare un singolo argomento, un oggetto di tipo
Position che contiene tutte le informazioni recuperate così come un timestamp delle data e ora di
recupero.

inCasoDiSuccesso = function(position){
  alert( "Posizione delle: " + position.timestamp.getHours() + ":" +
  position.timestamp.getMinutes() + "n" +
  "Accuratezza delle coordinate: " + position.coords.accuracy + " mt; n" +
  "Latitudine: " + position.coords.latitude + " gradi; n" +
  "Longitudine: " + position.coords.longitude + "gradi; n" +
"Accuratezza dell'altezza: " + position.coords.altitudeAccuracy + " mt; n" +
    "Altezza: " + position.coords.altitude + " mt; n" +
    "Direzione: " + position.coords.heading + " gradin " +
    "(0 = Nord, 90 = Ovest, 180 = Sud, 270 = Est);n" +
    "Velocita: " + position.coords.speed + " m/s;"
    );
}




Nel frammento di codice appena illustrato possiamo vedere tutte le informazioni estraibili dalla
struttura Position; chiaramente, a seconda del device sul quale viene effettuata l’interrogazione, non
tutte saranno sempre disponibili; in tal caso il loro valore sarà impostato a null. Nell’immagine
seguente il risultato dell’interrogazione eseguita dal mio portatile domestico (notate l’accuratezza!):

Figura 1 - (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_geolocation/ishot-437.png)

In caso invece si verifichi un errore la funzione preposta deve accettare anch’essa un parametro, un
oggetto di tipo PositionError contenente un codice di errore ed un messaggio ad uso di debug,
rispettivamente nei metodi code e message.

opzInCasoDiErrore = function(error){
  alert( "Errore " + error.code + ": " + error.message);
}




In ultimo ricordiamo che un invocazione del metodo watchPosition può essere successivamente
interrotta utilizzando la funzione clearWatch, come nell’esempio seguente:

<!doctype html>
<html>
<head>
    <title> Un esempio di watchPosition</title>
    <script>
var id_watch = null;

     inCasoDiSuccesso = function(position){
       document.getElementById("posizione_corrente").insertAdjacentHTML('beforeend',
       "<li> Lat: " + position.coords.latitude + ", Lon: " + position.coords.longitude +
);
         "</li>"
         );
     }

     sospendiLaRicezione = function(){
       navigator.geolocation.clearWatch(id_watch);
     }


  window.onload = function(){
    id_watch = navigator.geolocation.watchPosition(inCasoDiSuccesso);
  }
</script>
</head>
<body>
  <h1>La tua posizione attuale</h1>
  <menu type="toolbar">
    <button type="button" onclick="sospendiLaRicezione()">
       sospendi la ricezione di dati geospaziali
    </button>
  </menu>
  <ul id="posizione_corrente">
  </ul>
</body>
</html>




Sono disponibili le demo per il primo
(http://www.html.it/guide/esempi/html5/esempi/lezione_geolocation/geolocation.html) e per il
secondo esempio
(http://www.html.it/guide/esempi/html5/esempi/lezione_geolocation/geolocation_watchposition.html
).


Conclusioni
Come avete potuto notare questo set di API è contemporaneamente facilissimo da utilizzare ed
estremamente potente. Le possibilità offerte si dilatano enormemente se ci si concentra sul mercato
mobile dove la stragrande maggioranza dei device di ultima generazione è equipaggiata con ottimi
sensori GPS e bussole.


Tabella del supporto sui browser

API e Web Applications

Geolocation                 No   3.6+ 5.0+ 5.0+ 10.6+
Canvas
Una tela trasparente
Il canvas è un elemento HTML5 rappresentato dal tag <canvas>. Può essere inteso come il
corrispettivo digitale di una tela trasparente: uno spazio all'interno di una pagina web sul quale
insistere con specifiche API (http://www.whatwg.org/specs/web-apps/current-work/multipage/the-
canvas-element.html#the-canvas-element) adatte a tracciare linee, cerchi, rettangoli, immagini
e altro ancora. Il canvas è, in estrema sintesi, una grande matrice di pixel, ognuno dei quali
modificabile singolarmente nelle sue quattro componenti RGBA, rosso, verde, blu e alpha, la
trasparenza. Tutti siamo a conoscenza di questo elemento HTML5 per una sua semplice attitudine: la
possibilità di replicare i comportamenti fino ad oggi appannaggio della tecnologia Flash. In questo
articolo esploreremo le numerose API disponibili e ne sperimenteremo una buona parte per poi
concentrarci successivamente su di una interessante evoluzione del progetto guida.


Le specifiche
Prima di addentrarci nella giungla dei vari metodi a disposizione per disegnare sul canvas vediamo
brevemente l'architettura che contraddistingue il markup dell'elemento:

<canvas width="200px" height="200px" id="demo_canvas">
Contenuto da mostrare in caso il canvas non sia supportato.
(quindi anche informazioni per i motori di ricerca).
</canvas>


Gli unici due attributi disponibili, oltre ai canonici globali, sono width e height, rispettivamente
larghezza e altezza dell'elemento.

Sotto il profilo JavaScript i metodi disponibili direttamente attraverso questo elemento sono due:
toDataUrl e getContext. Il primo, toDataUrl, è fantastico nella sua semplicità: ritorna infatti il
contenuto del canvas secondo il protocollo 'data URL' (http://tools.ietf.org/html/rfc2397). Questo
schema memorizza file di ogni tipo in lunghissime sequenze di caratteri, che poi possono essere
salvati su disco o, se come nel caso del canvas siano immagini, essere utilizzate come attributo src di
tag img. Nei prossimi esempi vedremo un utilizzo tangibile di questo metodo, per ora invece
concentriamoci su getContext, vera e propria 'porta' verso le API di disegno. getContext, quando
invocato, ritorna un oggetto detto contesto del disegno:

var canvas = document.getElementById("demo_canvas");
var contesto = canvas.getContext("2d");


In questa lezione esploreremo solamente i metodi legati al contesto di disegno '2d', come specificato
dall'argomento utilizzato nello snippet, ma sappiate che esiste già una versione sperimentale di
contesto '3d' che sottende un set di API completamente diverso, basato sulle OpenGL ES 2.0 e detto
WebGL.

Ma torniamo al contesto '2d': possiamo dividere le funzionalità in esso racchiuse in categorie: path,
modificatori, immagine, testo e pixel per pixel; nelle prossime sezioni affronteremo in dettaglio
ognuno di questi aspetti. Prima di proseguire è però importante evidenziare che il contesto è un
oggetto stateful, che mantiene cioè al suo interno dei valori che influiscono sulle operazioni
successive; aspettiamoci quindi delle direttive di questo tipo:

       imposta colore di linea verde;
       imposta percorso della linea da (0,0) a (10,10);
       traccia tutti i percorsi di linea.

In questo caso, se avessimo usato le corrette API, avremmo ottenuto una linea verde tra i punti (0,0)
e (10,10). L'insieme dei valori memorizzati in un contesto viene detto drawing state.

Path

I metodi di questa categoria sono tutti funzionali al disegno di frammenti geometrici come linee, archi
e quant'altro: ecco un esempio omnicomprensivo:

disegnaBarchetta = function(contesto){
  contesto.beginPath();
  // sposta il cursore del path nella posizione 40,170
  contesto.moveTo(40,170);
  // crea un subpath linea fino alla posizione 260,170
  contesto.lineTo(260,170);
  // crea un subpath arco che sia tangente alle due linee (260,170 - 150,250)
  // (150,250 - 40,170) con diametro 150
  contesto.arcTo(150,250,40,170,150);
  contesto.lineTo(40,170);
  contesto.moveTo(150,170);
  contesto.lineTo(150,40);
  contesto.rect(150,40,60,30);
  // disegna tutti i subpath del path corrente sul canvas usando la
  // configurazione del drawing state
  contesto.stroke();
  // riempi tutti le aree inscritte dal path corrente usanto la configurazione
  // del deawing state
  contesto.fill();
}


Il concetto principe è il path, ovverosia un insieme di punti uniti fra loro da definite primitive
geometriche (arco, linea,...), dette subpath. Ogni contesto possiede solamente un path attivo per
volta. Per aggiungere un subpath al path attivo si possono utilizzare metodi come lineTo, arcTo,
rect e altri. Metodi come stroke e fill vengono applicati a tutti i subpath del path attivo. Per
resettare il path attivo, rimuovendo quindi tutti i subpath non ancora disegnati, si utilizza l'istruzione
beginPath().

Eseguiamo l'esempio appena mostrato per ottenere il risultato seguente (figura 1):

Figura 1
Oltre ai subpath mostrati ce ne sono altri, ma il meccanismo di generazione resta sempre lo stesso, la
lista completa è disponibile nel documento ufficiale delle specifiche
(http://dev.w3.org/html5/2dcontext/#complex-shapes-paths).

Modificatori

Le funzioni all'interno di questo gruppo insistono sul drawing state di un canvas, modificando il modo
in cui i path, le immagini ed i testi vengono disegnati. Nella categoria si annoverano matrici di
trasformazione (scale, rotate, translate), gestione della trasparenza globale e delle funzioni
di composizione nonché impostazioni di colore, di linea e di gradiente. Suona complicato ? Facciamo
un po' di prove:

contesto.scale(0.5,0.5);
contesto.rotate(0.5);
contesto.translate(300,-100);
disegnaBarchetta(contesto);


Ecco il risultato, i modificatori impostati vengono applicati alla barchetta (figura 2):

Figura 2
Utilizziamo ora trasparenza e composizione:

contesto.globalAlpha = 0.5;
disegnaBarchetta(contesto);
contesto.translate(40,-0);
contesto.globalCompositeOperation = "source-out";
disegnaBarchetta(contesto);


L'attributo globalAlpha definisce (da 0.0 a 1.0) il livello di trasparenza che dovrà essere applicato a
quanto verrà disegnato sul canvas. globalCompositeOperation ci permette invece di specificare in
che modo vogliamo che le immagini vengano sovrapposte: è possibile utilizzare un set di parole
chiave per indicare un ben preciso comportamento; in questo caso con source-out chiediamo allo
user agent di effettuare una somma tra le trasparenze ottenendo un risultato come questo (figura 3):

Figura 3
Attenzione, al momento non tutti i browser reagiscono allo stesso modo alle definizioni legate a
GlobalCompositeOperation. Infine esaminiamo anche le operazioni legate ai modificatori di colore:

contesto.strokeStyle = "#FF0000";
contesto.lineWidth      = "5";
var gradiente = contesto.createRadialGradient(150, 150, 30, 150, 160, 100);
gradiente.addColorStop(0.5, '#0000FF');
gradiente.addColorStop(0.1, '#000000');
contesto.fillStyle = gradiente;
disegnaBarchetta(contesto);


Con strokeStyle e fillStyle possiamo impostare le nostre preferenze su come intendiamo debba
essere disegnata la linea di contorno degli oggetti (stroke) ed il loro riempimento (fill). Entrambi gli
attributi possono essere valorizzati con stringhe, oggetti di tipo gradient ed oggetti di tipo pattern.
In questo caso è mostrato l'utilizzo di una semplice stringa di colore rosso per lo stroke e di un
gradient radiale con una variazione di colore dal nero al blu per il fill. L'utilizzo dell'oggetto pattern è
simile, il metodo da utilizzare è contesto.createPattern(immagine, opzionaleRipetizione) dove
il secondo argomento riprende le scelte effettuabili via CSS (no-repeat, repeat-x, ecc..) mentre il
primo può essere valorizzato con con un elemento img, un altro canvas o perfino un elemento video.
Ecco il risultato che si ottiene utilizzando il codice visto (figura 4):

Figura 4
Prima di passare al prossimo gruppo di funzioni citiamo brevemente anche la possibilità di
implementare un ombreggiatura, ecco le istruzioni:

contesto.shadowColor = '#003300'
contesto.shadowOffsetX = 10;
contesto.shadowOffsetY = 10;
contesto.shadowBlur = 30;
disegnaBarchetta(contesto);


Ed il risultato (figura 5):

Figura 5
Immagine
L'unico metodo appartenente a questo gruppo si chiama drawImage ed è disponibile in un certo
numero di varianti; il primo argomento può infatti essere sia un elemento immagine, sia un altro
canvas, sia un elemento di tipo video. Il risultato dell'operazione è sempre quello di disegnare sul
contesto invocante l'immagine prescelta, rispettando alcuni parametri dimensionali opzionali. Ecco un
esempio:

var immagine = new Image();
immagine.src = "http://img227.imageshack.us/img227/7092/marei.jpg";
contesto.drawImage(immagine,0,0);
disegnaBarchetta(contesto);


E il risultato (figura 6):

Figura 6
In questo caso chiediamo che l'immagine sia disegnata a partire dall'angolo in alto a sinistra del
canvas (coordinate 0,0); possiamo però anche specificare una dimensione di destinazione e perfino
un rettangolo di partenza in modo da prelevare solo una porzione di immagine utilizzando i numerosi
argomenti opzionali messi a disposizione dalle specifiche.


Testo
Come il nome del gruppo suggerisce, questi metodi permettono di scrivere porzioni di testo
all'interno di un canvas, eccone un esempio:

disegnaBarchetta(contesto);
contesto.globalCompositeOperation = "source-out";
contesto.font = "34px Georgia"
contesto.strokeText("Va va va barchetta", 20, 100);
contesto.fillText("va, naviga naviga naviga", 20, 140);
contesto.fillText("naviga e mai si", 20, 180);
contesto.fillText("fermera'...", 20, 220);


L'attributo font può essere valorizzato allo stesso modo del suo omonimo su CSS, per il resto
fillText provvede a scrivere il testo desiderato partendo dalla posizione x, y specificata come
secondo e terzo argomento; anche strokeText si comporta in modo simile ma con una piccola
variante che si può facilmente evincere dal risultato dell'esecuzione dell'esempio. Da notare inoltre il
comportamento interessante dell'applicazione di un particolare tipo di composizione (figura 7):

Figura 7
Pixel per Pixel
Come già anticipato in precedenza, il canvas non è nient'altro che una matrice di valori in formato
RGBA: non stupisce che sia quindi possibile insistere su ogni singolo e specifico pixel della
stessa; ecco un esempio:

disegnaBarchetta(contesto);
var dati_immagine = contesto.getImageData(0,0,
  contesto.canvas.width, contesto.canvas.height);


var array_dati_immagine = dati_immagine.data;
for(var i = 0; i < array_dati_immagine.length; i +=4 ){
  array_dati_immagine[i ] = Math.round(Math.random() * 255);
  array_dati_immagine[i+1] = Math.round(Math.random() * 255);
  array_dati_immagine[i+2] = Math.round(Math.random() * 255);
}
dati_immagine.data = array_dati_immagine;
contesto.canvas.width = contesto.canvas.width;
contesto.putImageData(dati_immagine, 0,0);


Il fulcro dello snippet è da ricercarsi nelle due funzioni getImageData e putImageData che
rispettivamente prelevano e 'stampano' sul canvas una porzione di immagine delineata dagli
argomenti passati. Tale porzione, un oggetto di classe ImageData, possiede in una sua proprietà,
data, un lunghissimo array contenente le componenti RGBA di ogni pixel. Il ciclo for presentato nel
codice precedente insiste proprio su questo array assegnando ad ognuna delle tre componenti RGB un
valore randomico. Ecco il risultato (figura 8):

Figura 8
Per una verifica diretta di quanto visto nel corso della lezione è disponibile questa demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_canvas/index.html).

Ora che abbiamo almeno un'idea delle potenzialità dello strumento che stiamo analizzando,
accingiamoci ad una sperimentazione pratica attraverso il progetto guida. Lo vedremo nella prossima
lezione.


Tabella del supporto sui browser

Grafica e Multimedia

<canvas>                9.0+ 2.0+ 3.1+ 2.0+ 10.0+




                            Canvas: un esempio pratico
Un esempio
Ora che abbiamo almeno un'idea delle potenzialità dello strumento che stiamo analizzando,
accingiamoci ad una sperimentazione pratica attraverso il progetto guida. Quello che faremo sarà
dare la possibilità agli utenti della fiveboard di disegnare utilizzando un canvas, lo stesso verrà poi
memorizzato insieme al testo sul già noto localStorage. Introduciamo anche in questo contesto il
concetto diEventoCustom, la possibilità cioè di lanciare arbitrariamente eventi in tutto e per tutto
simili a quelli utilizzati dal DOM. Questa feature ci sarà utilissima per mantenere un codice elegante e
contemporaneamente sincronizzare salvataggio e recupero di testo e canvas dal localStorage;
aggiungiamo ad application.js il seguente metodo:
lanciaEvento = function(nome_evento){
  var evento = document.createEvent("CustomEvent");
    evento.initCustomEvent(nome_evento, true, true, titolo_fiveboard);
    document.dispatchEvent(evento);
}




Eliminiamo successivamente dallo stesso file le funzioni salvaIlDato e recuperaIlDato sostituendole
con le seguenti:

salvaAppunti = function(evento){
  localStorage.setItem("fb_" + evento.detail,
     document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value
  );
  alert("Appunti salvati");
}

recuperaAppunti = function(evento){
  document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value =
localStorage.getItem("fb_" + evento.detail);
  alert("Appunti recuperati");
}

document.addEventListener('salvadato', salvaAppunti);
document.addEventListener('recuperadato', recuperaAppunti);




Bene, ora abbiamo creato le due funzioni e le abbiamo registrate rispettivamente agli eventi custom
‘salvadato’ e ‘recuperadato’; ora non ci resta che scatenare questi eventi al click dei bottoni del file
index.html, modifichiamo quindi come segue:

...
  <menu type="toolbar">
    <button type="button" onclick="lanciaEvento('salvadato');">
        Memorizza quanto scritto
      </button>
      <button type="button" onclick="lanciaEvento('recuperadato');">
      Recupera l'ultimo testo memorizzato
    </button>
  </menu>
...




Ottimo! Ora possiamo aggiungere un canvas poco prima della fine della pagina e procedere con il
nostro esperimento:

...
  <canvas id="drawboard"></canvas>
  </form>
...
In un file chiamato canvas.js, sempre nella cartella js, scriviamo:

var ctx   = null;
var started     = false;

iniziaDisegno = function(evento){
  ctx.beginPath();
  ctx.moveTo(evento.offsetX,evento.offsetY);
  started = true;
}

disegna = function(evento){
  if(started){
    ctx.lineTo(evento.offsetX,evento.offsetY);
    ctx.stroke();
  }
}

fermaDisegno = function(evento){
  ctx.closePath();
  started = false;
}

salvaCanvas = function(evento){
   localStorage.setItem("canvas_fb_" + evento.detail, ctx.canvas.toDataURL('image/png')
);
   alert("Canvas salvato");
}

recuperaCanvas = function(evento){
  var immagine_salvata = localStorage.getItem("canvas_fb_" + evento.detail);
    if(immagine_salvata == null) return;
    var img = new Image();
    img.src = immagine_salvata;
    ctx.canvas.width = ctx.canvas.width;
    ctx.drawImage(img, 0, 0);
    alert("Canvas recuperato");
}


attivaIlCanvas = function(evento){
  ctx = document.querySelector('canvas').getContext('2d');
  ctx.canvas.addEventListener('mousedown' , iniziaDisegno,false );
  ctx.canvas.addEventListener('mousemove' , disegna     ,false );
    ctx.canvas.addEventListener('mouseup' ,   fermaDisegno ,false );
    ctx.canvas.addEventListener('mouseleave', fermaDisegno ,false );
    document.addEventListener('salvadato', salvaCanvas    );
    document.addEventListener('recuperadato', recuperaCanvas      );
}
window.addEventListener('load' ,attivaIlCanvas,false);




Approfondiamo un po’ quanto scritto: la funzione appena qui sopra ‘attivaIlCanvas’ viene eseguita
al load della pagina e si preoccupa di registrare tutti gli handler necessari alla gestione delle funzioni
di disegno e di registrazione. In particolare iniziaDisegno, disegna e fermaDisegno utilizzano alcune
funzioni che abbiamo esplorato nelle specifiche per disegnare delle spezzate che seguono la posizione
del mouse. salvaCanvas e recuperaCanvas sono invece funzioni che vengono attivate dai nostri
eventi custom; in questo caso la parte interessante è da ricercarsi nell’utilizzo del metodo toDataUrl
che trasforma il contenuto del canvas in una stringa, rendendolo quindi memorizzabile all’interno
dell’hash localStorage. In recuperaCanvas poi, la stessa stringa contenente l’immagine viene
utilizzata come attributo src di un oggetto Image e successivamente ridipinta sul canvas.

Ricordiamoci di richiedere il file canvas.js all’interno della pagina index.html e proviamo la nostra
creazione (http://www.html.it/guide/esempi/html5/esempi/lezione_canvas/fiveboard/index.html)
(figura 1):

Figura 1




Conclusioni
Il canvas è uno strumento incredibilmente vasto, potente ed articolato. La panoramica offerta da
questa lezione introduce in modo abbastanza esauriente l’insieme delle tematiche anche se per non
dilungarsi troppo non ne approfondisce nessuna. Nonostante questa evidente ricchezza il canvas resta
comunque uno strumento di basso livello e permane in uno stato di difficile utilizzo a meno che non
sia supportato dai dovuti framework, come ad esempio processing.js (http://processingjs.org/),
canto.js (http://www.davidflanagan.com/2010/07/cantojs-an-impr.html) e l’ottimo Easel.js
(http://easeljs.com/). In questa lezione si è inoltre volutamente ignorato il tema dell’interazione tra
canvas e video, che verrà affrontato nel dovuto dettaglio nella prossima lezione di questa guida.




                                                Video


L'introduzione della possibilità di riprodurre filmati in un browser senza l'ausilio di plug-in di terze
parti rappresenta di per sé una grossa novità: vanno ad esaurirsi infatti tutte le tematiche legate alla
proliferazione del plug-in utilizzato ed alla portabilità dello stesso (basti pensare ai mille grattacapi
generati dal connubio Flash - iOs).

Ma c'è molto di più! Portando il player video all'interno del DOM si sono rese disponibili tutta una serie
di interessantissime possibilità di interazione: ad esempio il tag video può essere modificato
utilizzando i CSS; lo stesso effetto può essere poi ovviamente applicato anche ai pulsanti di controllo.
Inoltre l'elemento espone una ricca interfaccia Javascript che ci consente di effettuare senza alcune
fatica moltissime efficaci manipolazioni. Infine ricordiamo che il canvas, trattato nella lezione
precedente, ha la facoltà di recuperare fotogrammi da un elemento video e di manipolarli; più avanti
mostreremo questa potente tecnica in azione.


Le specifiche
Prima di iniziare la sperimentazione è necessario procurarsi dei video da utilizzare: per queste demo
abbiamo scelto il trailer del cartone 'Big Buck Bunny', distribuito in licenza Creative Commons e
disponibile per il download all'indirizzo http://www.webmfiles.org/demo-files/
(http://www.webmfiles.org/demo-files/) e dal sito ufficiale
(http://www.bigbuckbunny.org/index.php/trailer-page/); l'intero esempio e tutti i file necessari sono
allegati al pacchetto zip (http://www.html.it/guide/esempi/html5/esempi/esempi_html5_api.zip) che
contiene tutti gli esempi della guida.

Incominciamo dal markup, ecco un esempio di implementazione:

<video poster="big_buck_bunny/poster.jpg" controls>
  <source src="big_buck_bunny/trailer.mp4" type="video/mp4" >
  <source src="big_buck_bunny/trailer.ogg" type="video/ogg" >
  <source src="big_buck_bunny/trailer.webm" type="video/webm">
</video>


L'attributo poster ci consente di specificare una immagine che verrà utilizzata all'interno dell'area di
riproduzione prima che il video venga eseguito; controls invece indica che richiediamo la presenza
dei classici controlli, come play, pausa, volume, barra di avanzamento e quant'altro. Ci sono altri
attributi utilizzabili a livello di video, vediamoli:

      autoplay: se presente questa stringa di testo indica allo user agent di provvedere alla
      riproduzione del video appena si raggiungono le condizioni minime di buffer necessarie;
      loop: se il browser rileva questa stringa inizierà nuovamente la riproduzione del filmato una
      volta che questo è giunto al termine;
      preload: per questo attributo sono previste tre opzioni: none, metadata e auto. Con none si
      indica allo user-agent di non effettuare nessun download preventivo del contenuto del
      file video, il buffer inizierà quindi ad essere riempito alla prima pressione del tasto play; con
      metadata invece si richiede che vengano recuperate almeno le informazioni essenziali,
come la durata del filmato e le sue dimensioni. Infine con auto, che è anche la valorizzazione
       di default, si lascia al browser totale libertà di scelta;
       audio: possiamo valorizzare questo attributo a 'muted' ottenendo in questo modo che il player
       video non riproduca nessun suono. Nell'ultima versione delle specfiche la proprietà è stata
       sostituita da un più consono muted che deve essere valorizzato a true

Passiamo a source e al grande dilemma che questi elementi sottendono: i codec. Ogni elemento di
questo tipo infatti indica al browser la disponibilità dello stesso contenuto video in formati di codifica
diversi. Ad oggi non esiste un singolo codec che sia supportato dai maggiori browser in commercio: la
scelta più compatibile in assoluto è il nuovissimo, e open-source, formato .webm/ vp8, che però non
è ben accetto da Safari (e quindi da iPhone et-simila), per il quale la scelta obbligata è invece il
classico .mp4 con codifica h264. Per ogni source è necessario quindi specificare un attributo src,
un type ed opzionalmente anche un codec, da valorizzare con una lista di stringhe contenente il
dettaglio della codifica audio e video, separati dalla virgola.

Eseguendo il primo esempio otterremo questo risultato (figura 1):

Figura 1 (click per ingrandire)


            (http://www.html.it/guide/esempi/html5/imgs/lezione_video/1.png)

Ecco la demo (http://www.html.it/guide/esempi/html5/esempi/lezione_video/video.html).

Ma non è finita. Insieme agli elementi di tipo source possiamo anche specificare tag track che
rappresentano vari elementi testuali che si possono combinare con il video in modi diversi.
Sono un esempio di track sottotitoli, tracce, che contengono testo ed onomatopee di effetti speciali, e
sono di solito utilizzate dai non udenti, descrizioni, che sono invece composte da testo predisposto per
la sintesi audio da parte di sintetizzatori vocali, di solito utilizzati da persone non vedenti, capitoli ed
altri dati:

<video poster="big_buck_bunny/poster.jpg" controls>
  <source src="big_buck_bunny/trailer.mp4" type="video/mp4" >
  <source src="big_buck_bunny/trailer.ogg" type="video/ogg" >
  <source src="big_buck_bunny/trailer.webm" type="video/webm">
  <track kind="subtitles" src="big_buck_bunny/bunny.it.vtt"
  srclang="it" label="Sottotitoli in Italiano">
</video>


Le specifiche legate all'elemento in questione non sono però, ad oggi, supportate da nessuno dei
principali browser in commercio; la stessa estensione di file (.vtt) proposta dal W3C come standard
per i sottotitoli è praticamente sconosciuta e priva di documentazione.


Le API
Parallelamente al markup sono state introdotte tutta una serie di API utilissime per interfacciarsi con
oggetti video. Iniziamo dai più semplici, intuitivi ed utili:

<!doctype html>
<html>
<head>
  <title>Big Buck Bunny, il trailer</title>
  <script>
    var video;
eseguiIlPlay = function(){
      video.play();
    }
    eseguiIlPause = function(){
      video.pause();
    }
    cambiaVolume = function(evento){
       video.volume = evento.target.value;
    }
    window.addEventListener('load',function(e){
       video = document.querySelector('video');
    })
  </script>
</head>
<body>
  <menu type="toolbar">
    <button type="button" onclick="eseguiIlPlay()" > Play </button>
    <button type="button" onclick="eseguiIlPause()"> Pause </button>
    <label> Volume: </label>
    <input type="range" min="0.0" max="1.0"
    step="0.1" value="0.5" oninput="cambiaVolume(event)">
  </menu>
  <video poster="big_buck_bunny/poster.jpg">
    <source src="big_buck_bunny/trailer.mp4" type="video/mp4" >
    <source src="big_buck_bunny/trailer.ogg" type="video/ogg" >
    <source src="big_buck_bunny/trailer.webm" type="video/webm">
  </video>
</body>
</html>


Come potete notare, il controllo base del flusso e del volume è alquanto semplice, esistono però
alcune funzioni più particolari; ad esempio la proprietà playbackRate di un oggetto video può essere
valorizzata con cifre che indicano quale vogliamo che sia la velocità di riproduzione.

Interessanti sono anche i metodi che gravitano attorno alle tematiche temporali: duration,
currentTime e initialTime ritornano rispettivamente la durata in secondi del filmato, la posizione
corrente e la posizione dalla quale è partita la riproduzione (tipicamente 0). Il metodo currentTime
può essere inoltre valorizzato causando uno spostamento della posizione del video al secondo
richiesto. Ecco due funzioni che mostrano rispettivamente come raddoppiare la velocità di
riproduzione e modificare la posizione corrente esattamente a metà della lunghezza del video:

doppiaVelocita = function(){
  video.playbackRate = 2.0;
}
posizionatiAlCentro = function(){
  video.currentTime = Math.round(video.duration / 2);
}


Per attivarle aggiungiamo due pulsanti nella toolbar:

<menu type="toolbar">
...
  <button type="button" onclick="doppiaVelocita()"> x2 </button>
  <button type="button" onclick="posizionatiAlCentro()"> Centro </button>
  ...
</menu>


Eseguiamo il codice finora prodotto per testare la bontà di quanto sviluppato (figura 2):

Figura 2 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_video/2.png)

L'ultima parte del capitolo riguardante le API di questo elemento è dedicata ad una classe
interessante denominata TimeRanges e composta da un attributo: length e due metodi start(n) e
end(n). Alcuni metodi dell'elemento video ritornano oggetti di questo tipo che contengono
informazioni in merito a collezioni di intervalli temporali; ecco un esempio (aggiungiamolo alla demo
precedente):

generaTesto = function(intervalli){
  var testo = "Ci sono: " + intervalli.length + " intervalli";
  for(var i=0; i < intervalli.length; i++){
    testo +="ntda: " + intervalli.start(i) +
        " a:" + intervalli.end(i) + " secondi"
  }
  return testo;
}
ottieniInformazioni = function(){
  var caricato = video.buffered;
  var riprodotto        = video.played;
  var ricercabile = video.seekable;
alert( "Alcune informazioni sul video n" +
      "= Caricato =n" + generaTesto(caricato) + "n" +
         "= Riprodotto =n" + generaTesto(riprodotto) + "n" +
         "= Ricercabile =n" + generaTesto(ricercabile)
    );
}


Creiamo il bottone corrispondente, lanciamo la demo e sinceriamoci dell'effettivo funzionamento:

<button type="button" onclick="ottieniInformazioni()"> Info </button>


Nello screenshot in figura 3 possiamo verificare il risultato:

Figura 3 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_video/3.png)

Anche in questo caso è disponibile una demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_video/video_api_1.html).


Gli eventi
L'elemento video comunica attraverso un elenco lunghissimo di eventi, i principali sono facili e
facilmente comprensibili, come ad esempio play, playing, pause e waiting; per la lista completa
rimandiamo alle specifiche W3C (http://dev.w3.org/html5/spec/Overview.html#mediaevents).


MediaController API
Solo un piccolo accenno ad un set di API di recentissima pubblicazione; grazie alle MediaController
API e con l'ausilio di un nuovo attibuto mediagroup, disponibile sia per elementi audio che video, è
possibile chiedere allo user agent di gestire la riproduzione di più oggetti multimediali in modo
sincronizzato. Facciamo un esempio:

<video poster="big_buck_bunny/poster.jpg" controls mediagroup="anteprime">
    <source src="big_buck_bunny/trailer.mp4" type="video/mp4" >
    <source src="big_buck_bunny/trailer.ogg" type="video/ogg" >
    <source src="big_buck_bunny/trailer.webm" type="video/webm">
</video>
<video poster="big_buck_bunny/poster.jpg" controls mediagroup="anteprime">
    <source src="mixer/movies/src/ballo_green_screen.mp4" type="video/mp4" >
    <source src="mixer/movies/src/ballo_green_screen.ogg" type="video/ogg" >
    <source src="mixer/movies/src/ballo_green_screen.webm" type="video/webm">
</video>



in questo caso, avendo lo stesso valore per l'attributo mediagroup, entrambi i video vengono associati
allo stesso oggetto di tipo MediaController generato a runtime dal browser. Questo oggetto diventa
quindi responsabile della gestione temporale dei due filmati e ne coordina la riproduzione in sincrono.
Per accedere a questo oggetto la sintassi è estremamente semplice:

// ritorna il MediaController associato al video, se presente
document.querySelector("video").controller


così come semplici sono i metodi e gli eventi disponibili in quanto coincidono quasi completamente
con quelli dei MediaElements (es: play, pause, volume e tutto quanto visto in questa lezione).

Purtroppo nessun browser ad oggi implementa questo promettente set di API ma, visto l'interesse sul
tema, l'attesa non dovrebbe rivelarsi troppo lunga.




                      Video e Canvas: un esempio pratico


In questa lezione vedremo come manipolare flussi video in tempo reale con l'ausilio di un canvas.
Perché il tutto funzioni è necessario che sia il video che la pagina che costruiremo risiedano sullo
stesso dominio. Questo per prevenire l'intervento di alcune policy di sicurezza del browser che
impediscono di fatto la modifica di materiale multimediale che non provenga dalla stessa origine della
pagina. Ecco uno schema di quanto realizzeremo:

Figura 4 (click per ingrandire)
(http://www.html.it/guide/esempi/html5/imgs/lezione_video/4.png)

Utilizzeremo due canvas. Nel primo replicheremo i fotogrammi del video, mentre nel secondo
andremo a inserire il risultato della nostra elaborazione sull'array di pixel estratti dal primo. Il
procedimento sarà quindi il seguente:

       Avviare la riproduzione del video;
       Utilizzare la funzione drawImage per disegnare sul primo canvas il contenuto del fotogramma
       corrente del video;
       Con la funzione getImageData recuperare dal canvas il fotogramma precedentemente
       disegnato, questa volta però come array di pixel modificabili;
       Effettuare un ciclo su tutti i pixel recuperati apportando le modifiche volute;
       Utilizzare la funzione putImageData per disegnare l'array di pixel modificato sul secondo
       canvas;
       Ripetere dal secondo punto la procedura.

Ecco il codice necessario:

<!doctype html>
<html>
<head>
    <title> Canvas e Video </title>
    <script>
        decomposizioneColori = function(video, context, context_nascosto, colori){
               if(video.paused || video.ended) return false;
               context_nascosto.drawImage(video,0,0);
               var fotogramma = context_nascosto.getImageData(0,0,
                   context_nascosto.canvas.width,context_nascosto.canvas.height);
               var fotogramma_data = fotogramma.data;
               var rosso = colori.rosso.checked;
               var verde = colori.verde.checked;
               var blu       = colori.blu.checked;
for(var i=0; i < fotogramma_data.length; i+=4){
                  if (!rosso) fotogramma_data[i ] = 0;
                  if (!verde) fotogramma_data[i+1] = 0;
                  if (!blu ) fotogramma_data[i+2] = 0;
              }
              fotogramma.data = fotogramma_data;
              context.putImageData(fotogramma,0,0);
              setTimeout(function(){
                  decomposizioneColori(video, context, context_nascosto, colori)
              },0);
          }

        aspettaIlPlay = function(evento){
            var video            = document.querySelector('video');
            video.addEventListener("play", function(evento){
                var context          = document.querySelector('#canvas_principale').ge
tContext('2d');
                var context_nascosto = document.querySelector('#canvas_elaborazione').
getContext('2d');
                context.canvas.width = context_nascosto.canvas.width = video.clientWid
th;
                context.canvas.height = context_nascosto.canvas.height = video.clientH
eight;
                decomposizioneColori(evento.target,context,context_nascosto, document.
forms.colori);
            },true);
        }

          window.addEventListener("load",aspettaIlPlay,true);

    </script>
</head>
<body>
    <canvas id="canvas_principale"></canvas>
    <canvas id="canvas_elaborazione" style="display:none;"></canvas>
    <video poster="big_buck_bunny/poster.jpg" controls>
        <source src="big_buck_bunny/trailer.mp4" type="video/mp4" >
        <source src="big_buck_bunny/trailer.ogg" type="video/ogg" >
        <source src="big_buck_bunny/trailer.webm" type="video/webm">
    </video>
    <form name="colori">
        <fieldset>
            <legend>Usa le checkbox per controllare i tre canali (RGB)</legend>
            <label> Rosso <input type="checkbox" name="rosso" checked></label>
            <label> Verde <input type="checkbox" name="verde" checked></label>
            <label> Blue <input type="checkbox" name="blu" checked></label>
        </fieldset>
    </form>
</body>
</html>
Il funzionamento si concentra in decomposizioneColori; qui vengono eseguite tutte le operazioni
che abbiamo citato. All'interno del ciclo che scorre tutti i pixel (nelle componenti RGBA) un semplice
controllo pilotato da una form 'spegne' le componenti di colore corrispondenti ad una checkbox non
spuntata con un risultato simile a quello dell'immagine seguente:

Figura 5




Merita un cenno anche l'interessante uso della funzione requestAnimationFrame
(http://www.w3.org/TR/animation-timing/); il W3C ha predisposto questo metodo per meglio
coordinare la gestione di animazioni all'interno di documenti web. A differenza dei suoi 'cugini'
setTimeout e setInterval questa funzione applica alcune tecniche per ottimizzare le perfomance e
mantenere basso l'utilizzo delle risorse del sistema. Purtroppo la maggior parte dei browser la
considera ancora sperimentale e quindi antepone il proprio prefisso (moz, o, ms o webkit). Per
risolvere questo problema esistono alcuni snippet di codice come quello proposto da Paul Irish
(http://paulirish.com/2011/requestanimationframe-for-smart-animating/).

Possiamo verificare il tutto nella demo conclusiva
(http://www.html.it/guide/esempi/html5/esempi/lezione_video/video_canvas.html).


Conclusioni
In queste ultime due lezioni abbiamo esplorato tutto l'ecosistema che circonda la nascita del tag
video. Prima di passare alla prossima lezione ricordiamo che intorno a questo elemento già si
affacciano le prime librerie, capaci di aiutare nella personalizzazione del player e nella gestione di
fallback su altre tecnologie nel caso l'HTML5 non sia supportato. In particolare segnaliamo l'ottimo
video.js (http://videojs.com/), che sicuramente merita una visita di approfondimento.




                                                Audio


La presenza del tag audio è naturale conseguenza dell'esistenza di video
(http://xhtml.html.it/guide_preview/lezione/5003/video/). Fra l'altro i due elementi si assomigliano
molto, sia in materia di markup che di API, al punto che nelle specifiche sono raggruppati all'interno
della definizione di HTML Media Elements. L'utilizzo del tag audio sottende però aspetti
assolutamente originali ed interessanti; soprattutto quando utilizzato per arricchire con effetti sonori
una struttura HTML classica.


Le specifiche
La rappresentazione a markup dell'elemento ricalca perfettamente quella del video, anche se vi sono
meno attributi disponibili, vediamo un esempio:

<audio controls autoplay loop src="file_musicale.mp3">


In questo caso la definizione si risolve su di una singola linea e senza utilizzo del tag source perché è
stato utilizzato l'attributo src, il cui compito è quello di evitare una scrittura troppo prolissa nel caso
in cui il formato disponibile sia unico. Questo attributo anche se non è stato presentato nella lezione
precedente è valido anche per il tag video, anche se a meno di un reale processo di unificazione dei
codec ad oggi perde molta della sua utilità. In ogni caso la stessa definizione appena espressa si può
scrivere anche nel formato esteso:

<audio controls autoplay loop>
  <source src="file_musicale.mp3" type="audio/mp3">
</audio>


Oltre agli attributi presentati nel codice qui sopra, deve essere citato anche preload; ognuno di
questi è già stato illustrato nella lezione precedente e mantiene intatto il suo funzionamento anche su
questo elemento. A differenza di video, l'elemento audio dispone anche di un proprio costruttore
JavaScript, ecco la sintassi:

var audio = new Audio("file_musicale.mp3");


In questo modo è possibile utilizzare musiche ed effetti audio senza necessariamente creare
l'elemento HTML né ricorrere a display:none o similari; tratteremo più approfonditamente di questo
aspetto nella sezione dedicata all'esempio.

Per quanto riguarda invece le API e gli eventi associati a questo elemento l'elenco è pressoché
congruente con quello dell'elemento video; le stesse funzioni play, pause, playbackRate,
duration,currentTime e tutto quanto legato ai TimeRanges mantengono inalterato il proprio
comportamento in quanto appartenenti al gruppo HTMLMediaElements al quale entrambi gli elementi
fanno parte.


Un esempio
Sfruttiamo la possibilità di creare oggetti audio utilizzando il costruttore JavaScript per studiare un
meccanismo che ci consenta di associare ad ogni bottone un suono predefinito da riprodurre al click.
Ecco il markup che useremo all'interno del body della pagina:

<button type="button" data-effetto-audio="gatto_che_miagola.mp3" disabled>
  Il verso del gatto...
</button>
<button type="button" data-effetto-audio="cane_che_abbaia.mp3" disabled>
  Il verso del cane...
</button>


L'utilizzo degli attributi data-* ci consente in modo elegante di aggiungere un'informazione
caratterizzante al pulsante, senza creare strane sovrastrutture. Ora il codice JavaScript dovrà
occuparsi di scandagliare la pagina alla ricerca di pulsanti con suono associato e creare un oggetto
audio con la sorgente che punti all'attributo che abbiamo definito. L'evento canplaythrough, verrà
utilizzato per intercettare il momento in cui un dato oggetto audio ritiene di aver collezionato dati
sufficienti per garantire la riproduzione dell'intero brano. All'accadere di tale evento il pulsante
associato al suono verrà abilitato e un listener in ascolto sull'evento click garantirà l'esecuzione del
suono alla pressione del bottone. Ecco il codice della demo completa:

<!doctype html>
<html>
<head>
  <title>Il suono degli animali</title>
  <script>
    init = function(evento){
       var bottoni = document.querySelectorAll('button');
       for(var b=0; b < bottoni.length; b ++){
         var effetto_audio = bottoni[b].dataset.effettoAudio;
         if(effetto_audio != ""){
            var audio = new Audio(effetto_audio);
            audio.bottone_di_riferimento = bottoni[b];
            audio.addEventListener('canplaythrough',
              function(evento_load){
                var bottone = evento_load.target.bottone_di_riferimento;
                bottone.disabled = false;
                bottone.audio_di_riferimento = evento_load.target;
                 bottone.addEventListener('click',
                   function(evento_click){
                     evento_click.target.audio_di_riferimento.play();
                   }
);
                    }
               );
           }
       }
    }
window.addEventListener('load',init); </script>
</head>
<body>
<button type="button" data-effetto-audio="gatto_che_miagola.mp3" disabled>
Il verso del gatto...
</button>
<button type="button" data-effetto-audio="cane_che_abbaia.mp3" disabled>
Il verso del cane...
</button>
</body>
</html>




Il codice sembra a prima vista complicato ma è facilmente decomponibile in 3 fasi principali:

      Un ciclo for viene eseguito al caricamento della pagina su tutti gli elementi di tipo button; per
      ognuno di questi elementi viene istanziato un oggetto Audio. Su tale oggetto viene
      memorizzato un riferimento al bottone associato tramite la variabile bottone_di_riferimento
      e viene registrato un handler sull'evento canplaythrough.
      Quando un oggetto Audio colleziona un buffer di dati ritenuto dal browser tale da consentire la
      riproduzione integrale del suono, l'evento canplaythrough viene lanciato ed intercettato dalla
      nostra funzione che a quel punto crea, con un meccanismo simile al punto precedente, un
      handler sul pulsante presente nella variabile bottone_di_riferimento da attivarsi alla
      pressione dello stesso. Inoltre il pulsante viene abilitato (bottone.disabled=false) in modo
      da poter ricevere i click degli utenti.
      Il click dell'utente su di un pulsante attiva l'handler appena definito che non fa altro che
      riprodurre il suono presente nell'oggetto Audio ad esso associato.

Eseguiamo l'applicazione appena sviluppata e sinceriamoci del suo corretto funzionamento: ecco
l'esempio (http://www.html.it/guide/esempi/html5/esempi/lezione_audio/audio.html).


Conclusioni
L'elemento che abbiamo illustrato in questa sezione corre di pari passo con l'implementazione HTML5
del video, dal quale, abbiamo visto, eredita pressoché la totalità delle proprietà. Nonostante questo
l'utilizzo dell'elemento audio si presta a scenari molto più variegati rispetto a quanto ci si potrebbe
immaginare: parte di questa interessante flessibilità risiede proprio nella sua predisposizione ad
essere creato direttamente da JavaScript.


Tabella del supporto sui browser

Grafica e Multimedia

<audio>                      9.0+ 3.5+ 4.0+ 5.0+ 10.5+

Codec audio
MP3            Sì    No     Sì    Sì    No
Wav            No    Sì     Sì    No    Sì
AAC            Sì    No     Sì    Sì    No
Ogg/Vorbis     No    Sì     No    Sì    Sì




                                         SVG e MathML
SVG
SVG è un acronimo che significa Scalable Vector Graphic e indica una specifica modalità di definire
elementi di natura grafica attraverso una sintassi XML, ecco un esempio:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="300px" height="300px" viewbox="0 0 300 300"
  xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Una barchetta in SVG</title>
  <desc>La stessa barchetta della lezione sui canvas ora in SVG</desc>
  <path d="M40,170 h220 A150,150 0 0,1 40,170 h110 v-130 v130 h-110 z"
    fill="black" stroke="black" stroke-width="1"/>
  <rect x="150" y="40" width="60" height="30"
    fill="black" stroke="black" stroke-width="1"/>
</svg>




La maggior parte dei browser in circolazione interpreta correttamente lo standard SVG, se proviamo a
visualizzare questa pagina
(http://www.html.it/guide/esempi/html5/esempi/lezione_svg/barchetta.svg) in Chrome otterremo
questo risultato (figura 1):

Figura 1




Nonostante il risultato sia a prima vista identico rispetto alla barchetta disegnata all’interno della
lezione sull’elemento canvas, si può notare come a livello di codice ci sia una enorme differenza:
mentre sul canvas è necessario insistere con istruzioni JavaScript che pilotano la creazione del
disegno, qui ci troviamo di fronte ad un linguaggio XML fatto per descrivere l’immagine. Ma non è
finita: da un lato (canvas) stiamo lavorando su di una matrice di pixel, che quindi non può essere
ridimensionata senza subire perdite di qualità; dall’altro, con SVG, operiamo con grafica vettoriale,
che preserva in modo assoluto lo stesso risultato a prescindere dalle operazioni di trasformazione
applicate ad essa.


MathML
Anche MathML è un acronimo: Mathematical Markup Language; il fine di questa sintassi è quello
di rendere possibile la visualizzazione a schermo di formule complesse, senza ricorrere al
classico espediente dell’utilizzo di immagini create a partire da screenshot di applicazioni scientifiche.
Anche in questo caso stiamo parlando di sintassi XML-like. A seguire un esempio della famosa
equazione di Einstein secondo queste specifiche:

<?xml version="1.0" encoding="UTF-8"?>

<math xmlns="http://www.w3.org/1998/Math/MathML">
  <semantics>
    <mrow>
      <mi>E</mi>
      <mi mathvariant="normal">=</mi>
      <msup>
        <mi mathvariant="italic">mc</mi>
        <mn>2</mn>
      </msup>
    </mrow>
    <annotation>E=mc^2</annotation>
  </semantics>
</math>




Per testare la sintassi del nostro esempio
(http://www.html.it/guide/esempi/html5/esempi/lezione_svg/emc2.mml.html) è necessario munirsi
della versione nightly (sperimentale) del browser WebKit (http://nightly.webkit.org/), tra i pochi a
supportare queste specifiche. Questo è il risultato (figura 2):

Figura 2




Novità in HTML5
Ed ecco la novità: le specifiche HTML5 prevedono l’integrazione di entrambi questi markup senza
nessuna definizione aggiuntiva (a differenza della precedente integrazione con XHTML). Sarà quindi
possibile scrivere:

<!doctype html>
<html>
<title>Big Buck Bunny, il trailer</title>
<body>
  <svg>
    <!-- elementi SVG -->
  </svg>
  <math>
    <!-- elementi MathML -->
  </math>
</body>
</html>




E c’è di più: le due sintassi in questione potranno direttamente beneficiare dell’intero ecosistema
HTML5 attorno a loro. Questo significa che una espressione MathML può, in questo contesto, variare a
seguito della ricezione di alcuni dati tramite Ajax, oppure che una rappresentazione SVG può essere
pilotata da alcuni eventi JavaScript attivati attraverso una form.


Conclusioni
Con queste due nuove frecce al suo arco, le specifiche HTML5 acquistano un nuovo livello di potenza.
Forse non capiterà troppo spesso di implementare sintassi MathML per sistemi applicativi ma
sicuramente lo stesso non si può dire per SVG. Anche il supporto a queste due specifiche è diverso, la
Scalable Vector Graphic è pressoché presente su tutte le più recenti versioni dei browser, mentre il
supporto per il markup per definire espressioni matematiche comincia ora ad affacciarsi nelle prime
beta, come ad esempio Firefox 4 o la versione nightly di WebKit. In ogni caso esiste una interessante
libreria JavaScript, MathJax (http://www.mathjax.org/), capace di interpretare linguaggio MathML e
generare una corretta rappresentazione grafica sulla maggior parte dei browser in commercio.




                                 Una 'lavagna virtuale'


Nel corso delle lezioni abbiamo assistito al graduale arricchimento del progetto guida; in questo
articolo ne esploreremo alcune possibili evoluzioni attraverso l’utilizzo delle API HTML5 apprese finora.


Il canvas nella dashboard
È possibile fare in modo che dalla dashboard (dashboard.html) si possa visualizzare il contenuto,
attuale e salvato, del canvas di una data fiveboard con un meccanismo simile a quello utilizzato per la
memorizzazione su localStorage. Per ottenere questo risultato possiamo sostituire il blocco legato al
comando ‘richiedi_testo’ nel file application.js con:

case 'richiedi_testo':
  evento.ports[0].postMessage('testo_corrente:' +
    document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value);
  evento.ports[0].postMessage('testo_memorizzato:' +
    localStorage.getItem("fb_" + titolo_fiveboard));
  evento.ports[0].postMessage('canvas_corrente:' +
    ctx.canvas.toDataURL('image/png'));
  evento.ports[0].postMessage('canvas_memorizzato:'+
localStorage.getItem("canvas_fb_" + titolo_fiveboard));
break;




A questo punto dobbiamo far sì che la dashboard sappia ricevere ed interpretare correttamente questi
4 distinti messaggi, modifichiamo quindi il blocco che fa seguito al comando attendi_testo in
dashboard.html:

case 'attendi_testo':
  evento.ports[0].onmessage = function(e){
    nome_comando         = e.data.split(":")[0]
    valore_comando = e.data.substr(nome_comando.length + 1);
    elemento = document.getElementById(nome_comando);
    switch (elemento.tagName){
       case 'CANVAS':
         elemento.width = elemento.width;
         if(valore_comando == 'null') return
         var context = elemento.getContext('2d');
         var immagine = new Image();
         immagine.src = valore_comando; context.drawImage(immagine,0,0);
       break;
       case 'TEXTAREA':
         if(valore_comando == 'null')
           valore_comando = '';
         elemento.value = valore_comando;
       break;
    }
  }
break;




Nel frammento di codice appena esposto facciamo uso dell’attributo tagName per determinare il tipo di
elemento che deve contenere le informazioni appena ricevute. In questo modo il comando
‘testo_memorizzato:contenuto_del_testo’ inviato dalla fiveboard si traduce nella valorizzazione
dell’elemento ‘testo_memorizzato’ a ‘contenuto_del_testo’ nella dashboard.

Completiamo questa evoluzione inserendo gli elementi di markup all’interno di dashboard.html:

</ol>
<section>
  <h1>In Osservazione:</h1>
  <textarea id="testo_corrente" placeholder="testo_corrente" readonly></textarea>
  <textarea id="testo_memorizzato" placeholder="testo_memorizzato" readonly></textarea
>
    <canvas    id="canvas_corrente">Canvas Corrente</canvas>
    <canvas    id="canvas_memorizzato">Canvas Memorizzato</canvas>
</section>




Ecco uno screenshot dell’implementazione funzionante (figura 1):
Figura 1 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_progetto/1.jpg)

Com’è possibile notare, ora alla pressione sul link ‘più informazioni’ la dashboard è in grado di
mostrare sia testo che canvas, attuali e memorizzati, della fiveboard richiesta.


Esportazione in SVG
Potrebbe essere interessante, in questo contesto, offrire ai nostri utenti una esportazione in SVG del
contenuto del canvas. In un prossimo futuro il task in questione potrà essere risolto in modo efficace
utilizzando la funzione canvas.toDataUrl con parametro ‘image/svg+xml’ ; purtroppo ad oggi i
browser supportano soltanto un'esportazione in formato png e quindi dovremo costruire qualcosa di
più articolato per soddisfare questo comportamento.
Iniziamo con l’aggiungere questa funzione in canvas.js:

EsportaInSVG = function(){
  var bb = new BlobBuilder();
  bb.append( "" +
     "<?xml version='1.0' standalone='no'?>" +
     "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'" +
     " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>" +
     "<svg width='300px' height='300px' viewbox='0 0 300 300'" +
       " xmlns='http://www.w3.org/2000/svg' version='1.1'>" +
       " <title>SVG: " + titolo_fiveboard + "</title>" +
       " <desc>Un esportazione in SVG del canvas disegnato</desc>" +
       " <path d='" + svg_path + " z' fill='transparent'" +
           " stroke='black' stroke-width='1'/>" +
     "</svg>"
  );
  window.open(window.createObjectURL(bb.getBlob('image/svg+xml')));
}




L’oggetto BlobBuilder fa parte delle File API: Writer (http://www.w3.org/TR/file-writer-api/), un set
di funzionalità che gravitano attorno alle specifiche HTML5 e consentono di costruire veri e propri file
da far scaricare all’utente o ai quali far puntare il browser. Al momento questi metodi sono ancora
abbastanza sperimentali, nonostante già supportati in Chrome, Firefox e WebKit. In questo esempio
la variabile bbviene valorizzata con un file SVG nel quale sono inseriti dinamicamente i contenuti delle
variabili titolo_fiveboard, il titolo della fiveboard, e svg_path, della cui costruzione ci occuperemo
a breve. Nell’ultima riga della funzione il browser apre una nuova finestra verso una URL univoca
generata dal metodo createObjectURL. Tale URL punta al contenuto della variabile bb esposto con
MIME image/svg+xml.

Occupiamoci ora della generazione della variabile svg_path che dovrà contenere le istruzioni per
ricostruire il percorso generato dall’utente sul canvas. Modifichiamo il file canvas.js come segue:

var ctx = null;
var started = false;
var svg_path = "";
iniziaDisegno = function(evento){
  ctx.beginPath();
  ctx.moveTo(evento.offsetX,evento.offsetY);
  svg_path += "M " + evento.offsetX + " " + evento.offsetY + " ";
    started = true;
}


disegna = function(evento){
  if(started){
    ctx.lineTo(evento.offsetX,evento.offsetY);
    svg_path += "L " + evento.offsetX + " " + evento.offsetY + " ";
        ctx.stroke();
    }
}
Il trucco sta nel far seguire all’istruzione che insiste sul canvas la corrispondente variazione da
applicare al path svg. In questo modo una particella ‘M x y’ verrà concatenata a svg_path per ogni
istruzionemoveTo inviata al canvas, lo stesso accadrà per l’istruzione lineTo, seguita da una
concatenazione di ‘L x y’.

L’ultimo passo prima del completamento di questa evoluzione consiste nell’aggiungere il pulsante
preposto a far scatenare la funzione EsportaInSVG:

  </button>
  <button type="button" onclick="EsportaInSVG();">
    Esporta il canvas corrente in SVG
  </button>
</menu>




Per testare questa modifica ricorriamo a Chrome (figura 2):

Figura 2 (click per ingrandire)




(http://www.html.it/guide/esempi/html5/imgs/lezione_progetto/2.jpg)
Ed ecco il risultato! Mentre la barchetta nella finestra in sfondo risiede su di un canvas quella in primo
piano è creata a partire da un file SVG; notate inoltra il curioso URL di questa finestra, generato in
tempo reale dalla funzione di cui abbiamo parlato poco fa.

Non ci resta che rimandare alla demo
(http://www.html.it/guide/esempi/html5/esempi/lezione_progetto/fiveboard/index.html) per un test.
Il codice è disponibile per il download
(http://www.html.it/guide/esempi/html5/esempi/lezione_progetto/fiveboard.zip).


Un passo avanti
Con quest’ultima evoluzione ci apprestiamo a congedare il progetto guida, utile assistente durante le
passate lezioni. Per chi fosse interessato a mantenere questo applicativo come base per proprie
sperimentazioni ecco alcuni possibili e sfidanti implementazioni effettuabili:

      Fare in modo che il viewer.html possa seguire in tempo reale anche l’atto del disegno sul
      canvas della fiveboard che sta osservando, così come oggi avviene per il testo. Questa modifica
      coinvolge la definizione di un protocollo snello per la trasmissione di dati di questo tipo
      attraverso un WebSocket.
      Refactoring! Perché non provare a fare in modo che i comandi che giungono alle pagine web
      nel formato scelto per convenzione come ‘nome_comando:valore_comando’ siano trasformati in
      eventi custom e propagati attraverso il DOM ? La sintassi potrebbe essere questa:

worker.port.onmessage = function(evento){
  nome_comando = evento.data.split(":")[0]
  valore_comando = evento.data.substr(nome_comando.length + 1);
  var evento_custom = document.createEvent("CustomEvent");
  evento_custom.initCustomEvent(nome_comando, true, true, valore_comando);
  document.dispatchEvent(evento_custom);
}




In questo modo si potrebbe ottenere una struttura molto più elegante, nella quale ogni aspetto
dell’applicazione si sottoscrive autonomamente agli eventi dei quali necessita.


Conclusioni
Con questa lezione si conclude il ciclo dedicato all’esplorazione delle nuove API rese disponibili
dall’HTML5. Prima della conclusione di questa guida trova spazio un'ultima lezione, la prossima,
incentrata su tutte le tematiche che legano le specifiche al mondo reale. Verranno quindi trattati temi
come il feature detection ed elencate librerie che possono, di caso in caso, sopperire alla mancanza
di particolari funzionalità. Proprio a questo tema ricordiamo che il progetto guida è nato e cresciuto
con finalità didattiche e di sperimentazione e per questa ragione è molto carente in termini di
compatibilità tra browser e graceful degradation; a ben pensare anche perseguire queste tematiche
potrebbe essere un ottimo esercizio da svolgere in autonomia dopo aver letto la prossima lezione!




                  Feature detection e strategie di fallback


Come si comportano le API e i nuovi tag proposti dall'HTML5 nel mondo reale? Come dobbiamo
comportarci se vogliamo beneficiarne? In quest'ultima lezione risponderemo a questa importantissima
domanda esaminando alcune best-practice per una implementazione che possa arricchire le nostre
applicazioni e portali web senza causare grattacapi e disagi.


Una pioggia di tag
Possiamo usare <article>, <section> o <footer> su browser come Internet Explorer 6? La
risposta è sì, a patto di premunirsi di alcuni semplici e comodi strumenti capaci di far recepire questi
nuovi elementi HTML5 anche a user-agent non più giovanissimi. Uno di questi tool, specificatamente
progettato per i browser di casa Microsoft, prende il nome di html5shim
(http://code.google.com/p/html5shim/) e può essere incluso nelle proprie applicazioni molto
facilmente:

<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->


Questo strumento fa sì che Internet Explorer possa applicare gli stili ai nuovi tag introdotti
dall'HTML5. Ma ancora non basta. La maggior parte dei browser di non ultima generazione, infatti,
non conoscendo direttamente i nuovi elementi, applica a loro un foglio di stile di base errato,
assegnando ad esempio display:inline a tag come <article> o <section>.

Per risolvere questa fastidiosa incombenza la soluzione migliore è utilizzare un foglio di stile capace di
resettare le proprietà di questi e altri elementi attivando il comportamento atteso. HTML5 reset
stylesheet (http://html5doctor.com/html-5-reset-stylesheet/) si presta in modo ottimale a questo
obiettivo; l'unico passo da compiere è includerlo all'interno del proprio sito prima degli altri documenti
.css.


Feature detection vs. browser detection
La corsa all'HTML5 intrapresa dai vari browser nell'ultimo periodo si può descrivere come una
incrementale implementazione delle feature proposte dal W3C; il criterio con cui viene stabilita la
priorità nella 'scaletta delle API da aggiungere' è influenzato da moltissimi fattori e differisce da
prodotto a prodotto. Allo stato attuale delle cose non ha quindi particolarmente senso determinare il
comportamento della propria applicazione sulla base dello user-agent che la sta eseguendo, quanto
più cercare di intercettare la presenza o meno di una specifica feature.

L'attività, potenzialmente noiosa di per sé, viene facilitata in modo estremo dalla presenza sul
mercato di un utilissimo tool: Modernizr.js (http://www.modernizr.com/). Facciamo un esempio,
sinceriamoci del supporto per il localStorage:

if(Modernizr.localstorage) {
  alert("Rilevato il supporto per il localStorage");
} else {
    alert("Oh oh, nessun supporto per il localStorage");
}


Semplice, elegante e molto comodo. L'installazione di questa libreria si risolve con una semplice
inclusione e una speciale classe da applicare al tag <html>:

<html class="no-js">
<head>
<script src="/modernizr-1.6.min.js"></script>
  <script>
    // Ora puoi utilizzare Modernizr!
  </script>
</head>



Fallback e alternative
Abbiamo scoperto che il nostro utente non supporta una certa funzionalità... e ora? Niente panico:
nella maggior parte dei casi esistono valide alternative, o quantomeno interessanti opzioni di
fallback. Vediamole nel dettaglio.

Audio e Video

In questo caso l'alternativa migliore rimane la collaudata tecnologia Flash. Esistono parecchie
librerie sulla rete capaci di determinare autonomamente il supporto o meno al tag <video> e agire di
conseguenza. Noi abbiamo scelto video.js (http://videojs.com/) per la sua maturità e per l'ottimo
supporto di skin con le quali personalizzare il proprio player. L'utilizzo è semplicissimo e ben illustrato
nella pagina di Getting Started (http://videojs.com/#getting-started).

Canvas

Abbiamo due soluzioni da segnalare come alternativa all'assenza canvas: la prima si chiama
explorercanvas (http://code.google.com/p/explorercanvas/), una libreria Javascript sviluppata da
Google per simulare il comportamento del <canvas> all'interno di Internet Explorer. Per attivarla è
sufficiente richiamare lo script excanvas.js all'interno del proprio tag <head>:

<!--[if IE]><script src="excanvas.js"></script><![endif]-->


Da qui in poi è possibile utilizzare all'interno della pagina il tag <canvas> ed invocare su questo la
maggior parte dei metodi previsti dalle specifiche W3C.

L'altra tecnica di fallback prende invece il nome di FlashCanvas (http://flashcanvas.net/) ed emula il
comportamento delle API HTML5 attraverso il sapiente utilizzo della tecnologia Flash. La versione 1.5,
supera più del 70% dei test proposti da Philip Taylor
(http://philip.html5.org/tests/canvas/suite/tests/), piazzandosi in questo modo sul primo gradino del
podio delle alternative disponibili. Anche in questo caso per beneficiare di questa libreria è necessario
solamente richiamare l'apposito file javascript:

<!--[if lt IE 9]>
<script type="text/javascript" src="path/to/flashcanvas.js"></script>
<![endif]-->



Geolocation

Come simulare le API di geolocazione quando non sono supportate? Prima di disperare è importante
ricordare che seppur non tutti browser implementano queste specifiche è comunque possibile alle
volte accedere ad informazioni geospaziali utilizzando API proprietarie messe a disposizione da
particolari device o plug-in esterni, come ad esempio Google Gears. geo-location-javascript
(http://code.google.com/p/geo-location-javascript/) è una libreria che identifica le varie estensioni
disponibili sul portatile (o smartphone) dell'utente e ne uniforma e centralizza le API permettendo di
accedere ad informazioni come latitudine e longitudine usando gli stessi metodi proposti dal W3C.
Ecco un esempio. Per prima cosa è necessario includere i necessari file javascript:

<script src="http://code.google.com/apis/gears/gears_init.js" type="text/javascript"><
/script>
<script src="geo.js" type="text/javascript" ></script>


Quindi è necessario inizializzare la libreria con il comando:

if (!geo_position_js.init()) {
  // funzionalità di geolocazione non presenti (ne HTML5 ne attraverso estensioni
    // proprietarie...)
}


A questo punto è possibile invocare i classici metodi come da specifiche W3C, stando attendi a
preporre l'oggetto geo_position_js:

geo_position_js.getCurrentPosition(funzione_se_successo,funzione_se_errore);



Drag and Drop

In questo caso ci sono due tematiche da affrontare: la prima è di carattere 'workaround' e si focalizza
sull'identificare soluzioni alternative che preservino lo stesso meccanismo di interazione del Drag and
Drop HTML5. Per questo tipo di eventualità esistono potentissime librerie capaci di offrire esattamente
questo tipo di comportamento: le più famose sono ovviamente JQueryUI
(http://jqueryui.com/demos/draggable/), ma anche Scriptaculous
(http://madrobby.github.com/scriptaculous/draggable/) e il nuovo Scripty2
(http://scripty2.com/doc/scripty2%20ui/s2/ui/behavior/drag.html). Tutto questa interazione di
fallback, invece, viene meno nel caso le API previste dal W3C siano utilizzate espressamente per la
loro funzione di input, quindi, ad esempio, per trascinare file all'interno della pagina web. In questo
caso si può pensare di mantenere la funzionalità riducendo il potere dell'interfaccia e dell'interazione,
realisticamente con l'utilizzo di normalissime form e di campi <input type=file>.

WebSocket

Appurata l'assenza del supporto WebSocket l'alternativa migliore rimane ripiegare su una tra le
tecniche per soppiantare le quali sono nate proprio queste API HTML5: stiamo parlando di Comet e
diwebsocket in Flash. Proprio a proposito di quest'ultima opzione merita un cenno l'ottimo, anche
se poco documentato, web-socket-js (https://github.com/gimite/web-socket-
js/blob/master/README.txt) capace di replicare il comportamento delle specifiche W3C utilizzando
però un piccolo componente Flash iniettato dinamicamente all'interno della pagina. A questo indirizzo
(https://github.com/gimite/web-socket-js/blob/master/sample.html) un esempio di utilizzo.

WebWorkers

Non esistono, al momento, soluzioni di ripiego per riparare l'assenza del supporto di questa parte
delle specifiche HTML5 senza ricorrere ad estensioni di terze parti come le ottime WorkerPool API
(http://code.google.com/intl/it-IT/apis/gears/api_workerpool.html) di Google Gears. L'unico
workaround possibile è scrivere una versione del codice eseguito dal worker capace di funzionare
all'interno del contesto della pagina ed eseguire quella nel caso in cui venga verificata l'assenza della
feature (chiaramente perdendo il vantaggio dell'esecuzione asincrona). Nel caso invece si stia
trattando si SharedWebWorkers il discorso si complica ulteriormente; è in linea teorica possibile
emulare artigianalmente un comportamento di questo tipo utilizzando un area di storage comune (es:
localStorage) per lo scambio di messaggi convenzionati tra le pagine dello stesso dominio.

WebStorage e IndexedDB

Come per i WebWorkers, anche qui la soluzione di fallback più completa in termini di funzionalità
passa attraverso Google Gears: stiamo parlando delle Database API (http://code.google.com/intl/it-
IT/apis/gears/api_database.html). E' però importante sottolineare che, mentre WebStorage e
IndexedDB basano la loro filosofia di funzionamento su array associativi, Google Gears si appoggia ad
una istanza di SQLite, un database relazionale che opera tramite classiche istruzioni SQL, In questo
caso quindi offrire una soluzione alternativa all'assenza di questi componenti HTML5 potrebbe rivelarsi
decisamente costoso.

Offline Web Application

Google Gears anche in questo caso. Le API si chiamano LocalServer (http://code.google.com/intl/it-
IT/apis/gears/api_localserver.html) e presentano funzionalità simili a quelle offerte dalle
implementazioni delle specifiche W3C.


Shim e Polyfill a profusione
Abbiamo dato solo un piccolo assaggio della quantità di librerie alternative disponibili per sopperire
alle più svariate API HTML5. In realtà il numero di questi software di fallback è decisamente
sostanzioso ed è bene introdurre alcune differenze di massima tra le varie tipologie di librerie
disponibili.

Definiamo come Shim una libreria che sopperisce ad una funzione mancante emulandone il
comportamento ma richiedendo allo sviluppatore uno sforzo aggiuntivo in quando le API che mette a
disposizione differiscono da quelle native in HTML5. Con Polyfill indichiamo invece uno Shim che però
fornisce allo sviluppatore le medesime API offerte dalla controparte HTML5 rendendo così necessaria
una singola implementazione.

Quindi, ove possibile è meglio preferire un Polyfill ad uno Shim. Già, ma come trovarli? Si può
consultare la lista dei migliori polyfill (https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-
Browser-Polyfills) redatta dagli sviluppatori di Modernizr.

Un ultimo appunto: se il numero di file javascript dovesse diventare troppo sostanzioso ricordiamoci
che è sempre possibile ricorrere ad un loader condizionale, come yepnope.js
(http://yepnopejs.com/).


Conclusioni
Siamo alla fine, con queste ultime righe si conclude la guida HTML5 di html.it, in circa 50 lezioni
abbiamo sorvolato un pianeta ricco di nuove ed interessanti opportunità, capaci di far evolvere le
nostre creazioni web fino a dove pochi anni fa non ritenevamo possibile. In questa lezione abbiamo
scoperto come le specifiche si intreccino con il mondo reale e quali sono i potenziali comportamenti di
fallback da implementare nel caso di assenza di qualche particolare feature. In particolare ci siamo
accorti che per tematiche di basso livello (WebStorage, IndexedDB e Offline Web Application) non
esistono soluzioni alternative, se si esclude Google Gears, che però necessita di una installazione
manuale da parte dell'utente finale.

Un ultimo punto importante prima di concludere: non tutte le funzionalità proposte dall'HTML5 sono
da considerarsi pronte per un uso in fase di produzione, alcune di esse infatti sono ancora in uno
stadio di implementazione poco stabile, oppure subiranno a breve importanti cambiamenti nelle loro
API; per avere un elenco aggiornato di cosa è considerato utilizzabile in produzione e cosa no potete
consultare la tabella di compatibilità posta in appendice a questa guida.
Html5 based
Html5 based
Html5 based
Html5 based
Html5 based
Html5 based

Html5 based

  • 1.
    Introduzione Una guida operativa Affrontareun tema vasto come quello dell'HTML5 spaventa sempre un po', soprattutto quando l'obiettivo è quello di abbracciare l'intera tecnologia sottostante le specifiche proponendo al contempo un'opera che sia fruibile e divertente da leggere. Il primo passo che abbiamo deciso di compiere nella stesura di questo progetto è stato individuare il target di lettori ai quali la guida vorrebbe rivolgersi. Abbiamo allora identificato da un lato un gruppo di sviluppatori interessati alla consultazione di specifiche referenze del linguaggio, dall'altro un insieme di lettori desiderosi di informarsi a tutto tondo sulle novità offerte dall'HTML5. A questa commistione di interessi si aggiunge una naturale segmentazione della guida secondo i due temi che maggiormente permeano le specifiche: il nuovo approccio semantico nella creazione di pagine web e il ricco set di API reso disponibile. Il percorso si snoda in modo organico tra le due macro-sezioni alternando lezioni di stampo divulgativo, ottime per avere una visione di insieme della tematica trattata, a percorsi verticali costruiti attorno a funzionalità specifiche. Non sono previsti articoli introduttivi alla sintassi del linguaggio HTML e nemmeno approfondimenti su elementi o API già presenti nelle versioni precedenti, a meno che questi non abbiano subito un cambio radicale; se fosse necessario recuperare informazioni su tali aspetti rimandiamo alla lettura della guida HTML 4 (http://xhtml.html.it/guide/leggi/51/guida-html/) redatta da Wolfgang Cecchin. I progetti guida Per questa guida abbiamo deciso di combinare i tanti piccoli esempi dedicati ad ogni lezione in un vero e proprio progetto che sappia anche mostrare come le singole funzionalità HTML5 lavorano insieme. In realtà i progetti sono due, come due gli aspetti principali di questa specifica: al termine delle lezioni legate al nuovo supporto semantico dell'HTML5 la combinazione dei vari esempi mostrati darà vita ad untemplate per blog studiato per trarre vantaggio da elementi come <section> e <article>, dai nuovi content model e dalle novità in materia di form. La parte incentrata sulle API dedicate allo sviluppo di applicazioni web invece includerà tutti gli elementi necessari alla stesura di una lavagna virtuale sulla quale si potranno tracciare linee utilizzando il mouse e che darà la possibilità di salvare in locale le opere create. Chiaramente tale scelta è stata implementata in modo da non pregiudicare l'indipendenza delle singole lezioni ma con l'idea di suggellare un ulteriore filo conduttore all'interno dell'opera. Figura 1 - Anteprima del template per blog in HTML5 (click per ingrandire)
  • 2.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_1/blog_preview.png) Alcuni prerequisiti Nella prossimalezione ci interesseremo con maggior attenzione alla timeline ufficiale HTML5, per ora basti sapere che la data di accettazione delle specifiche come standard W3C è ancora sufficientemente lontana. Nonostante questo, buona parte di quanto verrà trattato in questa guida è già disponibile sulla grande maggioranza dei browser e, come vedremo, alcuni aspetti del linguaggio sono da tempo in produzione su portali di notevoli dimensioni, come youtube.com (http://www.youtube.com/html5) o vimeo.com (http://vimeo.com/blog:268). Esistono tuttavia ampie porzioni delle specifiche che, perché meno strategiche, di più difficile implementazione o meno mature, sono ad oggi supportate in modo superficiale e disomogeneo; per poter beneficiare al massimo delle demo e degli esempi anche per elementi o API che ricadono in questa categoria si consiglia quindi di dotarsi di un browser che utilizzi WebKit come layout engine in quanto dotato del più ampio supporto HTML5 ad oggi disponibile sia per le parti della specifica ormai consolidate sia per quelle al momento più 'sperimentali'. In particolare, tutto il codice di questa guida è stato sviluppato e testato usando la ‘Nightly Build' di Chromium, cioè la versione speciale per fini di sviluppo che contiene ogni feature ad oggi implementata, per quanto sperimentale esso sia. La pagina per il download, disponibile per i principali sistemi operativi, è raggiungibile all'indirizzo http://build.chromium.org/f/chromium/snapshots/ (http://build.chromium.org/f/chromium/snapshots/) seguendo il link nominato come il proprio sistema operativo e successivamente la cartella recante il numero più alto tra i presenti. Tabella della compatibilità sui browser Se Chromium, lo accennavamo, garantisce ad oggi il supporto più ampio alle funzionalità previste nella specifica e in via di definizione presso il W3C e il WHATWG, la maggior parte dei browser commerciali più diffusi, con ritmi e tempi diversi, si sta adeguando. Internet Explorer 9 e Firefox 5, rilasciati nella primavera di quest’anno, condividono infatti un ottimo supporto a queste specifiche.
  • 3.
    Lo stato dell'arterelativamente al supporto HTML5 lo abbiamo tracciato in questa tabella di compatibilità (http://www.html.it/guide/esempi/html5/tabella_supporto/tabella.html) che sarà via via aggiornata con l'estendersi del supporto alle feature che attualmente non sono supportate. Estratti della tabella sono inseriti anche a corredo delle lezioni dedicate a ciascun elemento. Mappa della guida Nell'immagine seguenti è riassunta l'intera struttura dell'opera mettendo in evidenza la posizione delle singole lezioni rispetto al contenuto globale con l'obiettivo di fornire una panoramica esauriente sui temi che verranno trattati. Ai nastri di partenza! Figura 2 - Mappa della guida (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_1/2.jpg) Bene, con questo può definirsi conclusa questa necessaria sezione introduttiva, la prossima lezione affronterà la travagliata storia che ha caratterizzato la definizione di queste specifiche, con un piccolo ma sentito cameo anche da parte dell'XHTML2. Da HTML 4 ad HTML5 Un po' di storia Siete curiosi di sapere come tutto è nato? Venerdì 4 giugno 2004, in una notte buia e tempestosa, Ian Hickson annuncia la creazione del gruppo di ricerca WHAT (http://www.whatwg.org/), acronimo di Web Hypertext Application Technology in un sintetico ma ricco messaggio (http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2004-June/000005.html). L'obiettivo del gruppo è quello di elaborare specifiche per aiutare lo sviluppo di un web più orientato alle applicazioni che ai documenti; tra i sottoscrittori di questa iniziativa si annoverano aziende del
  • 4.
    calibro di Apple,Mozilla e Opera. Questa piccolo scisma dal W3C è determinato dal disaccordo in merito alla rotta decisa dal consorzio durante un famoso convegno del 2004 dove, per un pugno di voti, prevalse la linea orientata ai documenti di XHTML2. "XHTML 2.0 considered harmful (http://lists.w3.org/Archives/Public/www-html/2003Jan/0123.html)" è il titolo di un messaggio inviato alla mailing list ufficiale del W3C datato 14 gennaio 2003 che ben riassume i sentimenti contrastanti che all'epoca si respiravano in merito a queste specifiche. La principale causa di perplessità è da ricercarsi nella decisione azzardata di non mantenere la retro- compatibilità con la versione 1.1 eliminando alcuni tag e imponendo agli sviluppatori web un controllo rigoroso nella creazione delle pagine, pena lo stop del processo di parsing e la visualizzazione a schermo degli errori di interpretazione. Nei due anni successivi i gruppi XHTML2 e WHAT proseguono i lavori sulle proprie specifiche in modo indipendente e parallelo, sollevando dubbi e confusione sia da parte dei produttori di browser che degli sviluppatori web. Emblematico in tal senso è un articolo firmato da Edd Dumbill nel dicembre 2005 intitolato The future of HTML (http://www.ibm.com/developerworks/xml/library/x-futhtml1/). Il 27 ottobre 2006 in un post sul proprio blog intitolato Reinventing HTML (http://dig.csail.mit.edu/breadcrumbs/archive/2006/10/27), Tim Berners-Lee annuncia la volontà di creare un nuovo gruppo di ricerca che strizzi l'occhio al WHAT e ammette alcuni sbagli commessi seguendo la filosofia XHTML2: Some things are clearer with hindsight of several years. It is necessary to evolve HTML incrementally. The attempt to get the world to switch to XML, including quotes around attribute values and slashes in empty tags and namespaces all at once didn't work. The large HTML-generating public did not move, largely because the browsers didn't complain. Some large communities did shift and are enjoying the fruits of well-formed systems, but not all. It is important to maintain HTML incrementally, as well as continuing a transition to well- formed world, and developing more power in that world. Tim Berners-Lee. Dovranno passare quasi altri due anni di competizione tra le due specifiche, questa volta entrambe interne al W3C, prima che nel luglio del 2009 lo stesso Tim sancisca di non voler riconfermare il gruppo XHTML2 per l'anno successivo preferendo di fatto la direzione intrapresa dall'HTML5. Frattanto il gruppo di ricerca, formato da una commistione di elementi del W3C e del WHAT, sotto la guida di Ian Hickson, è giunto alla 4° versione delle specifiche (http://www.whatwg.org/specs/web- apps/current-work/multipage/). Nonostante il continuo e solerte lavoro e il grande intervallo di tempo speso nella stesura di questo documento, i passi residui necessari ad eleggere questa tecnologia al rango di 'W3C Reccomandation', relegandola così tra gli standard riconosciuti, sono ancora molti, al punto che si prevede di raggiungere l'agognato traguardo soltanto attorno al 2020. Ricordiamo però che il consorzio si riferisce sempre all'intero universo inscritto nelle specifiche mentre alcune feature ritenute peculiari ed importanti, ad esempio il tag <video>, sono già implementate da tempo dalla totalità (o quasi) dei browser in commercio. Che cos'è l'HTML5? Domanda semplice solo all'apparenza; siamo sicuramente di fronte alla quinta revisione delle specifiche HTML ma anche di un vastissimo elenco di funzionalità che si sviluppano attorno al tema del linguaggio di markup: cerchiamo quindi di fare un po' di ordine. Per prima cosa è importante ricordare che, anche in virtù della storia della sua nascita, all'interno dell'HTML5 convivono in armonia due anime: la prima, che raccoglie l'eredità semantica dell'XHTML2, e la seconda che invece deriva dallo sforzo di aiutare lo sviluppo di applicazioni Web. Il risultato di questo mix di intenti è più articolato di quanto si immagini; in prima istanza si assiste ad una
  • 5.
    evoluzione del modellodi markup, che non solo si amplia per accogliere nuovi elementi, ma modifica in modo sensibile anche le basi della propria sintassi e le regole per la disposizione dei contenuti sulla pagina. A questo segue un rinvigorimento delle API JavaScript che vengono estese per supportare tutte le funzionalità di cui una applicazione moderna potrebbe aver bisogno: salvare informazioni sul device dell'utente; accedere all'applicazione anche in assenza di una connessione Web; comunicare in modo bidirezionale sia con il server sia con altre applicazioni; eseguire operazioni in background; pilotare flussi multimediali (video, audio); editare contenuti anche complessi, come documenti di testo; pilotare lo storico della navigazione; usare metafore di interazione tipiche di applicazioni desktop, come il drag and drop; generare grafica 2D in tempo reale; generare grafica 3D in tempo reale; accedere e manipolare informazioni generate in tempo reale dall’utente attraverso sensori multimediali quali microfono e webcam. Anche l'aspetto semantico a contorno del markup non è dimenticato; ecco quindi nascere le specifiche per la nuova generazione di microformati e per l'integrazione tra HTML5 e RDFa. Ma non è tutto, attorno a quello che può essere definito il nucleo autentico delle specifiche gravitano tutta una serie di altre iniziative, alcune delle quali in avanzato stato di definizione, studiate per: accedere alle informazioni geografiche del device dell'utente (posizione, orientamento,...); mantenere un database sul device dell'utente; generare grafica 3D in tempo reale; E non dimentichiamo che l'evoluzione dell'HTML viaggia di pari passo con quella dei CSS, che si avvicinano alla terza versione, e di altri importanti standard come SVG e MathML, e che ognuno di questi componenti è progettato nella sua versione più recente per recare e ricevere beneficio dagli altri. Perché dovremmo passare all'HTML5? Il panorama di Internet è cambiato molto dall'assunzione a 'W3C Reccomandation' della versione precedente delle specifiche, avvenuta verso la fine del 1999. In quel tempo il Web era strettamente legato al concetto di ipertesto e l'azione più comune per l'utente era la fruizione di contenuti, tipicamente in forma testuale. La mediamente bassa velocità di connessione e il limitato investimento sul media contribuivano ad una scarsa presenza di applicazioni web, più care da sviluppare ed esigenti in termini di banda. Tutto questo era ben rappresentato da un linguaggio, HTML, principalmente orientato ad agevolare la stesura di semplici documenti testuali collegati fra loro. Negli anni successivi l'interesse intorno alla rete ha subito una brusca accelerazione e questo ha condizionato positivamente sia la diffusione che la velocità di connessione della stessa, attirando di conseguenza maggiori investimenti e ricerca. Al modello di fruizione dei contenuti si è aggiunta la possibilità per l'utente finale di divenire esso stesso creatore attraverso applicazioni web sempre più elaborate ed interessanti. Questo nuovo livello di complessità, in termini di sviluppo, ha però dovuto scontrarsi con un set di specifiche poco inclini ad essere utilizzate per tali fini e che quindi si sono prestate al compito solo a scapito di infiniti hack e workaround. Esempi di questi 'utilizzi non premeditati' dell'HTML si possono trovare un po' ovunque, famoso il caso degli attributi rel e ref che hanno assunto nel tempo valori non previsti, (eg:nofollow) anche esterni alla loro funzione naturale (eg: l'utilizzo di questi due attributi in librerie come Lightbox).
  • 6.
    Parallelamente il percorsodi crescita del web ha fatto emergere alcune strutture di contenuto ricorrenti, ben caratterizzate dal fenomeno dei blog: informazioni di testata, menu di navigazione, elenchi di articoli, testo a pie' di pagina, ed altri. La parte dedicata al singolo articolo presenta anch'essa solitamente lo stesso set di informazioni quali autore, data di pubblicazione, titolo e corpo del messaggio. Anche in questo caso l'HTML4 non ha saputo fornire gli strumenti adatti a consentire una corretta gestione e classificazione del contenuto obbligando gli sviluppatori web a ripiegare su strutture anonime, quali <div> e<p>, arricchite di valore semantico con l'utilizzo di attributi quali class e id. L'HTML5 nasce per risolvere questi problemi offrendo agli sviluppatori web un linguaggio pronto ad essere plasmato secondo le più recenti necessità, sia dal lato della strutturazione del contenutoche da quello dello sviluppo di vere e proprie applicazioni. Grandi cambiamenti nell'ombra La differenza principale tra le versioni correnti di HTML e XHTML risiede nella sintassi. Il linguaggio di markup creato da Tim Berners-Lee, e che ancora oggi popola i nostri browser, è stato studiato come applicazione del più generico SGML, Standard Generalized Markup Language; ne è la prova la dichiarazione di Document Definition Type che dovrebbe essere posta nella prima riga di una pagina Web ad indicare la grammatica, HTML per l'appunto, usata nel documento: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/ strict. dtd"> In realtà la quasi totalità dei browser ignora la definizione e interpreta il documento secondo logiche più permissive, frutto di anni di eccezioni e di esperienza accumulata su pagine malformate. L'XHTML, invece, è una versione della sintassi HTML costretta all'interno delle regole XML, a sua volta grammatica SGML: questo approccio dovrebbe implicare un maggior rigore nella pagina e l'aderenza a regole quali l'obbligo di chiusura di tutti i tag. Il parser XML inoltre dovrebbe sospendere l'interpretazione della pagina al primo errore rilevato. L'arrivo dell'HTML5 introduce una importante novità in questo scenario, per la prima volta l'obiettivo delle specifiche è quello di definire un linguaggio ubiquo, che possa poi essere implementato su entrambe le sintassi. L'occasione è buona anche per fare un po' di pulizia e rompere definitivamente il legame tra HTML e SGML formalizzando e traducendo in standard le regole adottate da tempo nei browser. Per indicare un documento HTML5 è nata quindi la seguente semplice istruzione: <!DOCTYPE html> Che si affianca a quella da utilizzare in caso si intenda scrivere una pagina XHTML5: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> E adesso? Questa lezione aveva l'obiettivo di fornire un contesto storico e operativo ai temi dei quali questa guida tratterà in modo approfondito. Nelle prossime lezioni affronteremo in sequenza prima la parte
  • 7.
    legata al markuped alla gestione della disposizione del contenuto e successivamente le principali API introdotte da queste specifiche. Il viaggio all'interno dell'universo dell'HTML5 è appena iniziato! La sintassi di HTML5 Prima di scendere nei dettagli presentando i nuovi elementi e le nuove API definite nella specifica, è necessario spendere qualche momento per parlare delle regole sintattiche introdotte dall’HTML5, per larga misura ereditate e razionalizzate dalla precedente versione delle specifiche. In primo luogo familiarizziamo con il concetto noto di tag. Esistono tre distinte versioni di questa particella, ognuna di esse si applica selettivamente solo ad alcuni elementi: Tag ‘classico’ <p> bla bla bla bla ... </p> Tag ‘vuoto’ <img src="immagine_interessante.jpg" alt="Una immagine interessante"> Tag ‘autochiudente’ <rect x="150" y="40" width="60" height="30" fill="black" stroke="black"/> Gli elementi HTML5 si possono dividere in tre categorie sulla base della tipologia di tag da usare per implementarli. 1. Elementi normali: sono quelli che possono racchiudere dei contenuti sotto forma di testo, commenti HTML, altri elementi HTML, etc. Sono elementi normali, dunque, i paragrafi (<p>), le liste (<ul>), i titoli (<h1>), etc. Salvo specifici casi, cui accenneremo nel seguito della lezione, gli elementi normali vengono definiti attraverso un tag di apertura (<p>) e un tag di chiusura (</p>). 2. Elementi vuoti: gli elementi vuoti sono quelli che non possono avere alcun contenuto. Per questi elementi si utilizza un tag ‘vuoto’. Essi sono: area, base, br, col, command, embed, hr, img, input, keygen,link, meta, param, source, track, wbr. Per gli elementi vuoti, la chiusura del tag, obbligatoria in XHTML, è invece opzionale. Possiamo dunque definire un tag <img> secondo le regole XHTML: <img src="immagine.png" alt="testo" /> O seguendo la vecchie regole di HTML 4: <img src="immagine.png" alt="testo"> 3. Elementi provenienti da altri namespace: per questi elementi sono richiesti i tag ‘autochiudenti’. Si tratta degli elementi adottati da specifiche esterne, come SVG e MathML. Maiuscolo, minuscolo Una delle differenze principali rispetto alle regole XHTML riguarda l'uso del maiuscolo e del minuscolo per definire un tag. In XHTML è obbligatorio usare il minuscolo. In HTML5 è consentito scrivere un tag usando anche il maiuscolo:
  • 8.
    <IMG src="immagine.png" alt="testo"> Casiparticolari Esistono alcune casistiche per le quali un tag classico può mancare della sua particella di apertura o di chiusura; questo succede quando il browser è comunque in grado di determinare i limiti di operatività dell’elemento. Gli esempi più eclatanti riguardano i tag ‘contenitori’, come head, body e html, che possono essere omessi in toto a meno che non contengano un commento o del testo come istruzione successiva. È quindi sintatticamente corretto scrivere un documento come segue: <meta charset="utf-8"> <title>Pagina HTML5 Valida</title> <p>Un paragrafo può non avere la particella di chiusura <ol> <li>e anche un elemento di lista </ol> Notiamo che, come mostrato nell’esempio, anche i tag p e li possono essere scritti omettendo la particella di chiusura, a patto che l’elemento successivo sia all’interno di una cerchia di elementi definita dalle specifiche. A fronte di queste opzioni di semplificazione è però errato pensare che la pagina generata dal codice di cui sopra manchi, ad esempio, dell’elemento html; esso è infatti dichiarato implicitamente ed inserito a runtime dallo user-agent. Per un quadro dettagliato rimandiamo a questa sezione (http://www.w3.org/TR/html5/syntax.html#optional-tags) delle specifiche. Attributi Anche rispetto alle definizione degli attributi HTML5 consente una libertà maggiore rispetto a XHTML, segnando di fatto un ritorno alla filosofia di HTML 4. In sintesi: non è più obbligatorio racchiudere i valori degli attributi tra virgolette. I casi previsti nella specifica sono 4. Attributi 'vuoti': non è necessario definire un valore per l'attributo, basta il nome, il valore si ricava implicitamente dalla stringa vuota. Un caso da manuale: Secondo le regole XHTML: <input checked="checked" /> In HTML5: <input checked> Attributi senza virgolette: è perfettamente lecito in HTML5 definire un attributo senza racchiudere il valore tra virgolette. Esempio: <div class=testata>
  • 9.
    Attributi con apostrofo:il valore di un attributo può essere racchiuso tra due apostrofi (termine più corretto rispetto a 'virgoletta singola'). Esempio: <div class='testata'> Attributi con virgolette: per concludere, è possibile usare la sintassi che prevede l'uso delle virgolette per racchiudere il valore di un attributo. Il codice: <div class="testata"> Semplificazioni In direzione della semplificazione vanno anche altre indicazioni. Ci soffermiamo su quelle riguardanti due elementi fondamentali come style e script. La sintassi tipica di XHTML prevede la specificazione di attributi accessori come type: <style type="text/css"> regole CSS... </style> <script type="text/javascript" src="script.js"> </script> In HTML5 possiamo farne a meno, scrivendo dunque: <style> regole CSS... </style> <script src="script.js"> </script> Conclusioni Come abbiamo sperimentato, la sintassi HTML5 si caratterizza per una spiccata flessibilità e semplicità di implementazione. Le osservazioni che abbiamo snocciolato in questa lezione sono chiaramente valide a patto di implementare la serializzazione HTML; ricordiamo infatti che le specifiche prevedono anche l’utilizzo di una sintassi XML attraverso l’uso delle istruzioni: <!doctype html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> Infine, per una migliore leggibilità del codice sorgente, consigliamo di ricorrere il meno possibile all’utilizzo di elementi impliciti, scrivendo sempre tutti i tag necessari nella loro forma completa. Elementi disegnati per un web moderno All’inizio erano tabelle; ricordiamo tutti bene il tedio provocato dal dover costruire strutture complesse
  • 10.
    spezzandole all’interno diuna griglia fatta da infiniti <tr> e <td>: un attività noiosa, resa ancora peggiore dalla scarsa qualità e flessibilità del risultato. Poi arrivarono i <div> e fu una vera e propria rivelazione; finalmente un modello di costruzione del documento pensato per separare in modo chiaro i contenuti tipici di una pagina web. Grazie alla commistione tra questo elemento e i CSS nacquero pagine con codici HTML eleganti e leggibili come: <html> <head> </head> <body> <div id="header"> --- Titolo e Testata --- </div> <div id="body"> <div id="menu"> --- Voci di Menu --- </div> <div id="main_content"> <div class="post"> --- Un Post --- </div> <div class="post"> --- Un altro Post --- </div> </div> </div> </body> </html> All’interno di un costrutto come questo è tutto molto semplice da interpretare: basta seguire il flusso dei rientri di pagina facendosi guidare dai valori semantici attribuiti a id e class. Passarono anni e il modello cominciò a mostrare le sue prime debolezze; in primo luogo ci si accorse che da un lato non vi era una regola collettiva e quello che per uno sviluppatore era ‘body’ per l’altro poteva benissimo essere ‘corpo’; inoltre si realizzò che né il browser né tantomeno i motori di ricerca avrebbero mai potuto beneficiare di questa divisione semantica, proprio per colpa di quell’arbitrarietà che permetteva a milioni di siti web di essere organizzati in strutture simili ma sempre leggermente diverse tra loro e per questo non raggruppabili secondo schemi automatici. Emerse in questo modo uno dei più grandi problemi dell’HTML4: l’incapacità di descrivere il significato delle informazioni di una pagina web in un formato interpretabile da altri software. In un mondo sempre più caratterizzato dall’interconnessione e dall’aggregazione su base semantica dei contenuti ci si adattò inventando e condividendo convenzioni studiate per rappresentare eventi di calendario, persone e quant’altro; nacquero così diversi microformati, come ad esempio hRecipe (http://microformats.org/wiki/hrecipe) per le ricette: <span class="hrecipe"> <span class="fn">Tisana alla liquirizia</span> <span class="ingredient">2 cucchiai di Zucchero</span> <span class="ingredient">20g Radice di liquirizia</span> <span class="yield">2</span> <span class="instructions">
  • 11.
    Scaldare un pentolinocon 200cl d’acqua fino a 95°C; versare la radice di liquirizia; lasciar macerare per 7 minuti; zuccherare e servire. </span> <span class="duration"> <span class="value-title" title="PT90M"></span> 90 min </span> </span> L’HTML5 nasce per gestire ed incanalare tutte queste problematiche; nelle prossime lezioni scopriremo come beneficiare di nuovi tag disegnati appositamente per le più comuni strutture di contenuto e di attributi utilizzabili per definire ulteriore contenuto semantico alle pagine. Ma per arrivare a questo prima serve fare un po’ di pulizia... Elementi e attributi non più previsti nelle specifiche Le specifiche HTML5 (http://dev.w3.org/html5/spec/Overview.html) sanciscono definitivamente la fine di tutta una serie di elementi e attributi che mantengono validità formale solo per preservare la retrocompatibilità di pagine datate ma sono espressamente vietati nella creazione di nuovi documenti. I primi a subire questo esilio sono tutti quei costrutti funzionali alla parte di presentazione e caduti ampiamente in disuso con l’introduzione dei fogli di stile. Stiamo parlando di elementi come: basefont, big,center, font, s, strike, tt e u. Ad essi si aggiunge una moltitudine di attributi più o meno noti, tra i quali ricordiamo: align e valign, background, bgcolor, cellpadding, border, cellspacing e molti altri. In realtà alcuni tra i citati perdurano solamente su specifici elementi, per una lista completa ed esaustiva consigliamo di visionare questa pagina del W3C (http://www.w3.org/TR/html5-diff/#absent-attributes) dedicata al tema. Interessante notare come si sia deciso invece di mantenere elementi come i e b; trasformandoli, però, da modificatori tipografici a semplici indicatori di porzioni di testo particolari e consentendone l’uso solo come ultima risorsa. La falce della semplificazione si è successivamente abbattuta su un piccolo elenco di elementi obsoleti: acronym (sostituibile dal più comune abbr), applet (rimpiazzato da object), isindex (già arcaico in HTML4) e infine dir, sfavorito nel confronto con ul. Cadono, infine, anche tutti i tag che gravitano intorno al concetto dei frame, ritenuti dannosi per usabilità e accessibilità: frame, frameset e noframes. E con questi ultimi si chiude la lista degli elementi soppressi; in loro onore terminiamo la lezione con un piccolo snippet che li omaggi: <center> <font color="blue" size="2"> <big>Addio</big>, <s>monti sorgenti dall'acque, ed elevati al cielo; cime</s> elementi di markup inuguali, noti a chi è cresciuto tra voi,
  • 12.
    e impressi nellasua mente, non meno che lo sia l'aspetto de' suoi più familiari. </font> Liberamente adattato da: <acronym title="I Promessi Sposi">IPS</acronym> </center> Attributi globali Di attributi globali (quelli cioè che si possono applicare a tutti gli elementi HTML) ce ne sono sempre stati: basti pensare al classico ‘id’, disponibile da sempre sulla totalità degli elementi. HTML5 formalizza questa definizione e arricchisce lo sparuto gruppo con nuovi membri che, come intuibile, possono essere applicati a un qualsiasi tag di queste specifiche. In questo capitolo dedicheremo qualche paragrafo ad ognuno dei nuovi arrivati, alcuni dei quali, vedrete, sono decisamente interessanti. Modificare il contenuto di una pagina: contenteditable TinyMCE, CKEditor e WYMeditor sono solo tre di una lunga lista di librerie studiate per offrire uno strumento di editing testuale su web che superi le classiche textarea. I risultati sono già ad oggi eccellenti, vicini a prodotti desktop come Microsoft Word, ma le soluzioni implementate fanno tutte uso di escamotage più o meno furbi per ottenere questo livello di interazione in quanto l’HTML 4 non prevede alcun modo esplicito di generare controlli del genere. Durante la stesura delle specifiche HTML5 questo problema è stato affrontato e si è deciso di sviluppare un approccio comune al rich-editing di un documento re-introducendo un set di specifiche implementate in sordina da Microsoft (http://msdn.microsoft.com/en- us/library/ms537837(VS.85).aspx) nella versione 5.5 di Internet Explorer. Questo lavoro ha portato alla creazione di un nuovo attributo globale:contenteditable, che impostato a true su di un qualsiasi elemento lo rende modificabile da browser; lo stesso destino subiscono tutti gli elementi in esso contenuti a meno che non espongano un esplicitocontenteditable=false. Ma... cosa significa esattamente modificabile da browser? Che tipo di feedback visivo ci si dovrebbe aspettare? E come si comporterebbe il markup a fronte delle modifiche? Sfortunatamente qui le specifiche non sono troppo chiare e sanciscono semplicemente che qualsiasi cosa venga concessa all’utente il risultato deve comunque restare conforme all’HTML5: questa libertà operativa ha prodotto comportamenti diversi da browser a browser; ad esempio c’è chi traduce il tasto invio com un <br> e chi invece crea un nuovo paragrafo con <p>. Parallelamente è disponibile anche un set di API utilissime (http://dev.w3.org/html5/spec/dnd.html%23execCommand) per insistere sulla zona modificabile con comandi attivati dall’esterno, come ad esempio da una toolbar. Un pulsante che volesse attivare/disattivare il grassetto sulla selezione corrente dovrebbe invocare la seguente funzione: document.execCommand('bold') Prima di passare oltre sappiate che l’attributo contenteditable è stato applicato con valore true a all’intera sezione precedente con l’esclusione di questo paragrafo, permettendovi così di editarla e sperimentare l’interazione di questa nuova interessante caratteristica! (e se selezionate del testo e cliccate qui (javascript:document.execCommand('bold');), potrete renderlo grassetto!).
  • 13.
    Menu contestuali associatiad un elemento: contextmenu L’attributo globale contextmenu serve ad associare a un elemento un menu contestuale; questa forma di interazione è poco comune sul web ma molto praticata sul desktop dove, ad esempio, su di una cartella di sistema ci si aspetta di poter operare azioni quali ‘copia’, ‘elimina’ e ‘rinomina’. Vediamo un esempio: <img src="http://farm4.static.flickr.com/3623/3527155504_6a47fb4988_d.jpg" contextmenu="image_menu"> <menu type="context" id="image_menu"> <command label="Modifica il contrasto" onclick="contrastDialog();"> <command label="Negativo" onclick="negativo();"> </menu> Cliccando con il tasto destro del mouse sull’immagine il browser dovrebbe mostrare un menu contenente le due azioni disponibili. Purtroppo ad oggi non esiste ancora nessuna implementazione funzionante di questa feature, che resta al momento relegata a semplice specifica. Lʼattributo data-* L’HTML5 predispone la possibilità di associare ad ogni elemento che compone la pagina un numero arbitrario di attributi il cui nome può essere definito dall’utente sulla base di esigenze personali, a patto che venga mantenuto il suffisso ‘data-’; ad esempio: <img id="ombra" class="cane" data-cane-razza="mastino corso” data-cane-eta="5" data-cane-colore="nero" src="la_foto_del_mio_cane.jpg"> È inoltre interessante notare come queste informazioni, che arricchiscono e danno valore semantico all’elemento, siano accessibili anche attraverso un comodo metodo Javascript: alert("Ombra ha :" + document.getElementById("ombra").dataset.caneEta + " anni"); La fine del display:none in linea: hidden L’attributo globale hidden è stato introdotto per offrire un’alternativa all’utilizzo del predicato ‘style="display:none"’ che ha subito una proliferazione incontrollata soprattutto a seguito della diffusione di librerie Javascript come jQuery o Prototype. Un elemento marcato come hidden deve essere considerato dallo user agent come non rilevante e quindi non visualizzato a schermo. Spellcheck La quasi totalità dei browser oggi in commercio contiene un motore di verifica della sintassi grammaticale. Le specifiche HTML5 introducono un meccanismo per abilitare o disabilitare il controllo della sintassi su porzioni della pagina modificabili dall’utente. L’attributo in questione si
  • 14.
    chiama spellcheck e,quando impostato a true, ordina al browser di attivare il proprio correttore sull’elemento corrente e su tutti i suoi figli. Laddove invece non venga impostata nessuna preferenza, le specifiche prevedono che il browser utilizzi un proprio comportamento di default. Altri attributi globali Ci sono un paio di altri attributi globali introdotti dalle specifiche e non trattati in questa sede, draggable e aria-*; entrambi sottendono un discorso che si estende ben al di là della semplice implementazione di markup e verranno trattati più avanti nella guida. Ecco comunque la lista di tutti gli attributi globali previsti dalla specifica: accesskey, class, contenteditable, contextmenu, dir, draggable hidden, id, lang, spellcheck, style, tabindex, title Tabella del supporto sui browser Attributi globali contenteditable 5.5+ 3.0+ 3.1+ 2.0+ 9.0+ contextmenu No No No No No data-* No No 5.0+ 6.0+ No draggable No 3.5+ 5.0+ 5.0+ No hidden No 4.0+ Nightly build Nightly build Nightly build spellcheck No 2.0+ No No No Conclusioni In questa lezione abbiamo appreso che la nuova configurazione di markup dell’HTML5 è stata studiata per ovviare a tutti i problemi emersi in anni sviluppo di siti web e applicazioni con la precedente versione delle specifiche. Nelle prossime lezione introdurremo il nuovo content model, pensato non più nella forma binaria ‘block’ e ‘inline’ ma articolato in una serie di modelli di comportamento complessi ed interessanti. Un nuovo content model Non più solo div Ecco come si potrebbe codificare l’esempio della lezione precedente utilizzando i nuovi elementi messi a disposizione dall’HTML5: <!doctype html> <html lang="it"> <head> </head> <body>
  • 15.
    <header> --- Titolo e Testata --- </header> <nav> --- Voci di Menu --- </nav> <article> --- Un Post --- </article> <article> --- Un altro Post --- </article> </body> </html> Come si può notare, i tag introdotti hanno un nome in strettissima attinenza con il proprio contenuto; questo approccio risolve in modo elegante sia il problema dell’utilizzo dell’attributo class con valore semantico, sia la riconoscibilità delle singole aree del documento da parte di un browser. Ma non è tutto; l’introduzione di article, nav, header e altri tag che vedremo, impone anche sostanziose novità al modo in cui lo user-agent deve comportarsi nell’interpretare questi elementi. Contenuti in una bolla di sapone Partiamo dal seguente esempio HTML4: <html> <body> <h1>I diari di viaggio:</h1> <h2>A spasso per il mondo alla scoperta di nuove culture:</h2> <h3>Giro turistico della Bretagna</h3> <p>lorem ipsum..</p> <h3>Alla scoperta del Kenya</h3> <p>lorem ipsum..</p> <h3>Cracovia e la Polonia misteriosa</h3> <p>lorem ipsum..</p> <p>tutti i viaggi sono completi di informazioni su alberghi e prezzi</p> </body> </html> Se lo visualizziamo avremo un risultato assolutamente strutturato come questo: Figura 3 (click per ingrandire) - Struttura del documento
  • 16.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_5/immagine_1.png) Supponiamo ora divoler dividere i viaggi per continente. Con il modello attuale saremmo obbligati a cambiare l’elemento h3 in h4 in modo da fare spazio alla nuova suddivisione: <html> <body> <h1>I diari di viaggio:</h1> <h2>A spasso per il mondo alla scoperta di nuove culture:</h2> <h3>Europa</h3> <h4>Giro turistico della Bretagna</h4> <p>lorem ipsum..</p> <h4>Cracovia e la Polonia misteriosa</h4> <p>lorem ipsum..</p> <h3>Africa</h3> <h4>Alla scoperta del Kenya</h4> <p>lorem ipsum..</p> <p>tutti i viaggi sono completi di informazioni su alberghi e prezzi</p> </body> </html> Questo accade perché la gerarchia delle intestazioni è assoluta rispetto all’intero documento e ogni tag <h*> è tenuto a rispettarla. Nella maggior parte dei casi però questo comportamento è fastidioso in quanto è molto comune avere a che fare con contenuti che, come articoli o commenti, vorremmo avessero una struttura indipendente dalla loro posizione nella pagina. In HTML5 questo è stato reso possibile definendo una nuova tipologia di content model, chiamato ‘sectioning content’, al quale appartengono elementi come article e section. All’interno di tag come quelli appena citati la vita scorre come in una bolla di sapone, quindi l’utilizzo di un <h1> è considerato relativo alla sezione in cui si trova.
  • 17.
    Riprendiamo l’esempio precedenteed interpretiamolo in salsa HTML5: <!doctype html> <html> <head> <title>I diari di viaggio</title> </head> <body> <header> <hgroup> <h1>I diari di viaggio:</h1> <h2>A spasso per il mondo alla scoperta di nuove culture:</h2> </hgroup> </header> <section> <h1>Europa</h1> <article> <h1>Giro turistico della Bretagna</h1> <p>lorem ipsum..</p> </article> <article> <h1>Cracovia e la Polonia misteriosa</h1> <p>lorem ipsum..</p> </article> </section> <section> <h1>Africa</h1> <article> <h1>Alla scoperta del Kenya</h1> <p>lorem ipsum..</p> </article> </section> <p>tutti i viaggi sono completi di informazioni su alberghi e prezzi</p> </body> </html> Molto meglio! Ora i singoli componenti di questo documento sono atomici e possono essere spostati all’interno della pagina senza dover cambiare la loro struttura interna. Inoltre, grazie a queste divisioni, il browser riesce a discernere perfettamente il fatto che l’ultimo paragrafo non appartenga al testo del viaggio in Kenia. Diamo prova dell’atomicità creando un blocco dedicato all’ultimo articolo inserito: ‘Un week-end a Barcellona’: <!doctype html> <html> <head> <title>I diari di viaggio</title> </head> <body>
  • 18.
    <header> <hgroup> <h1>I diari di viaggio:</h1> <h2>A spasso per il mondo alla scoperta di nuove culture:</h2> </hgroup> </header> <article> <h1>Un week-end a Barcellona</h1> <p>lorem ipsum..</p> </article> <!-- resto della pagina --> Anche grazie a questo content model l’HTML5 introduce un nuovo e preciso algoritmo per il calcolo dell’outline del documento. La vista ad outline, tipica nei software di word processing e ancora non presente nei browser, è utilissima nella navigazione dei contenuti di una pagina. Sperimentiamo questa feature installando un’apposita estensione per Chromium (https://chrome.google.com/extensions/detail/afoibpobokebhgfnknfndkgemglggomo): Figura 4 (click per ingrandire) - Vista ad outline del documento (http://www.html.it/guide/esempi/html5/imgs/lezione_5/immagine_2.png) È interessante notare come l’algoritmo non solo identifichi correttamente i vari livelli di profondità, ma per ognuno di essi sappia anche recuperare il titolo adeguato. Nell’HTML5 è vitale che il design della pagina si rispecchi in una outline ordinata e coerente, questa infatti è la miglior cartina tornasole possibile in merito al corretto utilizzo delle specifiche: ad esempio, in una prima revisione della lezione, il codice HTML precedente mancava dell’elemento hgroup, utile a raggruppare elementi che concorrono a formare un titolo. L’errore è stato individuato e corretto proprio grazie alla visualizzazione di una outline errata: Figura 5 (click per ingrandire) - Strutturazione corretta del documento
  • 19.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_5/immagine_3.png) Concludiamo la trattazionedi questo content model citando la presenza di alcuni elementi che, pur seguendo le linee interpretative del 'sectioning content', devono essere ignorati dall'algoritmo di outline. Tali tag sono definiti 'sectioning roots' ed il motivo della loro esclusione è legato al fatto che essi non concorrono tanto alla struttura dei contenuti della pagina quanto all'arricchimento della stessa. Fanno parte di questo elenco elementi come: blockquote, details, fieldset, figure e td. Seppur esclusi dall'outline del documento, nulla vieta agli appartenenti di questo gruppo di avere una propria outline interna. Panoramica sui content model e presentazione del primo progetto guida Lo scopo di questa lezione è duplice: da un lato riallacciarsi al tema del sectioning content per offrire una visione di ampio respiro in merito alle differenti tipologie di disposizione di contenuto offerte dall'HTML5, dall'altro iniziare la stesura del primo progetto guida. Una panoramica completa Esistono altri content model oltre a quelli trattati nella lezione precedente, alcuni implicitamente presenti anche nelle vecchie specifiche, altri nuovi di zecca, per una rappresentazione grafica rimando all'ottima infografica (http://www.whatwg.org/specs/web-apps/current-work/multipage/content- models.html#kinds-of-content) dello stesso W3C, fra l'altro realizzata usando la sintassi SVG (provate a scorrere il mouse sopra le varie aree dell'insieme). Metadata content Fanno parte di questa categoria tutti gli elementi utili alla definizione del documento nel suo insieme: a livello di presentazione o di funzionamento. Tag: base, command, link, meta, noscript, script, style, title.
  • 20.
    Sectioning content Ne abbiamoappena parlato: il gruppo contiene tutti quegli elementi studiati per ospitare contenuti atomici e semanticamente ordinati. È importante utilizzare almeno un appartenente alla categoria heading content all’interno di ognuno di questi tag, questo anche per non avere un outline popolato da voci senza titolo (vedi immagine). Tag: article, aside, nav, section. Figura 6 (click per ingrandire) - Visualizzazione della struttura del documento (http://www.html.it/guide/esempi/html5/imgs/lezione_6/immagine_4.png) Heading content Tutti gli appartenenti a questo gruppo servono per definire titoli. Interessante notare come se la presenza di uno di questi elementi non sia associata ad un sectioning content questo venga definito implicitamente. Tag: h1, h2, h3, h4, h5, h6, hgroup Phrasing content Incorpora il testo del documento così come tutti i modificatori tipografici e visuali dello stesso. Tag: a (solo se contiene phrasing content a sua volta), abbr, area (se figlio di un elemento map), audio, b, bdi, bdo, br, button, canvas, cite, code, command, datalist, del (solo se contiene phrasing content a sua volta), dfn, em, embed, i, iframe, img, input, ins (solo se contiene phrasing content a sua volta), kbd, keygen, label, map (solo se contiene phrasing content a sua volta), mark, math, meter,noscript, object, output, progress, q, ruby, s, samp, script, select, small, span, strong, sub, sup, svg, textarea, time, var, video, wbr. Embedded content Ne fanno parte tutti quegli elementi che, come il nome del gruppo suggerisce, incorporano nella pagina oggetti esterni. Tag: audio, canvas, embed, iframe, img, math, object, svg, video. Interactive content Questa categoria comprende tutto ciò specificatamente studiato per interagire con l’utente.
  • 21.
    Tag: a, audio(con l’attributo controls presente), button, details, embed, iframe, img (con l’attributo usemap presente), input (con l’attributo type diverso da hidden), keygen, label, menu (con l’attributotype="toolbar"), object (con l’attributo usemap presente), select, textarea, video (i con l’attributo controls presente). Come avrete notato ogni elemento può appartenere ad uno o più content models, anche a seconda della sua configurazione degli attributi. Oltre ai gruppi citati, che sono i principali, va ricordato flow, che raggruppa la quasi totalità degli elementi e alcuni gruppi creati specificatamente per i controlli dei form, che vedremo più avanti. Progetto guida - nel tag HEAD Cogliamo l’occasione di questa panoramica alla struttura dell’HTML5 per gettare le prime fondamenta del progetto guida: in questo capitolo vedremo come impostare e strutturare il contenitore della pagina ed il tag head. Creiamo un file index.html ed iniziamo ad inserire i primi elementi: <!doctype html> <html lang="it"> <head> <title>We5! Il blog della guida HTML5</title> </head> </html> Fin qui, eccezion fatta per il doctype, nessuna differenza rispetto alla versione 4 delle specifiche; andiamo avanti aggiungendo alcuni tag meta e link: ... <head> <title>We5! Il blog della guida HTML5</title> <link rel="stylesheet" href="monitor.css" media="screen"> <link rel="stylesheet" href="printer.css" media="print"> <link rel="stylesheet" href="phone_landscape.css" media="screen and (max-device-widt h: 480px) and (orientation: landscape)"> <link rel="stylesheet" href="phone_portrait.css" media="screen and (max-device-width: 480px) and (orientation: portrait)"> </head> ... Incuriositi dalla strana sintassi degli attributi media degli ultimi due <link>? State osservando una tipica media query: il CSS viene interpretato solamente se le condizioni dell’espressione sono valutate come vere dallo user agent. Le media query, profetizzate già nel 1999, consentono di identificare il corretto CSS per un dato device con un incredibile livello di dettaglio, alcuni esempi: <!-- TV con scan dell’immagine progressiva --> <link rel="stylesheet" href="progressive.css" media="tv and (scan: progressive)"> <!-- Stampanti con almeno 1000dpi ---> <link rel="stylesheet" href="printer.css" media="print and (min-resolution: 1000dpi)"> <!-- Retina display --> <link rel="stylesheet" href="retina.css" media="screen and (-webkit-min-device-pixel-
  • 22.
    ratio: 2)"> <!-- Devicemonocromatici (Kindle, etc..) --> <link rel="stylesheet" href="mono.css" media="screen and (monochrome)"> Bene, completiamo questo capitolo aggiungendo icone e charset: <head> <meta charset="utf-8"> <!-- ..gli altri tag.. --> <link rel="icon" href="standard.gif" sizes="16x16" type="image/gif"> <link rel="icon" href="iphone.png" sizes="57x57" type="image/png"> <link rel="icon" href="vector.svg" sizes="any" type="image/svg+xml"> </head> Come potete notare è ora possibile specificare un attributo sizes per ogni icona, in questo modo lo user agent può liberamente scegliere quale icona abbia le dimensioni più adatte. Ci sono due motivi che giustificano l’inserimento della direttiva ‘charset’ in questo progetto: in primo luogo la nuova sintassi è molto più succinta della passata, seppur ancora valida: <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> In seconda istanza è intenzione dell’autore sottolineare come sussistano reali rischi di sicurezza legati all’assenza di questa direttiva. Dalla prossima lezione analizzeremo nel dettaglio i singoli elementi che concorrono alla costituzione della nuova impalcatura semantica del linguaggio. Header Funzioni e dati tecnici Il tag header serve a rappresentare "un gruppo di ausili introduttivi o di navigazione". Tale definizione, seppure apparentemente vaga, contiene in sé i concetti chiave per comprendere appieno la funzione di questo tag: 1. L'elemento <header> è un contenitore per altri elementi. 2. L'elemento <header> non va confuso con quella che è la testata/intestazione principale di un documento (quella che oggi si definisce in genere con il tag <h1>). 3. La natura e gli scopi dell'elemento <header> non dipendono dalla sua posizione nel documento, ma dai suoi contenuti (ausili alla navigazione o elementi introduttivi). 4. Il suo uso non è obbligatorio e in alcuni casi può risultare superfluo se non utilizzato in maniera appropriata. <header> <h1>Questo è un titolo</h1> <h2>Questo è un sotto-titolo</h2> [...]
  • 23.
    </header> Header: esempi concreti Riprendendoil nostro progetto guida, dove nella lezione precedente (http://xhtml.html.it/guide/lezione/4966/panoramica-sui-content-model-e-presentazione-del-primo- progetto-guida/) abbiamo definito il contenuto dell'<head>: <head> <meta charset="utf-8"> <title> We5! Il blog della guida HTML5 </title> <link rel="stylesheet" href="monitor.css" media="screen"> <link rel="stylesheet" href="printer.css" media="print"> <link rel="stylesheet" href="phone_landscape.css" media="screen and (max-device-width: 480px) and (orientation: landscape)"> <link rel="stylesheet" href="phone_portrait.css" media="screen and (max-device-width: 480px) and (orientation: portrait)"> <link rel="icon" href="standard.gif" sizes="16x16" type="image/gif"> <link rel="apple-touch-icon" href="iphone.png" sizes="57x57" type="image/png"> <link rel="icon" href="vector.svg" sizes="any" type="image/svg+xml"> </head> A questo punto possiamo iniziare a comporre il <body> del nostro documento partendo proprio con il tag <header>, che con l'elemento <hgroup> (http://xhtml.html.it/guide/lezione/4973/hgroup/) definisce il titolo principale del documento (del sito) e la cosiddetta tagline: <header> <hgroup> <h1>We5! Il blog della guida HTML5</h1> <h2>Approfittiamo fin da oggi dei vantaggi delle specifiche HTML5!</h2> </hgroup> </header> Ma header non deve contenere necessariamente solo titoli <hn]]>! Se titolo e sottotitolo principali sono certamente elementi introduttivi ai contenuti successivi, è naturalmente un ausilio di navigazione una lista di link che andrà a formare la barra di navigazione principale del sito. Ecco come possiamo completare la struttura del nostro primo <header>: <header> <hgroup> <h1>We5! Il blog della guida HTML5</h1> <h2>Approfittiamo fin da oggi dei vantaggi delle specifiche HTML5!</h2> </hgroup> <nav>
  • 24.
    <h1>Navigazione:</h1> <ul> <li><a href="/home">Home</a></li> <li><a href="/about">Gli autori</a></li> <li><a href="/refactoring">Il progetto four2five</a></li> <li><a href="/archives">Archivio</a></li> </ul> </nav> </header> Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta nel nostro progetto: Figura 8 - Struttura del documento: primo header Abbiamo inserito una sezione di navigazione (http://xhtml.html.it/guide/lezione/4971/nav/) (<nav> </nav>) introdotta da un elemento <h1> e strutturata con una lista non ordinata. In realtà, il menu di navigazione non deve essere necessariamente inserito nell'<header>, nel nostro esempio non poteva essere fatto altrimenti ma esistono numerosi tipi di layout in cui il menu di navigazione può essere facilmente slegato dagli elementi introduttivi di intestazione del documento. Il template del nostro progetto guida, lo accennavamo nelle precedenti lezioni, è relativo ad un blog. Nel corpo del documento, ad un certo punto, trova posto una sezione che ospita i contenuti principali della pagina, i post. Per definire semanticamente la sezione useremo il tag <section> (http://xhtml.html.it/guide_preview/lezione/4969/section/); ciascun post sarà definito a livello strutturale da un tag <article>(http://xhtml.html.it/guide_preview/lezione/4970/article/):
  • 25.
    <section> <h1>L'ultimopost</h1> <article> [...] </article> </section> Si noti, innanzitutto, come il tag <h1> che fa da titolo principale alla sezione non sia racchiuso in un elemento <header>. Ribadiamo: non è obbligatorio inserire i titoli <hn]]> all'interno di un contenitore<header>. A questo punto, dobbiamo definire due elementi fondamentali per la struttura di un post di blog: il titolo e la data. Sono certamente ausili introduttivi, secondo la definizione da cui siamo partiti. é più che legittimo e sensato, pertanto, racchiuderli in un tag <header>: <section> <h1>L'ultimo post</h1> <article> <header> <time datetime="2010-11-22" pubdate>Lunedì 22 Novembre</time> <h2>Nuove scoperte sul tag video!</h2> </header> <p> [...] </p> </footer> [...] </footer> </article> </section> Ecco quindi come il nostro articolo potrebbe essere rappresentato graficamente: Figura 9 - Struttura del documento: header degli articoli
  • 26.
    I due scenarimostrati rendono bene l'idea rispetto agli utilizzi tipici dell'elemento <header>. La specifica suggerisce come altri contesti d'uso la tavola dei contenuti di una sezione, un form di ricerca o i loghi più rilevanti presenti nel documento. Nella prossima lezione capiremo come utilizzare l'elemento <footer> e quale è la sua rilevanza semantica all'interno di un template. Tabella del supporto sui browser Nuovi tag semantici e strutturali <header> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Footer Funzioni e dati tecnici L'elemento <footer> deve contenere in genere informazioni sulla sezione che lo contiene come: i dati di chi ha scritto i contenuti; collegamenti ai documenti correlati; i dati di copyright; e così via... Riguardo il suo apporto semantico ad una pagina web sembra essere tutto chiaro, ma più complesso è il suo utilizzo a livello pratico:
  • 27.
    Non necessariamente deveessere inserito solo alla fine di un documento. Quando contiene intere sezioni, esse rappresentano: appendici, indici, note, accordi di licenza e altri contenuti del genere. Non introduce una nuova sezione e quindi non è rilevante per l'outliner (http://it.wikipedia.org/wiki/Outliner). All'interno di una pagina web possono essere presenti diversi <footer> anche più di uno per lo stesso elemento. <footer> <small>©2011 Autore contenuto. Design by Designer sito </small> </footer> Footer: esempi concreti Riprendendo il nostro progetto guida, dopo aver definito definito il contenuto dell'<header> (http://xhtml.html.it/guide/lezione/4967/header/), possiamo vedere come il <footer> chiuda il blog distaccandosi dalle altre sezioni in modo molto naturale, racchiudendo al proprio interno una lista che aggiunge informazioni riguardo l'autore della pagina e la data del suo ultimo aggiornamento; infine il tag <small>ridefinito nella specifica dell'HTML 5 (http://www.whatwg.org/specs/web-apps/current- work/multipage/text-level-semantics.html#the-small-element) racchiude il copyright della pagina: <footer> <dl> <dt>Creato da</dt> <dd><address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganotti</a></add ress></dd> <dt>Ultimo aggiornamento</dt> <dd><time datetime="2010-11-23" pubdate>Marted&igrave; 23 Novembre</time></dd> <dd> </dl> <small>Tutti i contenuti sono prottetti dalla licenza creative commons</small> </footer> Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta nel nostro progetto (figura 1): Figura 10 - Struttura del documento: il footer principale
  • 28.
    schema template html5[footer] Così come l'intero documento, ogni articolo del nostro blog avrà un <footer> contenente il nome dell'autore ed altre eventuali informazioni aggiuntive: <section> <h1>L'ultimo post</h1> <article> <header> [...] </header> <p> [...] </p> <footer> <dl> <dt>autore:</dt> <dd><address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganotti</a> </address></dd> <dt>categoria: </dt> <dd><a href="categoria/multimedia">multimedia</a>,</dd> <dt>tags: </dt> <dd><a href="tags/video">video</a>,</dd> <dd><a href="tags/canvas">canvas</a>,</dd> <dt>permalink: </dt> <dd><a href="2010/22/11/nuove-scoperte-sul-tag-video">permalink</a>,</dd> <dt>rank:</dt> <dd><meter value="3.0" min="0.0" max="5.0" optimum="5.0">ranked 3/5</meter></ dd> </dl> </footer>
  • 29.
    </article> </section> È da notarela scelta di inserire le informazioni riguardanti l'autore dell'articolo all'interno del tag <dl>; infatti nella specifica HTML5 questo elemento viene ridefinito come contenitore di metadati e quindi semanticamente corretto all'interno del nostro <footer>. Ecco quindi come il nostro articolo potrebbe essere rappresentato graficamente, tutte le informazioni contenute nel <footer> per comodità abbiamo deciso di chiamarle metadati: Figura 11 - Struttura del documento: footer degli articoli L'elemento <footer> potrebbe essere inserito anche all'inizio di un documento subito dopo il <body> oppure all'apertura di un tag <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/) ma in questi casi non dovrebbe contenere elementi introduttivi riguardo il contenuto della sezione che lo contiene; il suo uso in questa posizione potrebbe essere dovuto solamente a ragioni pratiche come ad esempio la duplicazione del <footer> in fondo alla pagina quando i contenuti della stessa sono molto lunghi: <body> <footer> <a href="#indice">Torna all'indice</a> </footer> <section> [Contenuti molto lunghi...] <section> <section> [Contenuti molto lunghi...] <section> <section>
  • 30.
    [Contenuti molto lunghi...] <section> <footer> <a href="#indice">Torna all'indice</a> </footer> </body> Nella prossima lezione parleremo del tag <section> e della sua importanza nel sezionare la pagina in blocchi semanticamente distinti. Tabella del supporto sui browser Nuovi tag semantici e strutturali <footer> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Section Funzioni e dati tecnici Il tag <section>, secondo la definizione presente nella specifica HTML5, rappresenta una sezione generica di un documento o applicazione. L'elemento <section>, in questo contesto, individua un raggruppamento tematico di contenuti,ed in genere contiene un titolo introduttivo. Vediamo quindi quali sono i punti fondamentali da ricordare riguardo il suo utilizzo: 1. l'elemento <section> non deve essere utilizzato in sostituzione del <div> per impostare graficamente la pagina; inoltre è fortemente consigliato utilizzare i <div> anche quando risultano più convenienti per gli script; 2. l'elemento <section> non deve essere preferito all'elemento <article> quando i contenuti possono essere ripubblicati anche su altri siti; 3. l'elemento <section> e l'elemento <article> non sono indipendenti ed esclusivi: possiamo avere sia un <article> all interno di un <section> che viceversa. <article> <section> <h1>Titolo 1</h1> <p>Testo correlato al titolo 1.</p> </section> <section> <h1>Titolo 2</h1> <p>Testo correlato al titolo 2.</p> </section> </article> L'elemento <section> può essere utilizzato in combinazione con l'attributo cite attraverso il quale è possibile specificare l'url dalla quale si stanno riportando i contenuti.
  • 31.
    Section: esempi concreti Comeabbiamo visto nei capitoli precedenti, il codice del nostro progetto inizia a prendere una forma più chiara e definita: infatti, dopo aver compreso l'utilità semantica dell'<header>(http://xhtml.html.it/guide_preview/lezione/4967/header/) e del <footer> (http://xhtml.html.it/guide_preview/lezione/4968/footer/), capiamo come utilizzare l'elemento <section> all'interno del nostro blog. Per strutturare la pagina raggruppando i contenuti correlati, in ordine decrescente incontriamo le prime due grandi macrosezioni del blog: "l'ultimo post" e "i post meno recenti" contenuti quindi in due<section>: <section> <h1>L'ultimo post</h1> <article> [contenuto del post...] <section> [commenti...] </section> </article> </section> <section> <h1>Post meno recenti</h1> <article> [contenuto del post...] </article> <article> [contenuto del post...] </article> <article> [contenuto del post...] </article> </section> Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta nel nostro progetto: Figura 12 - Struttura del documento: sezioni principali
  • 32.
    Nel nostro progettole <section>, oltre a poter raggruppare i vari <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/), sono presenti anche all'interno del primo <article> per suddividere i commenti dal contenuto del post. La sezione dei commenti a sua volta contiene un'altra sezione contenente il form per l'inserimento di un nuovo commento: <article> [contenuto del post...] <section> <article> [commento1...] </article> <article> [commento2...] </article> <article> [commento3...] </article> <section> [Inserisci un nuovo commento...] </section> </section> </section> </article> In questo modo il post è diviso in maniera molto netta rispetto ai propri contenuti solo con l'ausilio dei tag HTML5, separando quindi i commenti che sono una sezione aggiuntiva eventualmente anche eliminabile dall'argomento principale trattato all'interno dell'articolo.
  • 33.
    Lo schema dell'articoloanalizzato è quindi il seguente: Figura 13 - Struttura del documento: suddivisione semantica del post Il tag <section> rappresenta un elemento che viene considerato una sezione a sé stante dall'outliner (http://it.wikipedia.org/wiki/Outliner) e quindi un blocco con dei contenuti univoci che necessitano di un titolo (<hN>) che li riassuma. Come vedremo nelle prossime lezioni esistono anche altri elementi nelle specifiche HTML5 che sono considerati "contenitori di sezionamento dei contenuti" (http://xhtml.html.it/guide_preview/lezione/4965/un-nuovo-content-model/). Tabella del supporto sui browser Nuovi tag semantici e strutturali <section> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Article Funzioni e dati tecnici Il tag <article> rappresenta una sezione autonoma in un documento, pagina, applicazione o sito; infatti è potenzialmente ridistribuibile o riutilizzabile, e quindi ripubblicabile in parte o interamente in diverse pagine.
  • 34.
    Esso può identificareil post di un forum, un articolo di una rivista o di un giornale, l'articolo di un blog, un commento, un widget interattivo, o qualsiasi cosa che abbia un contenuto indipendente. Prima di vedere un esempio concreto bisogna chiarire alcuni concetti riguardo il suo utilizzo: 1. quando gli elementi <article> sono nidificati, gli <article> interni rappresentano gli articoli che sono in linea di principio relativi al contenuto dell'<article> esterno. Ad esempio, un blog che accetta commenti dagli utenti potrebbe rappresentarli come <article> figli annidati all'interno dell'elemento padre <article>; 2. le informazioni relative all'autore dell'<article> non devono essere replicate all'interno degli elementi nidificati all'interno dello stesso; 3. l'elemento <time> con l'attributo pubdate può essere utilizzato per definire la data di pubblicazione dell'<article>; 4. l'elemento <section> e l'elemento <article> non sono indipendenti ed esclusivi: possiamo avere sia un <article> all interno di un <section> che viceversa. <article> <header> <h1>Titolo articolo</h1> <time pubdate datetime="2011-10-09T14:28-08:00"></time></p> </header> <p>Contenuto dell'articolo</p> <footer> <p>Informazioni riguardo l'autore</p> </footer> </article> In sostanza, anche se nelle specifiche non è espresso, l'elemento <article> rappresenta una forma particolare di <section> (http://xhtml.html.it/guide_preview/lezione/4969/section/). Article: esempi concreti Nella lezione precedente abbiamo diviso i contenuti centrali del blog che stiamo costruendo in due <section>. All'interno della prima <section> possiamo trovare diversi tag <article>: il primo che incontriamo è l'articolo vero e proprio con tanto di <header> (http://xhtml.html.it/guide_preview/lezione/4967/header/) contenente il titolo dell'articolo e la data di pubblicazione e <footer> che all'interno di un <dl> raccoglie i metadati riguardanti l'autore. All'interno dell'<article> padre sono annidati ulteriori <article> contenenti i commenti all'articolo racchiusi in una <section> che li rende semanticamente separati dal contenuto principale. Così come i commenti, anche il form che permette di inserire un'ulteriore commento è inserito all'interno di una <section>. Possiamo quindi facilmente immaginare come il contenuto del nostro <article> possa essere citato o ripubblicato in altri blog indipendentemente dai commenti che ha ricevuto. Ecco quindi il codice dell'<article> sopra descritto: <section> <h1>L'ultimo post</h1><article> <header> <time datetime="2010-11-22" pubdate>Lunedì 22 Novembre</time> <h2>Nuove scoperte sul tag video!</h2> </header>
  • 35.
    <p> Attraverso un utilizzo sapiente del tag canvas è possibile leggere uno stream di dati proveniente da un tag video e <mark>manipolarlo in tempo reale</mark>. </p> <footer> <dl> <dt>autore: </dt> <dd><address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganott i</a></address></dd> <dt>categoria: </dt> <dd><a href="categoria/multimedia">multimedia</a>,</dd> <dt>tags: </dt> <dd><a href="tags/video">video</a>,</dd> <dd><a href="tags/canvas">canvas</a>,</dd> <dt>permalink: </dt> <dd><a href="2010/22/11/nuove-scoperte-sul-tag-video">permalink</a>,</dd > <dt>rank:</dt> <dd><meter value="3.0" min="0.0" max="5.0" optimum="5.0">ranked 3/5</met er></dd> </dl> </footer> <section> <h3>Commenti</h3> <article> <h4> <time datetime="2010-11-22" pubdate>Lunedì 22 Novembre</time> Angelo Imbelli ha scritto: </h4> <p>C'è un bell'esempio sulla rete: effetto ambi-light!</p> <footer> <address><a href="mailto:ambelli@mbell.it">Angelo Imbelli</a></addre ss> </footer> </article> <article> <h4> <time datetime="2010-11-23" pubdate>Martedì 23 Novembre</time> Sandro Paganotti ha scritto: </h4> <p>Bellissimo! Grazie per la segnalazione!</p> <footer> <address><a href="mailto:sandro.paganotti@gmail.com">Sandro Paganott i</a></address> </footer> </article> <section> <h4>Inserisci un nuovo commento:</h4> <form> [ campi form per inserire un nuovo commento] </form>
  • 36.
    </section> </section> </article> </section> Nel seguente schema abbiamo realizzato graficamente come si presenta il nostro articolo morfologicamente: Figura 14 - Struttura del documento: suddivisione semantica degli articoli C'è da notare che anche se il tag <article> rappresenta un elemento che viene considerato una sezione a sé stante dall'outliner (http://it.wikipedia.org/wiki/Outliner) e quindi un blocco con dei contenuti unici e un titolo univoco (quindi per ogni blocco avremmo potuto utilizzare un titolo racchiuso in un <h1>), abbiamo preferito rispettare comunque l'ordine decrescente per i titoli in modo da rendere i contenuti accessibili anche agli screen reader che al momento non hanno ancora implementato l'algoritmo outliner. Nella prossima lezione parleremo del tag <nav> e della sua fondamentale importanza all'interno di una pagina in HTML5. Tabella del supporto sui browser Nuovi tag semantici e strutturali
  • 37.
    <article> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Nav Funzioni e dati tecnici Il tag <nav> è uno degli elementi introdotti nelle specifiche HTML5 di più facile comprensione. Infatti, rappresenta una sezione di una pagina che contiene link (collegamenti) ad altre pagine o a parti interne dello stesso documento; quindi, in breve, una sezione contenente link di navigazione. A questo punto potremmo potremmo porci una domanda: come mai un elemento così scontatamente fondamentale è stato introdotto solamente adesso? La risposta potrebbe essere che, così come per i tag visti nelle precedenti lezioni, finalmente si è cercato di incentivare l'uso di standard condivisi proponendo elementi che possano aiutare gli sviluppatori proprio perché molto vicini ai modelli mentali oramai assimilati dagli esperti e di semplice comprensione per i novizi del mestiere. Per poter utilizzare correttamente l'elemento <nav> dobbiamo ricordare i seguenti punti: 1. solo sezioni che sono costituite da grandi blocchi di navigazione sono appropriati per l'elemento <nav>; 2. i link a pie' di pagina e le breadcumb non devono essere inseriti in una sezione <nav>; 3. l'elemento <nav> non sostituisce i link inseriti in elementi come gli <ul> o gli <ol> ma deve racchiuderli. <nav> <ul> <li>Questo è un link</li> <li>Questo è un link</li> <li>Questo è un link</li> <li>Questo è un link</li> [...] </ul> </nav> Nav: esempi concreti Prima di spiegare in che modo l'elemento <nav> può essere inserito nel progetto che abbiamo preso come base, riassumiamo brevemente i tag spiegati nelle lezioni precedenti: Con l'elemento <header> (http://xhtml.html.it/guide_preview/lezione/4967/header/) abbiamo indicato il titolo introduttivo del blog più i titoli dei singoli articoli. Con il <footer> (http://xhtml.html.it/guide_preview/lezione/4968/footer/) abbiamo racchiuso le informazioni relative agli autori dei contenuti, i metadati e il copyright. Con l'elemento <section> (http://xhtml.html.it/guide_preview/lezione/4969/section/) abbiamo strutturato la parte centrale della pagina dividendola in due sezioni semanticamente distinte. Infine abbiamo utilizzato <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/) per racchiudere i contenuti degli articoli. A questo punto non possiamo che inserire i link presenti nell'<header> all'interno del tag <nav>:
  • 38.
    <header> <hgroup> <h1>We5! Il blog della guida HTML5</h1> <h2>Approfittiamo fin da oggi dei vantaggi delle specifiche HTML5!</h2> </hgroup> <nav> <h1>Navigazione:</h1> <ul> <li><a href="/home">Home</a></li> <li><a href="/about">Gli autori</a></li> <li><a href="/refactoring">Il progetto four2five</a></li> <li><a href="/archives">Archivio</a></li> </ul> </nav> </header> Da notare la presenza del titolo <h1> che serve a specificare più dettagliatamente la funzione del nostro blocco di link e a conferirgli un titolo valido anche per l'outliner. Il tag <nav>, infatti, rappresenta un elemento che viene considerato una sezione a sé stante dall'outliner (http://it.wikipedia.org/wiki/Outliner) e quindi un blocco con dei contenuti univoci che necessitano di un titolo che li riassuma. Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta nel nostro progetto: Figira 15 - Struttura del documento: visualizzazione grafica del tag nav In realtà (come già specificato nel paragrafo <header>) il menu di navigazione non deve essere necessariamente inserito nel <header>, nel nostro esempio non poteva essere fatto altrimenti ma
  • 39.
    esistono numerosi tipidi layout in cui il menu di navigazione può essere facilmente slegato dagli elementi introduttivi di intestazione del documento. Nel nostro esempio l'elemento <nav> è presente anche nella colonna laterale del blog (<aside>) e racchiude un menu che ha come link le categorie nelle quali sono inseriti i vari articoli: <aside> <h1>Sidebar</h1> <section> <h2>Ricerca nel form:</h2> <form> [form di ricerca dei contenuti...] </form> </section> <nav> <h2>Categorie</h2> <ul> <li><a href="/categoria/multimedia">multimedia</a></li> <li><a href="/categoria/text">marcatori testuali</a></li> <li><a href="/categoria/form">forms</a></li> </ul> </nav> </aside> Per comprendere quale è la funzione dell'elemento <aside> che contiene il menu laterale non ci resta quindi che leggere la prossima lezione. Tabella del supporto sui browser Nuovi tag semantici e strutturali <nav> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Aside Funzioni e dati tecnici L'elemento <aside> rappresenta una sezione di una pagina costituita da informazioni che sono marginalmente correlate al contenuto dell'elemento padre che la contiene, e che potrebbero essere considerate distinte da quest'ultimo. Questo è ciò che viene indicato nelle specifiche HTML5, ma è facile immaginare l'utilità del tag <aside> semplicemente pensandolo come un contenitore di approfondimentoin cui possiamo inserire gruppi di link, pubblicità, bookmark e così via. <aside> <h1>Questi sono dei contenuti di approfondimento marginali rispetto al contenuto pri ncipale</h1> <nav> <h2>Link a pagine correlate al contenuto</h2> <ul>
  • 40.
    <li>Informazione correlata alcontenuto</li> <li>Informazione correlata al contenuto</li> <li>Informazione correlata al contenuto</li> </ul> </nav> <section> <h2>Pubblicità</h2> [immagini pubblicitarie] <section> </aside> Aside: esempi concreti Ritornando al nostro progetto guida, dopo aver definito il contenuto dell'elemento <nav>, possiamo analizzare la parte di codice in cui abbiamo utilizzato il tag <aside>: <aside> <h1>Sidebar</h1> <section> <h2>Ricerca nel form:</h2> <form name="ricerca" method="post" action="/search"> <label> Parola chiave: <input type="search" autocomplete="on" placeholder="article, section, ..." name ="keyword" required maxleng th="50"> </label> <input type="submit" value="ricerca"> </form> </section> <nav> <h2>Categorie</h2> <ul> <li><a href="/categoria/multimedia">multimedia</a></li> <li><a href="/categoria/text">marcatori testuali</a></li> <li><a href="/categoria/form">forms</a></li> </ul> </nav> </aside> Nel seguente schema abbiamo realizzato graficamente ciò che il codice semanticamente rappresenta nel nostro progetto: Figura 16 - Struttura del documento: visualizzazione grafica del tag aside
  • 41.
    Come possiamo notare,il form per la ricerca di parole chiave e i link alle categorie presenti nel nostro blog non sono informazioni particolarmente rilevanti per il contenuto centrale della nostra pagina, pertanto possiamo facilmente separarli con l'elemento <aside> che li qualifica come contenuti marginali. Nel nostro caso abbiamo utilizzato un <aside> per contenere la colonna sinistra del blog, ma in realtà, visto che in diversi siti va di moda la presenza di footer molto grossi con diversi link, consigliamo di utilizzare l'elemento <aside> in combinazione con il tag <nav> che potrebbe essere la soluzione migliore per questa tipologia di contenuti dato che, come abbiamo potuto constatare nella lezione dedicata al footer, ciò non è possibile farlo all'interno del tag <footer>. Così come gli elementi <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/), <section> (http://xhtml.html.it/guide_preview/lezione/4969/section/) e <nav>(http://xhtml.html.it/guide_preview/lezione/4971/nav/) anche l'<aside> appartiene alla categoria dei "contenitori di sezionamento dei contenuti" (http://xhtml.html.it/guide_preview/lezione/4965/un-nuovo-content-model/) in quanto per l'outliner necessita di un titolo che riassuma i propri contenuti. Ricordiamo però che non è obbligatorio inserire un titolo per gli elementi che vengono considerati delle sezioni a sé stanti dall'outliner, infatti in questo caso queste sezioni rimarrebbero senza titolo ma non genererebbero nessun errore di validazione. Vediamo quindi ora che abbiamo completato la struttura del blog come è l'outline del nostro documento Outline (esempi/outline_progetto/outline.html). Nella prossima lezione comprenderemo come usare l'elemento <hgroup> e la sua importanza per l'outline di un documento in HTML5. Tabella del supporto sui browser
  • 42.
    Nuovi tag semanticie strutturali <aside> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Hgroup Funzioni e dati tecnici L'elemento <hgroup> rappresenta l'intestazione di una sezione. L'elemento viene utilizzato per raggruppare un insieme di elementi h1-h6, quando il titolo ha più livelli, come sottotitoli, titoli alternativi o slogan. <hgroup> <h1>Questo è il titolo</h1> <h2>Questo è un sottotitolo</h2> </hgroup> La vera importanza del tag <hgroup> è che maschera l'outline dell'elemento padre che lo contiene; infatti, l'algoritmo dell'outliner riconosce come un titolo solamente l'heading con il valore più alto e considera tutti gli altri elementi sottotitoli. Esempio: <article datetime="2010-11-22" pubdate > <header> <hgroup> <h2>Le prospettive per il futuro del web</h2> <h1>L'HTML 5 rivoluzionerà il mondo del web</h1> </hgroup> </header> <p>Presto il web sarà pieno di siti e applicazioni che saranno in grado di mettere in crisi le più grandi case di desktop application...</p> <footer> <p>Pinco pallino</p> </footer> </article> Se facessimo l'outline della pagina HTML contenente questo <article> (http://xhtml.html.it/guide_preview/lezione/4970/article/) ci restituirebbe come titolo della sezione solamente l'<h1> contenuto nell'<hgroup> (non considerando l'<h2> anche se posto prima nel codice) poiché è il tag con il valore più alto all'interno della sezione. Senza l'elemento <hgroup> i due titoli verrebbero considerati entrambi sullo stesso livello d'importanza come titoli dell' <article>. Nella prossima lezione parleremo del tag <mark> e della sua utilità nell'evidenziare parti particolarmente importanti del testo. Tabella del supporto sui browser
  • 43.
    Nuovi tag semanticie strutturali <hgroup> 9.0+ 3.0+ 3.1+ 5.0+ 10.0+ Mark Funzioni e dati tecnici L'elemento <mark> rappresenta una parte di un testo segnato o evidenziato all'utente a causa della sua rilevanza anche in un altri contesti. Esso, in un testo in prosa, indica un punto culminante che non era originariamente presente, ma che è stato aggiunto per attirare l'attenzione del lettore su una parte del testo che potrebbe non essere stata considerata rilevante dall'autore originale quando il contenuto è stato scritto. Questa definizione abbastanza complessa in realtà può essere semplificata di molto chiarendoci le idee con un esempio: immaginiamo di cercare una determinata parola chiave in diverse pagine web e che la parola che abbiamo cercato nel motore di ricerca, ci venga evidenziata all'interno della pagina che andiamo a visualizzare; ciò che abbiamo appena descritto è la situazione ideale per l'utilizzo del tag <mark>poiché con quest'ultimo dobbiamo racchiudere la parola ricercata segnalandola graficamente all'utente. <p>Senza <mark>plug in</mark> di terze parti il web potrebbe diventare per noi sviluppatori più democratico; con le nuove API HTML5 non abbiamo più bisogno di diversi <mark>plug in</mark> di terze parti che sino ad ora erano indispensabili per i contenuti multimediali</p> Allo stato attuale non esiste un tag HTML standard utilizzato per evidenziare le parole chiave agli utenti che utilizzano i motori di ricerca per navigare: Google utilizza il tag <em>, Bing il tag <strong>, Yahoo il tag <b> e così via. Si spera che con l'introduzione dell'elemento <mark> le cose possano cambiare. Nella prossima lezione parleremo del tag <time>, un'altra delle novità dell'HTML5. Tabella del supporto sui browser Nuovi tag semantici e strutturali <mark> 9.0+ 4.0+ 5.0+ 5.0+ 11.0+ Time e gli attributi pubdate e datetime Funzioni e dati tecnici L'elemento <time> rappresenta il tempo su un orologio di 24 ore, o una data precisa nel calendario Gregoriano accompagnata opzionalmente con un orario e una differenza di fuso orario. Questo elemento è inteso come un modo moderno per codificare le date e gli orari in maniera leggibile anche per i computer. Ad esempio, i browser saranno in grado di offrire la possibilità di
  • 44.
    aggiungere promemoria peri compleanni o gli eventi in programma in una web application che funziona da calendario. <p>Oggi pomeriggio penso che sarò lì per le <time>15:00</time></p> Prima di inserire l'elemento <time> nelle nostre pagine in HTML5 dobbiamo comprendere quali sono i contesti in cui è sconsigliato utilizzarlo: non bisogna inserire nel tag <time> le date che non possono essere determinate con precisione; ad esempio: "un giorno nel lontano inverno del '68","da quando è nato il primo uomo"...; non bisogna inserire nel tag <time> le date prima dell'introduzione del calendario Gregoriano. L'elemento <time> può possedere l'attributo pubdate che è di tipo booleano; la sua presenza indica che la data presente nel tag <time> è anche la data nella quale è stato scritto l'<article>(http://xhtml.html.it/guide_preview/lezione/4970/article/) padre più vicino, e nel caso non esistesse un <article> padre allora essa è riferita alla creazione dei contenuti dell'intero documento. Ovviamente un elemento che possiede l'attributo pubdate necessita di una data. Per ciascun <article> può esserci solo un singolo tag <time> con pubdate e la stessa cosa vale per l'intero documento. Possiamo specificare in maniera più dettagliata una data aggiungendo l'attributo datetime: il valore dell'attributo deve essere una "stringa valida" (http://www.whatwg.org/specs/web- apps/current-work/multipage/common-microsyntaxes.html#valid-global-date-and-time-string) del tipo (ANNO-MESE-GIORNO-ORE:MINUTI:SECONDI.MILLISECONDI-FUSO ORARIO). se l'attributo datetime non è presente allora il contenuto testuale del'tag <time> deve essere una "stringa valida" (http://www.whatwg.org/specs/web-apps/current- work/multipage/common-microsyntaxes.html#valid-global-date-and-time-string). <time pubdate datetime="2011-01-20">20 Gennaio</time> Dobbiamo specificare che l'attributo pubdate in quanto di tipo booleano può essere inserito anche nel seguente modo: <time pubdate="pubdate" datetime="2011-01-20">20 Gennaio</time> Nella prossima lezione vedremo in quali occorrenze utilizzare il tag <meter>. Tabella del supporto sui browser Nuovi tag semantici e strutturali <time> No No No No No Meter Funzioni e dati tecnici L'elemento <meter> rappresenta una misura scalare all'interno di un intervallo noto, o un valore frazionario.
  • 45.
    Il tag <meter>è anche utilizzato come un indicatore di livello. <meter value="6" max="8">6 blocchi utilizzati (su un totale di 8)</meter> Vediamo alcuni contesti in cui non deve essere utilizzato: quando indica lo stato di una barra di progresso; quando i valori rappresentati sono di tipo arbitrario a scalare; ad esempio non deve segnalare un peso o un'altezza a meno che non vi sia un valore massimo riconosciuto. Esistono 6 attributi per determinare il valore semantico dell'elemento <meter>: 1. min: indica il valore limite minimo disponibile; 2. max: indica il valore limite massimo disponibile; 3. value: indica il valore dell'elemento; 4. low: indica un valore che viene considerato basso; 5. high: indica un valore che viene considerato alto; 6. optimum: indica un valore che viene considerato "ottimale"; se è più alto del valore specificato nell'attributo high indica che il valore più alto è il migliore,viceversa è più basso del valore specificato nell'attributo low indica che il valore più basso è il migliore. Se non è specificato un valore minimo e un valore massimo questi sono di default rispettivamente 0 e 1. Ad oggi l'unico browser che renderizza il tag <meter> è Google Chrome presentandolo graficamente come una barra di progresso, quindi gli sviluppatori sono fortemente incoraggiati a specificare il suo valore in formato testuale al suo interno. Nella prossima lezione illustreremo come utilizzare l'elemento <progress> e la sua utilità nel caricare i contenuti dei documenti web. Tabella del supporto sui browser Nuovi tag semantici e strutturali <meter> No No No 8.0+ 11.0+ Progress Funzioni e dati tecnici L'elemento <progress> rappresenta lo stato di completamento di un compito e può essere di due tipi: indeterminato: indica che il compito (task) è in fase di completamento, ma che non è chiaro quanto ancora resta da fare prima che l'operazione sia completata (ad esempio perché l'operazione è in attesa della risposta di un host remoto); determinato quando è possibile ricavare un numero quantificabile nel range da zero a un massimo, in modo da ottenere la percentuale di lavoro completato rispetto al totale(ad esempio un preloader di un'immagine). Esistono due attributi che determinano lo stato di completamento dell'attivita del tag <progress>.
  • 46.
    L'attributo value, chespecifica la quantità di lavoro completata, e l'attributo max, che specifica la quantità di lavoro richiesta in totale. Le unità sono arbitrarie e non specificate. Tuttavia, i valori passati come attributi del tag <progress> non sono renderizzati e quindi dovrebbero comunque essere inseriti in forma testuale nell'HTML in modo da poter fornire un feedback più preciso agli utenti; inoltre questo elemento attualmente non viene renderizzato dalla maggior parte dei browser ed è quindi ancora sconsigliato il suo utilizzo. <section> <p>Caricamento: <progress id="mioLoader" max="100" value="30"><span>30</span>%</prog ress></p> </section> Vediamone adesso un esempio concreto in questa demo (http://www.html.it/guide/esempi/html5/esempi/lezione_progress/progress.html) (funziona nelle più recenti versioni di Opera e Google Chrome). Gli attributi value e max, se presenti, devono essere valori validi (http://www.whatwg.org/specs/web- apps/current-work/multipage/common-microsyntaxes.html#valid-floating-point-number). L'attributovalue, se presente, deve avere un valore uguale o maggiore di zero e minore o uguale al valore dell'attributo max, se presente, o 1.0, in caso contrario. L'attributo max, se presente, deve avere un valore maggiore di zero. Ovviamente, l'elemento <progress> deve essere utilizzato solamente per indicare lo stato in fase di progressione di un compito; per indicare quantitativamente la misura di un'oggetto o di uno stato non in progressione bisogna utilizzare l'elemento <meter> (http://xhtml.html.it/guide_preview/lezione/4976/meter/). Data la complessità dell'argomento e la costante variazione delle specifiche a riguardo consigliamo di consultare il sito del WHATWG (http://www.whatwg.org/specs/web-apps/current- work/multipage/the-button-element.html#the-progress-element) per un maggiore approfondimento. Nella prossima lezione descriveremo brevemente gli altri tag semantici introdotti nella specifica HTML5. Tabella del supporto sui browser Nuovi tag semantici e strutturali <progress> No No No 8.0+ 11.0+ Altri tag Abbiamo scelto di riassumere brevemente in un'unica lezione diversi tag in quanto il costante aggiornamento delle specifiche non ci consente di poterli descrivere dettagliatamente, almeno sino a quando queste ultime non saranno rilasciate ufficialmente; inoltre, ne sconsigliamo l'utilizzo almeno sino a quando i browser non inizieranno a supportarli in maniera standard.
  • 47.
    I tag <figure>e <figcaption> Nell'elemento <figure> possiamo racchiudere dei contenuti, opzionalmente con una didascalia (<figcaption>), che rappresentano delle singole unità indipendenti rispetto al contenuto principale; ad esempio possiamo utilizzare l'elemento <figure> per annotare le illustrazioni, schemi, foto, elenchi di codice, etc... ovvero tutto ciò che fa parte del contenuto principale ma che potrebbe essere anche allontanato dallo stesso senza intaccarne il senso. L'elemento <figcaption> quindi rappresenta una didascalia o una leggenda per l'elemento <figure> padre. Esempio: <figure> <img src="benevenuti.jpg" alt=""> <figcaption> Foto di benvenuto <small>© Diritti riservati</small> </figcaption> </figure> È importante notare che l'attributo alt è vuoto poiché l'immagine è descritta nel tag <figcaption> ed è stato usato il tag <small> per il copyright. Il tag <embed> L'elemento <embed> è già utilizzato da anni per inserire nel codice HTML contenuti interattivi o multimediali (tipicamente Flash, Quicktime, etc.). Questo elemento, però, era stato deprecato nelle specifiche HTML 4 in favore del tag <object>. Ora è stato reintrodotto perché, nonostante la potenza delle nuove API HTML5, si pensa che al momento non tutto ciò che si riesce ad ottenere con plug-in di terze parti possa essere realizzato in HTML5. Inoltre, si è cercato di mantenere la retrocompatibilità con applicazioni basate sull'utilizzo di questo elemento. Il tag <ruby> Il tag <ruby> è usato per specificare le annotazioni Ruby, che vengono utilizzate nella tipografia orientale in combinazione con il testo principale. Il tag <wbr> Il tag <wbr> definisce dove, in una parola, sarebbe opportuno aggiungere un a capo. Infatti, quando una parola è lunga, utilizzando l'elemento <wbr> il browser comprenderà dove eventualmente sarà possibile inserire un a capo. I tag <command> e <menu> Entrambi sono elementi molto interessanti: permettono di definire barre degli strumenti o menu di scelta rapida per le nostre applicazioni, con le icone e i relativi comandi che possono essere eseguiti da script. Il tag <command> rappresenta un'azione che l'utente può richiamare in qualche modo. Esso è visibile solo se inserito all'interno di un elemento <menu>. In caso contrario, non verrà visualizzato,
  • 48.
    ma può essereutilizzato per specificare un tasto di scelta rapida. Al momento nessun browser supporta questi tag. I tag <details> e <summary> I tag <details> e <summary> rappresentano un widget informativo da cui l'utente può ottenere informazioni supplementari o controlli aggiuntivi. Nel tag <summary>,che è contenuto all interno del tag<details>, deve essere inserita una sintesi del contenuto del widget o anche una legenda. I contenuti dell'elemento <details> possono essere mostrati o meno dal browser grazie all'attributo open, di tipo booleano. Anche questi tag non sono supportati ancora da nessun browser. Il tag <keygen> L'elemento <keygen> rappresenta un generatore di chiavi numeriche all'interno di un form. Quando si effettua l'invio di un form contenente il tag <keygen>, la chiave privata viene memorizzata nel keystore locale e la chiave pubblica viene confezionato e inviata al server. L'elemento è già supportato da diversi browser ma manca il suo supporto in IE. Il tag <output> L'elemento <output> ci restituisce il risultato di un calcolo. Tabella del supporto sui browser Nuovi tag semantici e strutturali <figure> 9.0+ 4.0+ Nightly build Nightly build 11.0+ <figcaption> 9.0+ 4.0+ Nightly build Nightly build 11.0+ <ruby> 5.5+ No 5.0+ 5.0+ No <wbr> No No No No No <command> No No No No No <menu> No No No No No <details> No No No No No <summary> No No No No No <keygen> No 1.0+ 2.0+ 2.0+ 7.0+ <output> No 4.0+ Nightly build Nightly build 9.0+ I form in HTML5: una panoramica Quando Javascript fu introdotto nelle pagine web, fu subito implementato per assolvere a due compiti: il rollover delle immagini e la validazione dei form. Mentre il primo era un mero problema visuale (e in parte anche di usabilità), il secondo utlizzo comune di Javascript era necessario perché permetteva di eliminare (o per meglio dire arginare) l'invio di form mal compilati o con errori, evitando all'utente l'attesa non necessaria tra il l'invio,
  • 49.
    l'eventuale fallita validazionedei dati inviati e il conseguente reload della pagina. Ovviamente nessun programmatore esperto si fida di quello che "arriva" dai form. Si rende necessario, quindi, il controllo lato server dei dati inviati. Nonostante questo, l'implementazione di controlli client side è una prassi comune. Con HTML5 avviene per la validazione e in generale per l'interazione tramite i moduli, quello che è accaduto con elementi come video e audio. La specifica introduce funzionalità e tecniche che permettono allo sviluppatore da affidarsi unicamente al linguaggio di markup, senza dove ricorrere a Javascript o a plugin esterni. Quello che andremo a descrivere nelle prossime lezioni era originariamente parte della specifica del WHATWG chiamata Web Form 2.0 (http://www.whatwg.org/specs/web-forms/current-work/), aggiornamento della specifica del W3C denominata Web Form 1.0 (http://www.w3.org/TR/html4/interact/forms.html). Attualmente Web Form 2.0 è un progetto chiuso in quanto ora si lavora sulla parte dedicata ai form (http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html) della specifica HTML5. Lavorare con i form è stato finora molto semplice: si inizia tutto con il tag <form>, si prosegue con gli <input type="text">, a volte con gli <input type="password"> e magari con una <textarea> o una<select> o <input type="radio">, e si finisce sempre con <input type="submit">. Questi tipi di input, però, non si adattano alla varietà di dati possibilmente richiesti. Spesso ci troviamo a richiedere indirizzi e-mail, numeri di telefono, date e molti altri tipi di dati ancora. Non solo, ci sono anche le validazioni di consistenza derivate da questi dati, per esempio il fatto che l'indirizzo e-mail deve contenere la @ o che le date devono essere in un formato preciso, oltre a validazioni dipresenza e obbligatorietà. È soprattutto a questo livello, come si accennava, che entravano in gioco Javascript e le validazioni client side. Tutto questo con HTML5 finisce grazie all'inserimento nella specifica di nuovi type come email, number, range, search e di diversi nuovi attributi come placeholder, autofocus, require, min, max, etc. Questi nuovi tipi di input e attributi sono molto apprezzati dalla comunità dei programmatori perch´ oltre a fornire un aumento dell'accessibilità dei form (anche in ambito mobile) sono pronti per l'uso. Non tutti funzionano perfettamente su tutti i browser, ma senza nessun hack, senza Javascript che ne controlla il funzionamento, degradano bene, anche in preistorici browser. Possiamo allora iniziare. Nuovi attributi per i form: autofocus, placeholder e form I tipi di attributi Prima di iniziare cominciamo con il dire che esistono diversi tipi di attributi per i tag HTML. Nel particolare: Attributi booleani: questi tipi di attributi hanno due stati, lo stato vero e lo stato falso. Il primo è rappresentato dalla presenza dell'attributo mentre il secondo, lo stato falso, è rappresentato dalla sua assenza. Il valore dell'attributo, quindi, non è necessario (es. disabled) anche se sono accettati comunque alcuni valori. Nello specifico possiamo valorizzarlo con una stringa vuota (es.disabled="") oppure con il nome dell'attributo senza
  • 50.
    spazi iniziali ofinali (es. disabled=disabled o disabled="disabled"). Attributi enumerati: questi tipi di attributi possono prendere come valore un insieme finito di parole chiavi (es. l'attributo type per il tag ul può assumere i valori disc, square e circle). A volte le parole chiave possono avere sinonimi. Una parola chiave potrebbe essere anche "", cioè il valore nullo. Attributi generici: questo tipo di attributo, invece, può prendere qualsiasi valore. Esempi di questi attributi sono class, id e placeholder. Dopo questa premessa vediamo subito all'opera i primi tre dei nuovi attributi per i form definiti in HTML5: autofocus, placeholder e form. autofocus L'attributo autofocus è un attributo booleano e serve a impostare il focus su uno specifico elemento del form appena la pagina è caricata. Un esempio canonico è quello della home page di Google: appena viene caricata il focus è automaticamente impostato sul campo per la ricerca. Ovviamente solo un elemento per pagina può avere l'attributo autofocus. Questo attributo deve essere usato con parsimonia in quanto alcuni utenti, come quelli che usano la barra spaziatrice per scorrere la pagina, potrebbero trovare questa costrizione fastidiosa, preferendo un atteggiamento più soft. È per questo motivo che autofocus dovrebbe essere usato solo nelle pagine che contengono solamente (o principalmente) form (come per esempio pagine di login o di ricerca). Esempi d'uso dell'attributo autofocus Ecco comunque un esempio pratico: <form action="/" method="get"> <input type="text" name="myname" id="myid" autofocus> <input type="submit" value="Invia"> </form> Alternative per l'implementazione dell'attributo autofocus Per far sì che l'attributo autofocus funzioni su tutti i browser è sufficiente modificare il codice in questo modo: <form action="/" method="get"> <input type="text" name="myname" id="myid" autofocus > <script> if (!("autofocus" in document.createElement("input"))) { document.getElementById("myid").focus(); } </script> <input type="submit" value="Invia" > </form>
  • 51.
    placeholder Il valore dell'attributoplaceholder è visualizzato all'interno di un input, o di una textarea, fin quando il campo è vuoto e non guadagna il focus (tramite il click o spostandosi su di esso il tasto Tab). Semanticamente l'attributo placeholder dovrebbe essere valorizzato con valori accettabili dal form, dovrebbe, in altre parole, contenere un esempio di ciò che l'utente andrà a scrivere nel campo. Spesso il placeholder viene utilizzato erroneamente al posto della label descrivendo quindi cosa dovrebbero inserire. Esempi d'uso dell'attributo placeholder All'interno del nostro progetto guida si fa molto uso dell'attributo placeholder. Per esempio: <form name="ricerca" method="post" action="/search"> <label> Parola chiave: <input type="search" autocomplete="on" placeholder="article, section, ..." name="k eyword" required maxlength="50"> </label> <input type="submit" value="ricerca"> </form> Creando un risultato come nella seguente immagine: Figura 17 - Resa visiva di un input con l'attributo placeholder Alternative per l'implementazione dell'attributo placeholder Non sarà semplice scrivere una funzione che faccia funzionare il placeholder su tutti i browser e per tutti gli input, ma data la natura dell'attributo non dovrebbe essere nemmeno necessario. Ad ogni modo, se volessimo farlo funzionare su tutti i browser, avremmo diverse soluzioni, alcune più semplici altre più complesse. Proponiamo qui l'alternativa proposta da Andrew January sul suo blog (http://www.morethannothing.co.uk/2010/01/placeholder-text-in-html5-a-js-fallback/). Nello script placeholder.js (http://www.html.it/guide/esempi/html5/esempi/lezione_placeholder/placeholder.js) abbiamo modificato i commenti mettendoli in italiano di modo che sia di più facile comprensione. Per farlo funzionare si ricordi di includere la libreria jQuery nella pagina. form Un nuovo attributo che si può inserire in tutti gli input è appunto form, anche se sfortunatamente non è molto supportato e non esiste una vera e propria alternativa. Questo nuovo attributo serve per specificare a quale form, o a quali form, l'input fa riferimento.
  • 52.
    Richiede come valorel'id del form a cui vogliamo che faccia riferimento, o nel caso di più form, gli idseparati da uno spazio, " ". Esempi d'uso dell'attributo form Ecco comunque un esempio pratico: <form action="/" method="get" id="myFormId"> <input type="text" name="myname"> <input type="submit" value="Invia"> </form> <input type="text" name="mysurname" form="myFormId"> Nonostante l'input con id mysurname sia fuori dal form, inviando il form inviamo anche il suo valore grazie al suo attributo form. L'uso di questo attributo è sconsigliato a meno di non conoscere con certezza il browser dell'utente che utilizzarà la nostra applicazione. Alternative per l'implementazione dell'attributo form Come detto nella descrizione dell'attributo, non c'è una vera e propria alternativa. Siamo di fronte a due soluzioni: La prima è quella di agire sul DOM e spostare gli elementi interessati all'interno del form. La seconda è quella di assegnare una funzione al submit del form che cerchi i valori degli input interessati e per ognuno di questi inserire nel DOM degli <input type="hidden"> e quindi procedere con l'invio. Tabella del supporto sui browser Form: nuovi attributi autofocus No 4.0+ 4.0+ 2.0+ 9.0+ placeholder No 4.0+ 4.0+ 2.0+ 11.0+ form No 4.0+ Nightly build Nightly build 9.0+ Nuovi attributi dei form per la validazione La validazione dei form è forse l'argomento relativo ai form più importante. Vediamo subito in dettaglio che cosa ci propone la nuova specifica HTML5. required required è un attributo booleano e serve a rendere obbligatoria la compilazione dell'elemento a cui è applicato. La condizione viene valutata al submit del form. Ecco un esempio di utilizzo:
  • 53.
    <form name="commenti" method="post"action="/141/comments"> [...] <label>Messaggio: <textarea name="messaggio" placeholder="Scrivi qui il tuo messaggio (max 300 carat teri)" maxlength="300" required></textarea> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> autocomplete Anche se questo attributo non è esattamente un attributo per le validazioni, abbiamo deciso di inserirlo in questa lezione in quanto previene un comportamento dei browser non sempre voluto: spesso i browser riempiono i campi da inserire in maniera automatica. Questo comportamento è nella maggior parte dei casi un comportamento comodo, però in alcuni casi è fastidioso. Si pensi per esempio ai campi password o ai campi del codice della banca: probabilmente non vogliamo che il browser li completi in automatico. Ecco che arriva in nostro soccorso l'attributo autocomplete che è un attributo enumerato. In particolare i valori che accetta sono: on: indica che il valore non è particolarmente sensibile e che il browser può compilarlo in maniera automatica; off: indica che il valore è particolarmente sensibile o con un tempo di scadenza (il codice di attivazione di un servizio, per esempio) e che quindi l'utente deve inserirlo manualmente ogni volta che lo compila; nessun valore: indica in questo caso di usare il valore di default scelto dal browser (normalmente on). Ecco un esempio di utilizzo: <form name="commenti" method="post" action="/141/comments"> [...] <label>Nick: <input type="text" name="nickname" autocomplete="on" required pattern="[a-z]{1}[a-z_]{2,19}" title="Un nickname è composto da lettere minuscole e '_'; Sono consentiti da 3 a 20 caratteri." placeholder="your_nickname"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> multiple
  • 54.
    In molti casiabbiamo bisogno che l'utente possa inserire più valori per lo stesso input (per esempio se gli stiamo chiedendo gli indirizzi e-mail di amici a cui inviare un invito). Ecco che arriva in nostro soccorso l'attributo multiple che è un attributo booleano. Un esempio di utilizzo: <form> <label>eMail a cui inviare l'invito: <input type="email" multiple name="friendEmail" </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form> pattern In molti casi abbiamo bisogno di validare un determinato input verificando che il valore inserito sottostia a determinate regole di creazione (per esempio potremmo volere che il campo password non contenga spazi). Possiamo contare in questi casi sull'attributo pattern. Il valore di pattern, se specificato, deve essere una espressione regolare valida (http://www.ecma- international.org/publications/standards/Ecma-262.htm). Se viene indicato l'attributo pattern bisognerebbe indicare anche il title per dare una descrizione del formato richiesto, altrimenti il messaggio di errore sarà generico e probabilmente di poco aiuto. Ecco un esempio di utilizzo: <form name="commenti" method="post" action="/141/comments"> [...] <label>Nick: <input type="text" name="nickname" autocomplete="on" required pattern="[a-z]{1}[a-z_]{2,19}" title="Un nickname è composto da lettere minuscole e '_'; Sono consentiti da 3 a 20 caratteri." placeholder="your_nickname"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> In questo esempio si sta richiedendo che lo username sia una parola in minuscolo composta solo da lettere e da "_", di una lunghezza minima di 3 caratteri e massima di 20 e che non cominci con "_" min e max
  • 55.
    I valori mine max descrivono rispettivamente il valore minimo e massimo consentito. Il valore di max deve essere maggiore del valore di min se indicato. Questi attributi si applicano sia alle date (come detetime (http://xhtml.html.it/guide_preview/lezione/4986/nuovi-tipi-di-input-per-la-gestione-delle-date/), date(http://xhtml.html.it/guide_preview/lezione/4986/nuovi-tipi-di-input-per-la-gestione-delle- date/), month (http://xhtml.html.it/guide_preview/lezione/4986/nuovi-tipi-di-input-per-la-gestione- delle-date/)) sia ai numeri (number (http://xhtml.html.it/guide_preview/lezione/4987/input-type- number/) e range (http://xhtml.html.it/guide_preview/lezione/4988/input-type-range/)). Per maggiore dettagli rimandiamo alle lezioni che trattano in maniera specifica questi nuovi tipi di input. Ecco un esempio di utilizzo: <form name="commenti" method="post" action="/141/comments"> [...] <label>Età: <input type="number" name="age" min="13" max="130" step="1"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> In questo caso stiamo chiedendo un'età compresa tra i 13 e i 130 anni (estremi compresi). step Il valore step definisce la distanza che intercorre tra un valore e il successivo. Definisce, in altre parole, la granularità dei valori permessi. Il valore di step deve essere un valore positivo non nullo. Questo attributo si applica sia alle date (come detetime, date, month) sia ai numeri (number e range). Ecco un esempio di utilizzo: <form name="commenti" method="post" action="/141/comments"> [...] <label>Età: <input type="number" name="age" min="13" max="130" step="1"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> In questo caso stiamo chiedendo solo valori interi. novalidate
  • 56.
    Questo attributo siapplica al tag form e permette di saltare tutte le validazioni dei tag che da esso discendono. novalidate è un attributo booleano. Ecco un esempio di utilizzo: <form novalidate> <label>Età: <input type="email" name="myEmail" required> </label> <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form> In questo caso non verrà controllato che il campo email sia in un formato valido né che sia presente. Tabella del supporto sui browser Form: nuovi attributi autocomplete No No No No 10.6+ min, max No No Nightly build Nightly build 9.0+ multiple No 3.6+ 4.0+ 2.0+ 11.0+ pattern No 4.0+ 4.0+ 2.0+ 9.0+ required No 4.0+ 4.0+ 2.0+ 9.0+ step No No 4.0+ 2.0+ 9.0+ Input type: tel È possibile utilizzare l'elemento input con type=tel per creare un campo adatto all'inserimento di numeri di telefono. A differenza degli input di tipo email (http://xhtml.html.it/guide_preview/lezione/4985/input-type- email/) e url (http://xhtml.html.it/guide_preview/lezione/4984/input-type-url/), questo tipo non impone un particolare formato. Ciò è intenzionale. In pratica, i campi di numero di telefono sono campi liberi, perché, a livello intenzionale, i numeri possono essere scritti in diversi modi. È comunque possibile usare l'attributo pattern (http://xhtml.html.it/guide_preview/lezione/4981/nuovi-attributi- dei-form-per-la-validazione/) per forzare un determinato formato. I dispositivi mobili e il type tel I dispositivi mobili possono presentare tastiere personalizzate per facilitare l'inserimento come mostrato nelle immagini che seguono (la prima è relativa a iPhone/iOS, mentre la seconda a un sistema Android). Figura 18 - Tastiera iPhone
  • 57.
    Figura 19 -Tastiera Android Esempi d'uso L'esempio è molto semplice, eccolo: <form> <label>Inserisci il tuo numero di telefono: <input type="tel" name="myTelephone"> </label> <input type="submit" value="Invia" > </form> Questo codice produce visivamente un normale <input type="text">. Tabella del supporto sui browser Form: nuovi tipi di input
  • 58.
    tel No 4.0+ 4.0+ 2.0+ 11.0+ Input type: search È possibile utilizzare l'elemento input con type=search per creare un campo di ricerca. Questo campo è, ovviamente, un campo libero nel senso che non impone nessun pattern. Safari su Mac OS X e il type search Nella maggior parte dei browser non c'è alcuna differenza tra un campo di tipo text e un campo search, ma in Safari su Mac OS X abbiamo un comportamento particolare: 1. Visivamente l'input ha i bordi arrotondati. 2. Se scriviamo qualcosa nel campo compare una piccola X sulla destra che, se cliccata, svuota il campo. Figura 1 Esempi d'uso Ecco un semplice esempio per implementare questo tipo di input: <form name="ricerca" method="post" action="/search"> <label> Parola chiave: <input type="search" autocomplete="on" placeholder="article, section, ..." name="k eyword" required maxlength="50"> </label> <input type="submit" value="Ricerca"> </form> Come detto precedentemente, questo codice nella maggior parte dei browser produce visivamente un normale <input type="text">. Tabella del supporto sui browser Form: nuovi tipi di input search No 4.0+ 2.0+ 2.0+ 11.0+ Input type: url Si usa l'elemento input con type=url per creare un campo destinato all'inserimento di un
  • 59.
    indirizzo web. Il tipourl, se specificato, dovrebbe rappresentare l'inserimento di un URL assoluto, ovvero nel formato http://www.sito.com/etc.... Nel caso in cui il valore inserito non sia valido, viene sollevata, nei browser che supportano il tipo url, un'eccezione che non riconosce il pattern. I dispositivi mobili e il type url I dispositivi mobili possono presentare tastiere personalizzate per facilitare l'inserimento. iPhone modifica la sua tastiera eliminando la barra spaziatrice e mettendo il punto, la slash e l'estensione ".com" come visualizzato nella figura sottostante. Android, invece, visualizza attualmente la tastiera standard. Figura 21 - Tastiera iPhone con un campo di tipo url Esempi d'uso L'esempio è molto semplice, eccolo: <form name="commenti" method="post" action="/141/comments"> [...] <label> Www: <input type="url" name="url" autocomplete="on" placeholder="http://mywebsite.com"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> Produce visivamente un normale <input type="text">. Tabella del supporto sui browser Form: nuovi tipi di input url No 4.0+ 4.0+ 2.0+ 9.0+
  • 60.
    Input type: email L'elementoinput con type=email viene usato per creare un campo per inserire un indirizzo e- mail. L'input con tipo email, se specificato, dovrebbe rappresentare l'inserimento di indirizzi e-mail. Una fondamentale condizione di validità, dunque, sarà rappresentata dalla presenza del simbolo @. Nel caso in cui il valore inserito non sia valido viene sollevata un'eccezione. I dispositivi mobili e il type email I dispositivi mobili possono presentare, anche in questo caso, tastiere ad hoc. iPhone modifica la sua tastiera mostrando la chiocciola e il punto come visualizzato in figura 22. Android attualmente visualizza la tastiera standard. Figura 22 - Tastiera iPhone con un campo di tipo email Esempi d'uso Anche per questo tipo di input presentiamo un piccolo snippet di codice: <form name="commenti" method="post" action="/141/comments"> [...] <label> Email: <input type="email" name="email" autocomplete="on" placeholder="email@domain.ext"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> Il codice visto produce visivamente un normale <input type="text">.
  • 61.
    Tabella del supportosui browser Form: nuovi tipi di input email No 4.0+ 4.0+ 2.0+ 9.0+ Nuovi tipi di input per la gestione delle date Tutti i programmatori prima o poi hanno avuto a che fare con la gestione delle date, con tutti i problemi che ne conseguono. HTML5 mette a disposizione nuovi tipi di input concepiti per facilitare il compito dei programmatori nell'inserimento di date da parte degli utenti. Dal momento che ci sono diversi tipi di date di cui potremmo aver bisogno, ecco che sono stati implementati nella specifica diversi tipologie. In particolare: datetime: gestisce sia la data che l'ora (con fuso orario); datetime-local: gestisce sia la data che l'ora (senza fuso orario); date: gestisce le date; month: gestisce i mesi; week: gestisce le settimane; time: gestisce l'ora. Per tutti questi input abbiamo degli attributi specifici che sono: min: che rappresenta il minimo valore accettato; max: che rappresenta il massimo valore accettato; step: che rappresenta la granulosità dei valori accettati. Vediamoli nel dettaglio. datetime Come detto, serve per permettere l'inserimento di date e ore in un solo colpo. Visivamente nei browser che lo supportano (pochi per ora) abbiamo la generazione di un datepicker in cui abbiamo la possibilità di selezionare un giorno e con l'opzione di mettere anche l'ora, come nell'immagine qui sotto. Ecco cosa avviene quando clicchiamo su quella che sembra essere una select (lo screenshot è di Opera 11): Figura 23 - Input datetime su Opera
  • 62.
    Nei sistemi chenon supportato il tipo datetime, viene generato un normale input di testo. Vediamo un esempio di codice <form> <label>Quando sei disponibile per un incontro? <input type="datetime" name="mydatetime"> </label> <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form> Gli attributi specifici min: il valore di questo attributo deve essere una data e ora con il fuso orario valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-global-date-and-time- string). max: il valore di questo attributo deve essere una data e ora con il fuso orario valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-global-date-and-time- string) e deve essere maggiore del valore dell'attributo min se specificato. step: il valore di questo attributo deve essere un intero e rappresenta i secondi. Il valore di default è di 60 secondi. datetime-local È del tutto simile a datetime, con l'unica differenza che non vengono passate informazioni sul fuso orario. Ecco come appare su Opera 11: Figura 24 - Input datetime-local su Opera
  • 63.
    Nei sistemi chenon supportato il datetime-local viene generato un normale input type="text". Esempio: <form> <label>Quando sei disponibile per un incontro? <input type="datetime-local" name="mydatetime"> </label> <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form> Gli attributi specifici min: il valore di questo attributo deve essere una data e ora valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-local-date-and-time- string). max: il valore di questo attributo deve essere una data e ora valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-local-date-and-time-string) e deve essere maggiore del valore dell'attributo min se specificato. step: il valore di questo attributo deve essere espresso in secondi. Il valore di default è di 60 secondi. date Serve per inserire una data. Nei browser che lo supportano si ottiene un datepicker in cui abbiamo la possibilità di selezionare un giorno: Figura 25 - Input date su Opera
  • 64.
    Nei sistemi chenon supportato il tipo date viene generato un normale campo di testo. Esempio: <form> <label>Quando sei disponibile per un incontro? <input type="date" name="mydatetime"> </label> <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form> Gli attributi specifici min: il valore di questo attributo deve essere una data valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-date-string). max: il valore di questo attributo deve essere una data valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-date-string) e deve essere maggiore del valore dell'attributo min se specificato. step: il valore di questo attributo deve essere espresso in giorni. Il valore di default è di 1 giorno. month Serve per permettere di selezionare un mese dell'anno. In figura 4 il datepicker per selezionare un mese generato su Opera 11: Figura 26 - Input month su Opera Nei sistemi che non supportato month viene generato un normale campo di testo. Esempio <form> <label>Quando sei disponibile per un incontro? <input type="month" name="mydatetime"> </label> <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form>
  • 65.
    Gli attributi specifici min: il valore di questo attributo deve essere un mese valido (http://developers.whatwg.org/common-microsyntaxes.html#valid-month-string). max: il valore di questo attributo deve essere una mese valido (http://developers.whatwg.org/common-microsyntaxes.html#valid-month-string) e deve essere maggiore del valore dell'attributo min se specificato. step: il valore di questo attributo deve essere espresso in mesi. Il valore di default è di 1 mese. week Viene usato per la selezione di una determinata settimana dell'anno (composta da anno - numero di settimana). In figura 5 il widget prodotto dall'inserimento di questo tipo di input su Opera 11: Figura 27 - Input week su Opera Nei sistemi che non supportato week viene creato un normale campo di testo. Esempio: <form> <label>Quando sei disponibile per un incontro? <input type="week" name="mydatetime"> </label> <input type="reset" value="Resetta il form"> <input type="submit" value="Invia"> </form> Gli attributi specifici min: il valore di questo attributo deve essere una settimana valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-week-string). max: il valore di questo attributo deve essere una settimana valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-week-string) e deve essere maggiore del valore dell'attributo minse specificato. step: il valore di questo attributo deve essere espressa in settimane. Il valore di default è di 1 settimana. time Serve per selezionare e inserire una determinata ora del giorno. Ancora una schermata da Opera 11:
  • 66.
    Figura 28 -Input time su Opera Nei sistemi che non supportato il time genera un normale input type="text". Vediamo un esempio di codice <form> <label>Quando sei disponibile per un incontro? <input type="time" name="mydatetime"> </label> <input type="reset" value="Resetta la form"> <input type="submit" value="Invia"> </form> Gli attributi specifici min: il valore di questo attributo deve essere una ora valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-time-string). max: il valore di questo attributo deve essere una ora valida (http://developers.whatwg.org/common-microsyntaxes.html#valid-time-string) e deve essere maggiore del valore dell'attributo min se specificato. step: il valore di questo attributo deve essere espresso in secondi. Il valore di default è di 60 secondi. Tabella del supporto sui browser Form: nuovi tipi di input datetime No No Parziale Parziale 9.0+ date No No Parziale Parziale 9.0+ month No No Parziale Parziale 9.0+ week No No Parziale Parziale 9.0+ time No No Parziale Parziale 9.0+ datetime-local No No Parziale Parziale 9.0+ Input type: number È possibile utilizzare l'elemento input con type=number per creare un campo destinato all'inserimento di un numero. I dispositivi mobili e il type number I dispositivi mobili possono presentare tastiere personalizzate per facilitare l'inserimento come mostrato nelle immagini che seguono (fanno riferimento, rispettivamente, a iPhone/iOS e Android).
  • 67.
    Figura 29 -Tastiera iPhone con un input di tipo number Figura 30 - Tastiera Android con un input di tipo number Attributi specifici per il type number HTML5 mette a disposizione un set di attributi specifici per i campi di tipo number. Servono a specificare delle limitazioni per il valore di questo attributo. Questi attributi sono min, max e step. Attributo min Specifica il minimo valore permesso. La sintassi è semplice: min="1" permette solo l'inserimento di numeri positivi. I valori permessi sono, ovviamente, numeri. Attributo max Specifica il massimo valore permesso. max="10" permette solo l'inserimento di numeri inferiori o uguali a 10. Il valore di questo attributo deve essere maggiore del valore dell'attributo min (se specificato). Attributo step
  • 68.
    L'attributo step indicala granulosità che deve avere il valore, limitando i valori permessi. Il valore di step se specificato deve essere un numero (anche non intero) maggiore di zero oppure la stringa "any" (che equivale a non inserire l'attributo). La sintassi è anche in questo caso molto semplice: step=3 influenza i valori permettendo valori come -3, 0, 3, 6 ma non -1 o 2. Esempi d'uso Un esempio potrebbe avere questa forma: <form name="commenti" method="post" action="/141/comments"> [...] <label>Età: <input type="number" name="age" min="13" max="130" step="1"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> In questo caso stiamo chiedendo di inserire un'età compresa tra i 13 e i 130 anni (estremi compresi) e i valori accettati sono interi. Nella maggior parte dei browser si produce attualmente un normale <input type="text">, ma nei browser che supportano number abbiamo: Figura 31 - Un input di tipo number Tabella del supporto sui browser Form: nuovi tipi di input number No No 4.0+ 2.0+ 9.0+ Input type: range Molto simile semanticamente all'input type=number (http://xhtml.html.it/guide_preview/lezione/4987/input-type-number/), questo nuovo tipo di input permette agli utenti di inserire un numero tramite uno slider. Attributi specifici HTML5 mette a disposizione un set di attributi specifici per il tipo range (che sono gli stessi del type=number): servono a specificare delle limitazioni per il valore di questo attributo. Questi attributi
  • 69.
    sono min,max estep. min: specifica il minimo valore permesso. Esempio: min="1", che permette solo di passare numeri da 1 in su. max : specifica il massimo valore permesso. Esempio: max="10", che permette solo di inviare numeri inferiori o uguali a 10. Il valore di questo attributo deve essere maggiore del valore dell'attributo min (se specificato). step: indica la granulosità che deve avere il value limitando i possibili valori passati. Il valore di step se specificato deve essere un numero (anche non intero) maggiore di zero oppure la stringa "any" (che equivale a non inserire l'attributo). Esempio: step=3, che influenza i valori inseriti passando valori come -3, 0, 3, 6. Esempi d'uso L'esempio è molto semplice, eccolo: <form name="commenti" method="post" action="/141/comments"> [...] <label>Voto: <input type="range" name="voto" min="0" max="5" step="1"> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> Ecco come appare un input di tipo range sui browser che lo supportano: Figura 32 - Un input di tipo range Tabella del supporto sui browser Form: nuovi tipi di input range No No 4.0+ 2.0+ 9.0+ Input type: color L'elemento input con type=color dovrebbe creare un color picker, quel tipo particolare di widget utile per la selezione di un colore a partire da una palette di colori. Una volta selezionato il colore, il campo passa alla nostra pagina di ricezione un colore RGB esadecimale composto da 6 cifre. Questo tipo input ad oggi non è molto supportato. Per la sua implementazione sui nostri siti si può però ricorrere ad una qualsiasi delle tante soluzioni Javascript disponibili.
  • 70.
    Opera e iltype color Nella maggior parte dei browser non c'è alcuna differenza tra un campo di tipo text e un campo color, ma su Opera abbiamo un comportamento particolare: 1. Visivamente abbiamo una select particolare con i colori (esempio in figura 33). 2. Se clicchiamo abbiamo una scelta dei colori base (esempio in figura 34). 3. Se clicchiamo su "Altro" richiamiamo il color picker di sistema (esempio su Opera su Mac OS X in figura 35). Figura 33 - Input color su Opera: stato iniziale Figura 34 - Input color su Opera: selezione del colore Figura 35 - Input color su Opera: color-picker di sistema Esempi d'uso Un input di tipo color si crea così: <form> <label>Seleziona il colore di sfondo:
  • 71.
    <input type="color" name="mybackground"> </label> <input type="reset" value="Resetta la form"> <input type="submit" value="Invia"> </form> Tabella del supporto sui browser Form: nuovi tipi di input color No No Parziale Parziale 11.0+ Cosa sono le datalist? L'elemento HTML5 <datalist> è utilizzato per fornire una funzione di "completamento automatico" ad un elemento del form. Permette, in altre parole, di fornire all'utente un elenco di opzioni predefinite da cui scegliere, offrendo al contempo la possibilità di inserire un valore non presente inizialmente nella lista. Mette insieme i vantaggi di una select e di un input di testo. Sfortunatamente questo tag molto utile è al momento poco supportato dai browser (solo Opera lo supporta). Datalist su Opera Nella maggior parte dei browser il codice relativo all'elemento datalist viene completamente ignorato ma su Opera abbiamo il seguente comportamento: 1. Visivamente abbiamo solo il nostro input e la datalist non è renderizzata in nessun modo (figura 36). 2. Se clicchiamo sull'input a cui la datalist è collegata, vengono presentate le opzioni della datalist (figura 37). 3. Se clicchiamo su un'opzione, l'input a cui la datalist è collegato assume il valore dell'opzione (figura 38). Figura 36 - Datalist su Opera: stato iniziale Figura 37 - Datalist su Opera: opzioni della datalist Figura 38 - Datalist su Opera: valorizzazione dell'opzione
  • 72.
    Esempi d'uso Per utilizzaredatalist prima di tutto dobbiamo scrivere un input a cui collegare la nostra datalist. Per collegare l'input alla datalist basta impostare l'attributo list dell'input con l'id della datalist. Per ogni suggerimento che vogliamo dare all'utente facciamo discendere da datalist un tag option, mettendo il suggerimento nell'attributo value. Il value dell'option non deve essere vuoto e l'option non deve essere settata su disabled per essere visualizzata. Vediamo il codice: <form name="commenti" method="post" action="/141/comments"> [...] <label>Stato d'animo: <input type="text" name="mood" placeholder="felice, triste, incuriosito, ..." list ="stato-danimo"> <datalist id="stato-danimo"> <option value="triste"> <option value="annoiato"> <option value="curioso"> <option value="felice"> <option value="entusiasta!"> </datalist> </label> [...] <input type="reset" value="Resetta il form"> <input type="submit" value="Invia il commento"> </form> Tabella del supporto sui browser Form: nuovi tipi di input datalist No 4.0+ No No 9.0+ La potenza dei microdati Introduzione: semantica e rich snippet Leggendo questa guida dovrebbe essere chiaro che un punto focale di HTML5 è la semantica. HTML5 ha introdotto infatti diversi tag semantici (come header, article o nav) che permettono di strutturare il contenuto secondo una logica, appunto, semantica. Ma questa suddivisione non assolve a tutte le necessità semantiche di cui il web ha bisogno. L'obbiettivo è quello di dare la possibilità a programmi come crawler dei motori di ricerca o screen reader di comprendere il significato del testo. Queste informazioni sono accessibili da questi
  • 73.
    programmi e rimangono(attualmente) invisibili per l'utente. Entrano qui in gioco i cosiddetti microdati. Ecco come Google li descrive, all'interno della Guida di Strumenti per i Webmaster: "La specifica dei microdati HTML5 è un modo per assegnare etichette ai contenuti al fine di descrivere un tipo specifico di informazioni (ad esempio recensioni, informazioni su persone o eventi). Ogni tipo di informazione descrive uno specifico tipo di elemento, come una persona, un evento o una recensione. Ad esempio, un evento ha proprietà quali il luogo, l'ora di inizio, il nome e la categoria." Attualmente, la cosa forse più interessante relativa ai microdati è in effetti uscita fuori dai laboratori di Google: si tratta dei cosiddetti rich snippet. I rich snippet sono risultati della ricerca di Google in cui, oltre alle comuni informazioni, compaiono altri dati allegati, come nelle immagini qui sotto: Figura 39 - Rich snippet relativo a un hotel Figura 40 - Rich snippet con informazioni personali I microdata in pratica Applicare i microdati è semplice: per ogni tag HTML possiamo specificare degli attributi che ci permettono di definire gli oggetti semantici. Prima di tutto dobbiamo applicare a un elemento radice (cioè un elemento che contiene tutte le informazioni che vogliamo specificare) itemscope e itemtype. itemscope definisce l'elemento a cui è applicato è un contenitore dell'oggetto che andremo a descrivere. itemtype definisce il vocabolario che specifica il tipo di oggetto che andremo a descrivere. Per finire, sugli elementi che discendono dall'elemento radice specifichiamo l'attributo itemprop che definisce la proprietà che verrà valorizzata con il testo contenuto nel tag. Ecco un esempio semplice: <div itemscope itemtype="http://data-vocabulary.org/Person">My name is <span itemprop="name">Simone Bonati</span> and my nickname is <span itemprop="nickname">svarione</span> on several site (like twitter). Here is my twitter account: <a href="http://www.example.com" itemprop="url">http://twitter.com/svarione</a> I li
  • 74.
    ve in Milano, Italy and work as <span itemprop="role">web developer</span>. </div> Possiamo anche nidificare gli oggetti. Ecco un esempio: <div itemscope itemtype="http://data-vocabulary.org/Person">My name is <span itemprop="name">Simone Bonati</span> and my nickname is <span itemprop="nickname">svarione</span> on several site (like twitter). Here is my twitter account: <a href="http://www.example.com" itemprop="url">http://twitter.com/svarione</a> I li ve in <span itemprop="address" itemscope itemtype="http://data-vocabulary.org/Address"> <span itemprop="locality">Milano</span>, <span itemprop="country-name">Italy</span> </span> and work as <span itemprop="role">web developer</span>. </div> I vocabolari Per sfruttare al massimo la potenza dei microdati e per ottenere i rich snippet dobbiamo usare i vocabolari (che specifichiamo, come abbiamo visto, con itemtype) supportati da Google. I vocabolari descrivono l'insieme di proprietà che possono essere definite per un determinato oggetto. I vocabolari più popolari supportati da Google sono: Breadcrumbs: questo vocabolario serve a rappresentare un insieme di link che può aiutare un utente a comprendere e navigare all'interno del sito. Businesses and organizations: questo vocabolario definisce una società, un negozio e più in generale un luogo. People: questo vocabolario definisce una persona. Address: questo vocabolario definisce un indirizzo. Events: questo vocabolario definisce un evento (con informazioni come il titolo, la data e il luogo). Product: questo vocabolario definisce un prodotto. Offer: questo vocabolario definisce un'offerta. Offer-aggregate: questo vocabolario definisce un aggregato di offerte (con prezzo minimo, prezzo massimo, etc). Recipes: questo vocabolario definisce una ricetta. Review: questo vocabolario definisce una singola recensione. Review-aggregate: questo vocabolario definisce una recensione aggregata. Rating: questo vocabolario definisce una valutazione. Link utili Google: informazioni sui microdati (http://www.google.com/support/webmasters/bin/answer.py? hlrm=en&answer=176035) Vocabolario Breadcumbs (http://data-vocabulary.org/Breadcrumb)
  • 75.
    Vocabolario Businesses andOrganizations (http://data-vocabulary.org/Organization) Vocabolario People (http://data-vocabulary.org/People) Vocabolario Address (http://data-vocabulary.org/Address) Vocabolario Events (http://data-vocabulary.org/Event) Vocabolario Products (http://data-vocabulary.org/Product) Vocabolario Offer (http://data-vocabulary.org/Offer) Vocabolario Aggregate (http://data-vocabulary.org/Offer-aggregate) Vocabolario Recipes (http://data-vocabulary.org/Recipes) Vocabolario Reviews (http://data-vocabulary.org/Review) Vocabolario Review-aggregate (http://data-vocabulary.org/Review-aggregate) Vocabolario Rating (http://data-vocabulary.org/Rating) Nuova linfa alle applicazioni web L’acronimo alla base di HTML è chiaro, ‘HyperText Markup Language’; ipertesto: un insieme di documenti messi in relazione tra loro da parole chiave. La tecnologia in questione dovrebbe essere quindi utile per strutturare collezioni di contenuti legati fra di loro in modo più o meno oggettivo; Wikipedia ne è il classico e più calzante esempio. Lascia invece un po’ più perplessi realizzare che l’HTML è sempre più la base di vere e proprie applicazioni web: google docs, Google Maps, Meebo, solo per citarne alcune, dove la forma ipertestuale è decisamente marginale quando non completamente assente. Come già abbiamo avuto modo di anticipare, l’HTML5 nasce anche per meglio indirizzare questo crescente filone di realtà web, mettendo a disposizione dello sviluppatore una nutrita serie di nuovissime API studiate per dare adito alle più ardite applicazioni all’interno di un browser. È un po’ come se il prematuro sogno di Marc Andressen (http://www.forbes.com/forbes/1997/1201/6012308a_print.html) stesse per avverarsi. La risposta ad un bisogno molto sentito Lo sforzo profuso prima dal gruppo WHAT e poi dal W3C è stato parzialmente accentuato dalla necessità di porre un freno al dilagare di tecnologie, parallele all’HTML, sorte per rispondere ai nuovi bisogni delle applicazioni web. Il motivo principale di questo comportamento è da ricercarsi nella necessità di mantenere unito il controllo sullo standard e, conseguentemente, di evitare che l’insediamento in pianta stabile di soluzioni non interne al consorzio possa complicare di fatto la gestione dell’intero sistema. Flash ed il cugino Flex sono due ottimi esempi della tendenza intrapresa dal web in tal senso, seguono Google Gears, Google O3D e un’infinita di altre estensioni, ognuna delle quali cerca di riempire un vuoto più o meno piccolo percepito dagli utenti della rete. I punti chiave dell’offerta
  • 76.
    Ecco un elencoesaustivo delle API trattate nelle prossime lezioni della guida. E` possibile suddividere tale insieme sommariamente in due categorie: nella prima, multimedialità, trovano spazio le API studiate per gestire ed incanalare nuovi strumenti di comunicazione, come video, audio e grafica bi/tridimensionale; nella seconda, che invece potremmo chiamare arricchimento, si posizionano le nuove funzionalità studiate per avvicinare l’esperienza di fruizione di una web application a quella di un normale programma eseguito in una finestra del desktop. Multimedialità Gestione di flussi video (il tag <video> e le relative API); Gestione di flussi audio (il tag <audio> e le relative API); Gestione di grafica libera bi/tridimensionale (il tag <canvas> e le relative API); Grafica vettoriale e notazioni matematiche (i tag <svg>, <math> e le relative API). Arricchimento Applicazioni web offline (file .manifest e API di sincronizzazione); Memorizzazione di informazioni sul browser (WebSQL e LocalStorage API); Javascript asincrono e parallelo (Web Workers API); Comunicazioni bidirezionali tra client e server (Web Socket API); Drag and Drop; Utilizzo di informazioni georeferenziate (GeoLocation API). Oltre a questo elenco, meritano di essere citate e brevemente esplorate alcune funzionalità che, seppur non rivoluzionarie, promettono di semplificare alcuni aspetti dello sviluppo odierno di applicazioni web. Manipolare la cronologia: le History API Tutti conosciamo il meccanismo per navigare all’interno della cronologia del browser: alert("La cronologia contiene " + history.length + " documenti." ); // torna al documento precedente history.back(); Meno invece sanno che di questo oggetto history, seppur supportato dalla totalità degli user-agent, non esiste traccia nelle attuali specifiche HTML. Con l’avvento della versione 5 il W3C ha deciso diincludere le API per la navigazione della cronologia all’interno del futuro standard; l’occasione si è rivelata ghiotta anche per un interessante arricchimento delle funzionalità. In particolare è ora possibile creare nuovi elementi della cronologia associando loro un payoff di dati, come ad esempio un hash chiave-valore, che poi potrà essere recuperato attraverso l’utilizzo del tasti di navigazione del browser. Vediamone un esempio: <!doctype html> <html> <head> <title>Messaggi dalla storia:</title> <script> historyInject = function(){ message = prompt('Che messaggio vuoi salvare in history?'); history.pushState(message, document.title + " '" + message + "'"); };
  • 77.
    onpopstate = function(event){ document.getElementById("messagelist"). insertAdjacentHTML('beforeend',"<li>"+event.state+"</li>"); } </script> </head> <body> <h1>Lista dei messaggi presenti nella cronologia:</h1> Premi il tasto back del browser o <a href="javascript:historyInject();return false ;"> carica un nuovo messaggio.</a> <ul id="messagelist"></ul> </body> </html> La funzione historyInject crea, attraverso il metodo pushState, un nuovo elemento nella cronologia che contiene il messaggio generato dall’utente. Successivamente, la pressione del tasto back del browser viene intercettata dall’handler onpopstate che recupera il messaggio e lo aggiunge all’elenco a fondo pagina. Con questo meccanismo diviene possibile simulare una normale navigazione con i pulsanti back e forward anche all’interno di applicazioni web che fanno uso intensivo di Javascript, come ad esempio la maggior parte delle realizzazioni sviluppate con il framework Ext.JS (http://dev.sencha.com/deploy/dev/examples/). Associare protocolli e MIME type alla propria web application Supponiamo di aver sviluppato una web application per inviare e ricevere sms. Le specifiche HTML5 introducono la possibilità di registrare la propria applicazione come gestore preposto ad uno specifico protocollo; nel nostro caso sarebbe quindi auspicabile che qualunque link nel formato: <a href="sms://+39396819577">Manda un SMS a Sandro Paganotti</a> convogli l’utente alla nostra applicazione a prescindere dal sito in cui si trova il link, un po’ come succede con il client di posta al click di link col protocollo ‘mailto’. Registrare un protocollo è molto semplice: <!doctype html> <html> <head> <title>TuttiSMS: Il servizio per inviare e ricevere SMS</title> <script> registraProtocollo = function(){ navigator.registerProtocolHandler( 'sms', 'http://localhost/invia_sms?dest=%s', 'TuttiSMS'); }; </script> </head> <body onload="registraProtocollo();"> </body> </html>
  • 78.
    Caricando la paginain un browser che supporta questa funzionalità, noi abbiamo usato Firefox 3.5, verremo notificati della volontà da parte dell’applicazione web di registrare il protocollo sms e potremo decidere se consentire o meno l’operazione: Figura 41 (click per ingrandire) - Associazione del protocollo su Firefox (http://www.html.it/guide/esempi/html5/imgs/lezione_api/1.jpg) Una volta accettata questa associazione, ad ogni link nel formato: ‘sms://qualsiasi_cosa’ seguirà un redirect automatico all’indirizzo specificato come parametro nella funzione registerProtocolHandler, nel nostro caso: ‘http://localhost/invia_sms?dest=%s’, con la particella ‘%s’ sostituita con l’intero link in questione. Ecco un esempio di risultato: http://localhost/invia_sms?dest=sms%3A%2F%2F%2B39396819577 E’ possibile seguire esattamente la stessa procedura anche per registrare uno specifico MIME type utilizzando la funzione gemella: registerContentHandler. Un progetto ambizioso Chiudiamo la lezione gettando le basi per lo sviluppo del progetto guida associato al prossimo gruppo di lezioni: FiveBoard, una lavagna interattiva basata su canvas. Chiaramente l’obiettivo del progetto è quello di fornire un pretesto per poter esplorare le novità introdotte dall’HTML5 in un contesto un po’ più ampio rispetto a quello solitamente offerto. Proprio per questo alcune scelte potranno essere opinabili in un ottica di efficenza o di strutturazione dell’architettura. Bene, cominciamo creando una cartella di progetto, ‘fiveboard’, contenente un file ‘index.html’ e una cartella ‘js’ con un ulteriore file ‘application.js’: Figura 42 - Vista delle cartelle
  • 79.
    Ora impostiamo lapagina html perché richiami il file Javascript: <!doctype html> <html lang='it'> <head> <meta charset="utf-8"> <title>FiveBoard: uno spazio per gli appunti.</title> <script src="js/application.js" defer></script> </head> </html> L’attributo defer, poco supportato perché definito in modo poco chiaro nelle specifiche HTML4 (http://www.w3.org/TR/html401/interact/scripts.html#h-18.2.1), assume nella versione 5 un significato più delineato: “(se) [..] l'attributo defer è presente, allora lo script viene eseguito quando la pagina ha finito di essere processata.” In questo nuovo contesto è interessante l’utilizzo di defer per velocizzare il caricamento della pagina posticipando in seconda battuta il load del file Javascript. Da notare, prima di proseguire, anche l’aggiunta nelle specifiche dell’attributo async che causa il caricamento dello script in parallelo, o asincrono, da qui il nome, rispetto a quello della pagina. Prima di concludere sinceriamoci del funzionamento del nostro impianto aggiungendo al file Javascript: alert("pronti per cominciare!"); e caricando il tutto all’interno di un browser: Figura 43 (click per ingrandire) - Risultato dell'operazione
  • 80.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_api/3.jpg) Codice degli esempi Primadi proseguire, potete scaricare per una migliore consultazione il pacchetto zip (http://www.html.it/guide/esempi/html5/esempi/esempi_html5_api.zip) che contiene il codice di tutti gli esempi che vedremo nelle lezioni che seguono. Applicazioni web offline (file .manifest) Con le Offline API è possibile specificare in un apposito file, detto manifest, un elenco di asset (pagine web, filmati Flash, immagini, fogli di stile CSS, Javascript e quant’altro può finire in una pagina web) dei quali vogliamo che il browser conservi copia locale. Tali oggetti, dopo la prima sessione di navigazione online, resteranno quindi accessibili anche in assenza di una connessione di rete. In questo modo è possibile creare applicazioni web perfettamente funzionanti anche offline. Il primo passo per ottenere questo risultato è aggiungere l’attributo manifest all’elemento html segnalando così allo user agent l’esistenza di un file preposto alla memorizzazione offline: <!doctype html> <html lang='it' manifest='fiveboard.manifest'> ... A questo punto deve essere creato un file con MIME type ‘text/cache-manifest’, dal nome specificato nel valore dell’attributo, contenente l’elenco dei documenti per i quali si richiede la memorizzazione offline. Ad esempio, considerando il nostro progetto guida, il file potrebbe risultare: CACHE MANIFEST index.html js/application.js
  • 81.
    Per fare inmodo che il file venga servito con il corretto MIME type ci sono molte soluzioni, tutte, purtroppo, molto legate al tipo di webserver che sostiene la pagina. Nel caso si tratti del popolare Apache la soluzione più veloce consiste nel creare un file ‘.htaccess’ nella cartella principale del progetto contenente la seguente istruzione: AddType text/cache-manifest manifest Una volta soddisfatte queste condizioni è possibile verificare il buon funzionamento delle API utilizzando un browser come Chromium e caricando la pagina assicurandosi di avere i Developer Tools attivati (CTRL + SHIFT + J) e posizionati sulla sezione ‘Console’ (figura 1): Figura 1 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_offline/1.jpg) Aggiornando nuovamente il contenuto della pagina riceveremo conferma della corretta memorizzazione in locale dei documenti specificati nel file manifest, ora disponibili anche per consultazioni in assenza di rete (figura 2): Figura 2 (click per ingrandire)
  • 82.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_offline/2.jpg) Andare in profondità Ilfile .manifest di cui abbiamo accennato nella sezione precedente, offre una serie di interessanti funzionalità aggiuntive; in primo luogo è possibile indicare al browser percorsi di fallback, da utilizzare nel caso in cui durante la navigazione offline si necessiti di risorse non disponibili: CACHE MANIFEST index.html FALLBACK: news.html offline.html In questo esempio, il tentativo di navigazione verso la pagina ‘news.html’ verrà risolto, in caso di browser in stato offline, servendo la pagina ‘offline.html’. In caso fosse necessario convogliare risorse multiple si può anche far ricorso a wildcard, ad esempio: CACHE MANIFEST index.html FALLBACK: news.html offline.html news/* offline.html Così facendo anche la navigazione verso una pagina di dettaglio news, come ad esempio ‘news/ 10/12/2010/nuovi-progressi-al-cern’ verrà servita in assenza di rete con il contenuto di ‘offline.html’. Usando la sezione ‘NETWORK’ è invece possibile notificare risorse che devono sempre essere recuperate online: CACHE MANIFEST index.html NETWORK: saldo_conto_corrente.php
  • 83.
    Le API associate Lagestione della cache introduce tutta una serie di eventi e proprietà che consentono di acquistare controllo su ogni passo della procedura, vediamoli in questo esempio (offline.html), che stampa a video un nuovo elemento di un elenco puntato per ogni evento intercettato: <!doctype html> <html lang='it' manifest='offline.manifest'> <head> <title>Tutti i passi della cache</title> <script> // Funzione di servizio: say = function(message){ document.getElementById("cache-steps"). insertAdjacentHTML('beforeend',"<li>"+message+";</li>"); } clear = function(message){ document.getElementById("cache-steps").innerHTML = ""; } // L'oggetto che controlla la cache var appCache = window.applicationCache; // Nuovi eventi disponbili con le Offline API appCache.addEventListener('checking', function(ev) { say("Controllo, se posso, che il .manifest online non sia cambiato"); }, false); appCache.addEventListener('noupdate', function(ev) { say("Il .manifest non è cambiato"); }, false); appCache.addEventListener('downloading', function(ev) { say("Inizio a scaricare i file listati dal manifest che non ho già in cache"); }, false); appCache.addEventListener('progress', function(ev) { say("Scarico una risorsa"); }, false); appCache.addEventListener('cached', function(ev) { say("Tutte le risorse sono state scaricate"); }, false); appCache.addEventListener('updateready', function(ev) { say("Ho scaricato una nuova versione della cache e sono pronto "+ "a sostituirla alla precedente"); }, false); appCache.addEventListener('obsolete', function(ev) {
  • 84.
    say("Ho cercato il.manifest online ottenendo un 404 o 410 come risposta " + "probabilmente il sito non supporta più funzionalità di " + "caching, cancello la cache in locale."); }, false); appCache.addEventListener('error', function(ev) { say("Ops, si è verificato un errore"); }, false); </script> </head> <body> <button onclick="clear();appCache.update();">Forza il controllo della cache</button> <ul id="cache-steps"> </ul> </body> </html> Proviamo ad eseguire questa pagina in Chromium senza aver preventivamente creato il file ‘offline.manifest’ (figura 3): Figura 3 Creiamo ora un semplice ‘offline.manifest’ come segue, CACHE MANIFEST offline.html e ricarichiamo la pagina (figura 4): Figura 4
  • 85.
    Ricarichiamo nuovamente lapagina per certificare l’avvenuto cache delle risorse (figura 5): Figura 5 Bene, da qui si dipanano tutta una serie di comportamenti interessanti come ad esempio: modificare il file .manifest e forzare il controllo della cache, oppure rimuovere il file .manifest e ricaricare la pagina. Provando ognuno di essi con lo script che abbiamo appena steso sarà facile identificare i vari eventi coinvolti. Ecco la demo (http://www.html.it/guide/esempi/html5/esempi/lezione_offline/offline.html). Conclusioni Come avete potuto notare le attività per far in modo che il nostro progetto guida benefici delle Offline API sono state semplici e di pochissimo impatto sull’architettura della pagina. E` importante però ricordare che man mano andremo ad aggiungere risorse al progetto (immagini, librerie, etc...) sarà necessario includerle nel file .manifest pena il malfunzionamento dello stesso in assenza di connessione. Tabella del supporto sui browser
  • 86.
    API e WebApplications Applicazioni web offline (.manifest) No 3.5+ 4.0+ 2.0+ 10.6+ Indexed Database API Queste API nascono per dare la possibilità di creare e manipolare un database di ispirazione NoSQL memorizzato all'interno del browser dell'utente. Ogni database, identificato da un nome, può contenere un numero arbitrario di Object Store, letteralmente contenitori di oggetti: ad esempio un ipotetico database ‘libreria' potrebbe ospitare i seguenti Object Store: Libri, Clienti. Sia ‘Libri' che ‘Clienti' non sono altro che strutture paragonabili ad array associativi ordinati, dove ogni coppia chiave-valore rappresenta un oggetto, quindi in questo caso o un libro o un cliente. All'interno di un Object Store è possibile eseguire le normali operazioni di inserimento, modifica, eliminazione e ricerca; le API sono state pensate per ragionare con record in un formato JSON-like ma nulla vieta di memorizzare oggetti Javascript di fattura diversa, purché serializzabili. Come funzionano le API Utilizziamo Chromium per un tour operativo di questo interessante set di API; iniziamo preparando un documento HTML5 e creando il database: <!doctype html> <html> <head> <title> WebLibreria: gestionale per librerie </title> <script> setup = function(){ if ('webkitIndexedDB' in window){ indexedDB = webkitIndexedDB; IDBCursor = webkitIDBCursor; IDBKeyRange = webkitIDBKeyRange; IDBTransaction = webkitIDBTransaction; }else if ('moz_indexedDB' in window){ indexedDB = moz_indexedDB; } } init = function(){ setup(); var request = indexedDB.open("WebLibreria", "Il gestionale per librerie"); } </script> </head> <body onload="init();"> </body> </html>
  • 87.
    Essendo, ad oggi,queste features ancora sperimentali Chromium (e Firefox) prefissano le classi di riferimento con la particella ‘webkit': la funzione ‘setup' serve solamente per creare degli alias che abbiano nomi più aderenti alle specifiche. Mano a mano che queste API si avvicineranno allo status di standard assisteremo alla rimozione di questi prefissi e potremo commentare la funzione setup. La creazione del database avviene attraverso l'istruzione ‘indexedDB.open', che accetta come parametro il nome e la descrizione dell'oggetto che stiamo creando; in caso un database con lo stesso nome e lo stesso dominio di origine sia già presente nel browser allora verrà utilizzato quest'ultimo. A questo punto possiamo intercettare gli eventi di avvenuta creazione della connessione e di errore durante la procedura gestendoli nel modo che meglio ci aggrada: init = function(){ setup(); var request = indexedDB.open("WebLibreria", "Il gestionale per librerie"); request.onsuccess = function(){console.log("Connessione Ok!");} request.onerror = function(){console.log("Ops, errore");} } Eseguiamo la pagina all'interno di Chromium prestando attenzione alla linguetta 'console' all'interno dei Developer Tools (CTRL + SHIFT + J) e avremo conferma dell'avvenuta connessione/creazione al database (figura 1): Figura 1 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_database/1.jpg) Ora che abbiamo stabilito una connessione è essenziale determinare se l'utente sia alla sua prima visita, perché in questo caso è necessario procedere con la creazione degli object store e della struttura necessari. Per gestire questo meccanismo le API mettono a disposizione il concetto di versione, impostata a null per un database appena creato, che può essere utilizzata proprio capire quando sia necessario apportare modifiche alla struttura. Ad esempio: ... init = function(){ setup(); var request = indexedDB.open("WebLibreria", "Il gestionale per librerie"); request.onsuccess = controllaVersione; request.onerror = function(){console.log("Ops, errore");} } controllaVersione = function(event){ window.database = event.result;
  • 88.
    if(database.version != "1.0"){ var request = database.setVersion("1.0"); request.onsuccess = aggiornaLoSchema; request.onerror = function(){console.log("Ops, errore");} }else{ // il database è già aggiornato alla versione più recente } } aggiornaLoSchema = function(){ console.log("Qui posso aggiornare lo schema"); } ... La funzione aggiornaLoSchema viene invocata solamente nel caso in cui la versione del database sul browser dell'utente sia diversa da quella attesa dal javascript (in questo caso la ‘1.0'); in particolare registrare una funzione sul callback ‘onsuccess' del metodo setVersion è l'unico modo in cui sia consentito dalle specifiche modificare la struttura del database. Se ora provate questo codice all'interno del browser, nella linguetta console vedrete comparire la scritta ‘Qui posso aggiornare lo schema', se poi ricaricate nuovamente la pagina invece noterete che tale messaggio scompare: il database è infatti già nella versione “1.0”, quindi il flusso del programma transita attraverso il blocco else, che è al momento vuoto. Per cancellare il database creato e poter così ripetere la procedura è necessario svuotare completamente la cache, chiudere Chromium e riaprirlo (figura 2): Figura 2 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_database/2.jpg) A questo punto creiamo la struttura del database: per prima cosa dobbiamo aprire una transazione, successivamente istruiamo il database alla creazione dell'object store ‘libri', con chiave sul campo ‘isbn', e di due indici, rispettivamente per i campi ‘titolo' e ‘autore'. Approfittiamone anche per creare la funzione recuperaLibri, che verrà invocata sia al completamento della struttura sia nel caso il database sia già alla versione desiderata: ... controllaVersione = function(event){ window.database = event.result; if(database.version != "1.0"){ var request = database.setVersion("1.0"); request.onsuccess = aggiornaLoSchema; request.onerror = function(){console.log("Ops, errore");}
  • 89.
    }else{ recuperaLibri(); } } aggiornaLoSchema = function(){ window.transazione = event.result; transazione.oncomplete = recuperaLibri; transazione.onabort = function(){console.log("Ops, errore");} var libri = database.createObjectStore("libri", "isbn", false); var titolo = libri.createIndex("titolo", "titolo", false); var autore = libri.createIndex("autore", "autore", false); } recuperaLibri = function(){ // recupera l'elenco dei libri e stampali a video } ... La funzione createObjectStore richiede 3 parametri, di cui solamente il primo, il nome, obbligatorio; il secondo parametro rappresenta il nome della proprietà del record che vogliamo funga da chiave all'interno dell'object store. Il terzo parametro imposta invece la proprietà autoincrement; nel caso non sia presente una chiave nel record in inserimento e autoincrement = true, una chiave verrà generata in automatico. Le due chiamate alla funzione createIndex provvedono alla creazione di due indici, utili per ricerche e query, sui campi ‘titolo' e ‘autore'; il terzo parametro impostato a falseconsente la presenza di valori duplicati. Ora che abbiamo creato la struttura del database stampiamo a video l'elenco, per ora vuoto, dei libri registrati e creiamo un piccola form per l'inserimento di un nuovo volume: ... recuperaLibri = function(){ window.transazione = database.transaction(["libri"], IDBTransaction.READ_WRITE, 0); var request = transazione.objectStore("libri").openCursor(); request.onsuccess = stampaLibri; request.onerror = function(){console.log("Ops, errore");} } stampaLibri = function(){ var cursor = event.result; if( cursor != null){ document.getElementById("catalogo_libri").insertAdjacentHTML('beforeend', "<li>" + cursor.value.autore + ": " + cursor.value.titolo + " (ISBN: "+ cursor.value.isbn +"); </li>"); cursor.continue(); } } aggiungiLibro = function(){ // aggiungiamo un nuovo libro all'object store } </script> </head> <body onload="init();"> <h1> WebLibreria: gestionale per librerie</h1>
  • 90.
    <section> <h1>Elencodei libri</h1> <ul id="catalogo_libri"> </ul> </section> <aside> <h1>Aggiungi un nuovo libro</h1> <form name="aggiungi_libro" onsubmit="aggiungiLibro(this); return false;"> <fieldset name="info_libro"> <legend>Dati richiesti:</legend> <label>Titolo: <input name="titolo" type="text" required placeholder="es: Dalla terra alla luna"> </label> <label>Autore: <input name="autore" type="text" required placeholder="es: Jules Verne"> </label> <label>ISBN: <input name="isbn" type="text" required placeholder="es: 8862221320"> </label> <input type="submit" value="Aggiungi"> </fieldset> </form> </aside> </body> </html> Possiamo tranquillamente tralasciare la spiegazione della nuova porzione di codice HTML aggiunto: trattasi semplicemente di un form che all'invio chiama una funzione, ancora da sviluppare, per l'aggiunta di un nuovo libro. Molto più interessanti sono invece recuperaLibri e stampaLibri. In recuperaLibri viene creata una nuova transazione che coinvolge l'object store ‘libri'; il secondo parametro indica il tipo di transazione: purtroppo lettura/scrittura (IDBTransaction.READ_WRITE) è l'unica opzione supportata ad oggi; il terzo valore rappresenta invece il timeout della transazione: lo 0 utilizzato significa mai. Vediamo l'istruzione successiva: var request = transazione.objectStore("libri").openCursor(); La funzione openCursor inizializza un puntatore, detto anche cursore, al primo record dell'object store ‘libri'. La funzione stampaLibri, che viene chiamata appena il cursore è stato creato e popola una lista con i libri in catalogo, agisce come una specie di ciclo, infatti il metodo continue non fa nient'altro che richiamare nuovamente stampaLibri, posizionando però il cursore al record successivo; questo fino a quando non si giunge alla fine dei risultati di ricerca, a quel punto il valore del cursore diviene null e un apposito if si preoccupa dell'abbandono della funzione. Completiamo il nostro progetto con la funzione aggiungiLibro: ... aggiungiLibro = function(data){ var elements = data.elements window.transazione = database.transaction(["libri"], IDBTransaction.READ_WRITE, 0);
  • 91.
    var request =transazione.objectStore("libri").put({ titolo: elements['titolo'].value, autore: elements['autore'].value, isbn: elements[ 'isbn'].value }, elements[ 'isbn'].value)); request.onsuccess = function(){pulisciLista(); recuperaLibri();} request.onerror = function(){console.log("Ops, errore");} } pulisciLista = function(){ document.getElementById("catalogo_libri").innerHTML =""; } </script> Il metodo interessante in questo caso è il put, che si preoccupa di aggiungere al database, previa apertura di una transazione appropriata, un nuovo record con gli elementi ricevuti dal form (da notare il secondo parametro, corrispondente alla chiave del record). Da notare che esiste anche un metodo add che differisce nell'impedire la creazione di un record la cui chiave sia già presente nel database. Eseguiamo un ultima volta l'applicazione, proviamo ad inserire un paio di libri ed ammiriamone il risultato (figura 3): Figura 3 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_database/3.jpg) Una funzione di ricerca Aggiungiamo questa funzione al progetto: ... ricercaLibro = function(autore){ pulisciLista(); window.transazione = database.transaction(["libri"], IDBTransaction.READ_WRITE, 0); var request = transazione.objectStore("libri").index('autore').openCursor(
  • 92.
    IDBKeyRange.bound(autore,autore+"z",true,true), IDBCursor.NEXT); request.onsuccess = stampaLibri; request.onerror = function(){console.log("Ops, errore");} } </script> L'unica differenza rispetto alla già analizzata recuperaLibri sta nel fatto che, in questo caso, con il metodo ‘index(‘autore')' indichiamo al database che la selezione dovrà essere fatta utilizzando come discriminante l'indice ‘autore' creato sull'omonimo campo. In particolare tale selezione dovrà recuperare tutti i record il cui autore inizia con una stringa passata al metodo. Per provare questa funzione di ricerca creiamo un nuovo frammento HTML in coda alla form di inserimento: ... </form> <h1>Cerca un libro per autore</h1> <form name="cerca_per_autore" onsubmit="ricercaLibro(this.elements['keyword'].value); return false;"> <fieldset name="campi_ricerca"> <legend>Ricerca per autore:</legend> <label>Inizio del nome: <input name="keyword" type="search" required placeholder="es: Franc"> </label> <input type="submit" value="Ricerca"> </fieldset> </form> </aside> ... Ricarichiamo l'applicazione e proviamo ad inserire una valida stringa di ricerca (figura 4): Figura 4 (click per ingrandire)
  • 93.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_database/4.jpg) Conclusione Le Indexed DatabaseAPI sono uno strumento estremamente potente che consentono di beneficiare di un vero e proprio database all'interno del browser dell'utente. Le stesse funzionalità, anche se in una sintassi più orientata a SQL, venivano offerte anche dalle promettenti Web SQL Database API (http://www.w3.org/TR/webdatabase/), abbandonate dal 4 dicembre 2010 per motivi di sicurezza e di, ironia della sorte, troppa uniformità di implementazione da parte degli user-agent (avevano tutti scelto SQLlite come back-end per questo tipo di specifiche). Prima di passare alla prossima lezione ricordo che le stesse API che abbiamo usato in forma asincrona all'interno di questo progetto sono disponibili anche nel formato sincrono; per maggiori informazioni in questo senso rimando alla documentazione ufficiale (http://www.w3.org/TR/IndexedDB/). Per testare il tutto potete partire dalla demo (http://www.html.it/guide/esempi/html5/esempi/lezione_indexeddb/indexeddb.html). Tabella del supporto sui browser API e Web Applications Indexed Database API 9.0+ (parziale) 4.0+ (parziale) 5.0+ (parziale) 8.0+ (parziale) No WebStorage API
  • 94.
    Le WebStorage APInascono per risolvere due problematiche tipiche dei cookies; la loro limitata dimensione massima (tipicamente 4K) e l’impossibilità di avere cookies differenziati tra due diverse sessioni di navigazione sullo stesso dominio dallo stesso browser. Il secondo punto si esplicita molto bene cercando di mantenere aperti contemporaneamente due account Gmail sullo stesso browser, ogni navigazione sul primo comporterà il logout del secondo e viceversa. I problemi in questione sono stati risolti creando due nuovi oggetti. sessionStorage consente di avere un meccanismo di persistenza dei dati distinto per ogni sessione di navigazione in ogni finestra, o tab, del browser. Usando sessionStorage sarebbe quindi possibile coordinare l’apertura contemporanea di due distinti account GMail sullo stesso browser. localStorage mantiene il comportamento del cookie essendo comune a tutte finestre del browser che condividono lo stesso dominio. Entrambi sono inoltre stati studiati per ospitare molti più dati, almeno 5Mb, ed essere persistenti anche alla chiusura ed alla riapertura del browser. Le specifiche Le API si compongono di una singola interfaccia Storage. localStorage e sessionStorage implementano entrambi questa interfaccia e quindi dispongono dello stesso set di metodi anche se chiaramente restano due oggetti distinti. Le principali operazioni possono essere eseguite come su di un normale array associativo: localStorage.nome = 'Sandro'; localStorage.cognome = 'Paganotti'; o con l’ausilio di metodi dedicati: localStorage.setItem('nome','Sandro'); localStorage.getItem('nome'); // ritorna 'Sandro' Se però ci accingiamo ad inserire valori diversi da stringhe il comportamento diverge: in un oggetto come localStorage o sessionStorage è consentita la memorizzazione di soli contenuti testuali: localStorage.ordini = Array("1","2","3"); localStorage.ordini; // ritorna "1,2,3" È quindi necessario ricorrere a stratagemmi, come la serializzazione su JSON, per ottenere l’effetto desiderato: localStorage.ordini = JSON.stringify(Array(1,2,3)); JSON.parse(localStorage.ordini); // ritorna un array di 3 elementi: [1,2,3] Chromium mette a disposizione degli sviluppatori web che intendono utilizzare queste API la linguetta ‘Storage’ dei Developer Tools (raggiungibili da menu o con la combinazione di tasti Control - Shift - J e visibile in figura 1): Figura 1 (click per ingrandire)
  • 95.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_webstorage/1.jpg) Un esempio Utilizziamo ilprogetto guida per implementare una meccanica di salvataggio dei dati basata su localStorage. Non avendo ancora sviluppato la gestione della lavagna ci concentreremo sul memorizzare e visualizzare stringhe inserite dall’utente attraverso una textarea, iniziamo con il markup completando il file index.html: <!doctype html> <html lang='it' manifest='fiveboard.manifest'> <head> <meta charset="utf-8"> <title>FiveBoard: uno spazio per gli appunti.</title> <script src="js/application.js" defer></script> </head> <body> <hgroup> <h1>Dì qualcosa </h1> <h2>FiveBoard ricorda tutto</h2> </hgroup> <form name="form_da_ricordare"> <menu type="toolbar"> <button type="button" onclick="salvaIlDato(this.form.elements['testo_da_ricordar e'].value);"> Memorizza quanto scritto </button> <button type="button" onclick="recuperaIlDato(this.form.elements['testo_da_ricor dare']);"> Recupera l'ultimo testo memorizzato </button> </menu> <label>Cosa hai in mente? <textarea name="testo_da_ricordare" required autofocus placeholder="La lista della spesa, il teatro di questa sera ..."></textarea> </label> </form> </body> </html>
  • 96.
    Dato che questoprogetto si configura più come una web application che come un sito web, implementiamo una classica struttura di controllo desktop-oriented: la toolbar. All’interno di questa, due semplici pulsanti gestiscono le azioni di memorizzazione e recupero del testo salvato attraverso una textarea; l’attributo ‘autofocus’ indica allo user-agent di portare il cursore in posizione di inserimento testo nell’istante di completo caricamento della pagina. Ora, nel file application.js completiamo l’esempio introducendo le funzioni di salvataggio e di recupero informazioni basate su localStorage: salvaIlDato = function(info_da_salvare){ localStorage.ultimo_pensiero = info_da_salvare; alert("Memorizzazione effettuata"); }; recuperaIlDato = function(elemento){ if(confirm("Sostituire il contenuto attuale con l'ultimo pensiero memorizzato?")){ elemento.value = localStorage.ultimo_pensiero; } }; Bene, non ci resta che provare l’esempio (http://www.html.it/guide/esempi/html5/esempi/lezione_webstorage/fiveboard/index.html) all’interno di Chromium e constatare il suo semplice ma efficace funzionamento, capace di persistere l’informazione anche a seguito di un riavvio del browser. Tabella del supporto sui browser API e Web Applications WebStorage 8.0+ 3.6+ 3.1+ 2.0+ 10.5+ Web Workers API I Web Workers nascono per consentire l’esecuzione di porzioni di codice Javascript in modo asincrono, senza intaccare le performance della pagina web in visualizzazione. I Web Workers, nient’altro che file Javascript, possono essere comparati a dei thread che la pagina web può lanciare e con i quali può dialogare attraverso semplici metodi. Ogni WebWorker può eseguire a sua volta altri WebWorkers ed ognuno di essi può effettuare operazioni di I/O, calcoli complessi e quant’altro. Le API in questione prevedono che un WebWorker possa essere generato come oggetto della classe Worker o SharedWorker: nel primo caso la sua esecuzione sarà limitata alla specifica sessione di navigazione all’interno della finestra (o tab) del browser che l’ha invocato; nel secondo invece ogni sessione di navigazione che condivide la stessa origine (lo stesso dominio) potrà connettersi e scambiare messaggi con il medesimo worker. In questo modo lo SharedWorker
  • 97.
    assume il ruolodi coordinatore, ottimo per, ad esempio, propagare su tutte le finestre del browser puntate su di un particolare dominio un messaggio ricevuto dal server. Le specifiche Per creare un Worker l’istruzione da eseguire è decisamente semplice: // pagina principale bot_noioso = new Worker('bot_noioso.js'); Lo script invocato, ‘bot_noioso.js’, verrà eseguito in asincrono e potrà inviare messaggi verso la pagina principale attraverso la funzione postMessage: // bot_noioso.js postMessage('Perch´ la terra è tonda?'); Per ricevere questi messaggi è necessario registrare una funzione all’handler ‘onmessage’ del worker contenuto nella variabile bot_noioso: chiaramente utilizzando la stessa variabile è possibile inviare, sempre tramite postMessage messaggi al worker. // pagina principale bot_noioso = new Worker('bot_noioso.js'); bot_noioso.onmessage = function(event){ bot_noioso.postMessage(prompt(event.data)); } Allo stesso modo è possibile registrare l’handler ‘onmessage’ anche all’interno del file del worker: // bot_noioso.js postMessage('Perch´ la terra è tonda?'); onmessage = function(event){ if(event.data != null){ postMessage('Perch´:' + event.data + ' ?'); } } Ora, se lanciamo Chromium potremo sincerarci del buon funzionamento dello script (figura 1): Figura 1 - Testo
  • 98.
    Le API delloSharedWorker differiscono in modo sensibile rispetto a queste, anche se ovviamente mantengono immutato il funzionamento di base; per poterle trattare con la dovuta cura costruiamo un piccolo esempio che le utilizzi. Ecco la demo (http://www.html.it/guide/esempi/html5/esempi/lezione_webworkers/webworker.html). Un esempio Continuiamo con il progetto guida; l’obiettivo di questo capitolo è beneficiare delle feature di uno SharedWorker per implementare una dashboard di controllo, che amministri le sessioni di Fiveboard aperte contemporaneamente in un browser. Per raggiungere questo (ambizioso) bersaglio creeremo un file ‘js/hub.js’, presso il quale tutte le finestre attive del progetto dovranno registrarsi, che convoglierà le informazioni da e verso una console ‘dashboard.html’. Ecco uno schema dell’architettura (figura 2): Figura 2 (click per ingrandire)
  • 99.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/2.jpg) Le comunicazioni dae verso lo SharedWorker transitano attraverso oggetti chiamati MessagePort, nell’immagine qui sopra sono esplicitate 4 MessagePort. Una MessagePort è utilizzata implicitamente anche dall’oggetto Worker del capitolo precedente e quindi non ci si deve sorprendere nell’apprendere che il meccanismo di funzionamento di uno SharedWorker è esattamente lo stesso, fatto salvo che in questo caso è necessario specificare la porta attraverso la quale si vuole inviare o ricevere il messaggio. // Invio di un messaggio ad uno SharedWorker worker.port.postMessage('...'); // Ricezione di un messaggio da uno SharedWorker worker.port.onmessage = function(evento){ ... } // Dall’interno di uno SharedWorker: invio di un messaggio messageport_del_destinatario.postMessage('...'); // Dall’interno di uno SharedWorker: ricezione di un messaggio messageport_del_mittente.onmessage = function(event){ ... } Bene, ecco in breve come dovranno comportarsi i 3 componenti che andremo a definire: Index.html Connettersi allo SharedWorker; Presentarsi come Client e specificare il proprio nome (il titolo del documento); Dashboard.html Connettersi allo SharedWorker; Presentarsi come dashboard e ricevere informazioni su tutti i Client già registrati; Essere notificata ad ogni nuova registrazione di un Client;
  • 100.
    Mantenere a videoun elenco dei Client registrati; Hub.js (SharedWorker) Registrare ogni Client e notificare, se presente, la dashboard; Registrare la dashboard ed inviarle tutti i Client registrati fino a quel momento. Lo scambio di messaggi tra le varie parti avverrà tramite stringhe di testo nel formato “chiave:valore”, come ad esempio “registra_client:Documento di prova”; questa scelta non è dettata dalle specifiche, che sono abbastanza lasche in tal senso, ma da una semplice convenzione adottata anche da alcuni esempi del W3C (http://www.whatwg.org/specs/web-apps/current- work/complete/workers.html#shared-state-using-a-shared-worker). Bene, possiamo partire; iniziamo dal file ‘application.js’ che dovrà assumere questo aspetto: salvaIlDato = function(info_da_salvare){ localStorage.setItem("fb_" + titolo_fiveboard,info_da_salvare); alert("Memorizzazione effettuata"); }; recuperaIlDato = function(elemento){ if(confirm("Sostituire il contenuto attuale con l'ultimo pensiero memorizzato?")){ elemento.value = localStorage.getItem("fb_" + titolo_fiveboard); } }; var titolo_fiveboard = null; window.onload = function(){ var worker = new SharedWorker('js/hub.js'); worker.port.onmessage = function(evento){ nome_comando = evento.data.split(":")[0] valore_comando = evento.data.substr(nome_comando.length + 1); console.log("Ricevuto comando: " + nome_comando); switch (nome_comando){ case 'pronto': titolo_fiveboard = prompt("Seleziona il titolo per questa FiveBoard"); document.title = "FB: " + titolo_fiveboard; worker.port.postMessage("registra_client:" + titolo_fiveboard); break; } } } Al caricamento della pagina viene attivata la funzione collegata a window.onload: questa crea il collegamento con lo SharedWorker (se il worker non è già presente verrà caricato in memoria e lanciato) e definisce una funzione di ‘ascolto’ nella quale, per ogni messaggio ricevuto, esegue particolari azioni a seconda della chiave estratta. In questo momento la funzione reagisce alla sola chiave ‘pronto’ chiedendo all’utente un titolo del documento ed inviando il risultato al worker con la chiave ‘registra_client’. Il titolo del documento viene anche utilizzato nelle funzioni di salvataggio e di recupero del dato, per differenziare fra di loro le variabili memorizzate su LocalStorage in modo da evitare collisioni durante l’apertura contemporanea di più index.html. Ora definiamo hub.js:
  • 101.
    var fiveboards_registrate =new Array(); var dashboard = null; processa_il_messaggio = function(evento){ nome_comando = evento.data.split(":")[0] valore_comando = evento.data.substr(nome_comando.length + 1); switch (nome_comando){ case 'registra_client': fiveboards_registrate[valore_comando]=evento.target; if(dashboard != null){ dashboard.postMessage("nuova_fiveboard:" + valore_comando); } break; case 'registra_dashboard': dashboard = evento.target; for(fiveboard in fiveboards_registrate){ evento.target.postMessage("nuova_fiveboard:" + fiveboard); } break; } } onconnect = function(nuova_finestra){ var port = nuova_finestra.ports[0]; port.onmessage = processa_il_messaggio; port.postMessage("pronto"); } L’handler onconnect viene invocato ogni qualvolta una pagina tenta di aprire una connessione verso il worker; nella funzione si delega al metodo ‘processa_il_messaggio’ la gestione dei futuri scambi tra in worker e la pagina; a quest’ultima è inoltre segnalato il concludersi delle operazioni con un messaggio ‘pronto’. La funzione processa_il_messaggio interpreta e gestisce le due chiavi registra_client e registra_dashboard: nel primo caso aggiunge la MessagePort di comunicazione con il client ad una collezione fiveboards_registrate e comunica alla dashboard, se presente, la nuova aggiunta tramite un messaggio con chiave nuova_fiveboard. Nel secondo caso registra la MessagePort della dashboard (attraversoevento.target, che corrisponde ad evento.ports[0]) e comunica alla stessa tutti client finora memorizzati attraverso un ciclo sulla collezione fiveboards_registrate e ad un messaggio con la già nota chiave nuova_fiveboard. Perfetto, ora non ci resta che definire markup e Javascript di ‘dashboard.html’: <html lang='it'> <head> <meta charset="utf-8"> <title>Five(Dash)Board: tutto sotto controllo!.</title> <script> var worker = null; getInfo = function(title){ // to be defined...
  • 102.
    } init= function(){ worker = new SharedWorker('js/hub.js'); worker.port.onmessage = function(evento){ nome_comando = evento.data.split(":")[0] valore_comando = evento.data.substr(nome_comando.length + 1); console.log("Ricevuto comando: " + nome_comando); switch (nome_comando){ case 'pronto': worker.port.postMessage("registra_dashboard") break; case 'nuova_fiveboard': document.getElementById("elenco_fiveboard").insertAdjacentHTML('beforeend', "<li>" + "Titolo: " + valore_comando + " " + "(<a href='javascript:getInfo("" + valore_comando +"");'>" + "più informazioni" + "</a>)" + "</li>"); break; } } } </script> </head> <body onload="init();"> <h1>FiveBoard:</h1> <ol id="elenco_fiveboard"> </ol> </body> </html> Il listato è largamente autoesplicativo sulla base di quanto già enunciato: in questo caso le chiavi gestite sono ‘pronto’ e ‘nuova_fiveboard’: mentre la prima attiva il meccanismo di registrazione (registra_dashboard) appena visto in hub.js, la seconda si incarica di popolare una lista ordinata ogniqualvolta la pagina riceve una notifica di registrazione di una nuova fiveboard. Eseguiamo il tutto in Chromium e godiamoci il risultato dei nostri sforzi (figura 3): Figura 3 (click per ingrandire)
  • 103.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/3.jpg) È possibile verificareil tutto in questa demo (http://www.html.it/guide/esempi/html5/esempi/lezione_webworkers/fiveboard/index.html). Più informazioni per i più ardimentosi Vi siete chiesti a cosa debba servire il link ‘più informazioni’ e la funzione getInfo ad esso collegata? L’idea è questa: al click sul comando il sistema deve mostrare all’utente il testo inserito ed il testo memorizzato per l’istanza selezionata. Per raggiungere l’obiettivo ecco come interverremo: 1. La dashboard chiede allo SharedWorker maggiori_informazioni per una data finestra; 2. Lo SharedWorker mette in diretta comunicazione la finestra in oggetto e la dashboard; 3. La finestra in oggetto comunica alla dashboard testo inserito e testo memorizzato. Ecco uno schema delle comunicazioni (figura 4): Figura 4 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/4.jpg) Il punto 2 è decisamente il più interessante e si avvale di MessageChannel: trattasi di un semplicissimo oggetto, nato all’interno delle API di comunicazione HTML5 (http://www.w3.org/TR/html5/comms.html), contenente due MessagePort ed istruito per fare in modo che ogni messaggio in ingresso alla porta1 venga recapitato alla porta2 e viceversa. Tutto quello che hub.js deve fare è quindi creare un MessageChannel e dare i due capi del filo (le due MessagePort) rispettivamente alla dashboard ed al client interessato; per fare questo risulta molto comodo il secondo argomento del metodo postMessage, che consente appunto di specificare un array di porte da allegare ad un messaggio. Tutto chiaro? No? Vediamo le modifiche apportate al progetto: // file dashboard.html
  • 104.
    // invio dellarichiesta allo SharedWorker (punto 1) getInfo = function(title){ worker.port.postMessage("maggiori_informazioni:" + title); } // una nuova chiave da gestire. case 'attendi_testo': evento.ports[0].onmessage = function(e){ alert(e.data); } break; // file hub.js // una nuova chiave da gestire, creazione del canale ed invio delle porte di // comunicazione alla dashboard ed al client interessato (punto 2) case 'maggiori_informazioni': var channel = new MessageChannel(); dashboard.postMessage("attendi_testo",[channel.port1]); fiveboards_registrate[valore_comando].postMessage("richiedi_testo", [channel.port2]); break; // file application.js // una nuova chiave da gestire, invio delle informazioni richieste attraverso la // MessagePort ricevuta dall’evento (non la MessagePort di comunicazione col worker) // (punto 3) case 'richiedi_testo': evento.ports[0].postMessage( "testo corrente:" + document.forms['form_da_ricordare'].elements ['testo_da_ricordare'].value + "n" + "testo memorizzato:" + localStorage.getItem("fb_" + titolo_fiveboard) ); break; Eseguiamo il tutto in Chromium ed ammiriamone il risultato (figura 5): Figura 5 (click per ingrandire)
  • 105.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_webworkers/5.jpg) Conclusioni I Web Workersono il classico esempio di API semplici solo in apparenza; più ci si addentra in utilizzi concreti più si scoprono possibilità e nuove metodologie di sviluppo. È importante ricordare infatti che sono previsti meccanismi di interazione tra i Web Workers, l’ApplicationCache, e i WebSocket, e che ogni WebWorker può invocarne altri. Sono diretta conseguenza di questa intrinseca flessibilità delle API gli innumerevoli scenari di utilizzo ipotizzabili oltre a quello già mostrato, come ad esempio svolgere calcoli complessi (ogni Worker può girare su di un core differente) quali rendering o image detection in tempo reale, centralizzare le comunicazioni con il server attraverso uno SharedWorker e un WebSocket oppure monitorare processi browser-side. Tabella del supporto sui browser API e Web Applications WebWorkers No 3.6+ 4.0+ 2.0+ 10.6+ WebSockets API Le WebSockets API introducono, nella loro estrema semplicità, una funzionalità tra le più attese ed
  • 106.
    emulate: la possibilitàdi stabilire e mantenere una connessione dati tra browser e server remotosulla quale far transitare messaggi in entrambe le direzioni. Le attuali specifiche, che lasciano ben poco spazio per implementazioni del genere, hanno, nel corso degli anni, dato luogo a workaround più o meno esotici tra i quali l'utilizzo di socket in Flash pilotati via Javascript e della famosa tecnica di long polling (l'utilizzo continuo di chiamate AJAX mantenute aperte fino alla ricezione del dato o al tempo di timeout). Le nuove API offrono invece un meccanismo ben più semplice grazie all'oggetto WebSocket, al metodo send e all'evento onmessage. Prima di passare alla visione delle specifiche e al dovuto esempio di implementazione è importante ricordare che questa tecnologia non consente di creare connessioni verso altri, ben conosciuti protocolli, come ad esempio telnet, SMTP, IRC, etc., per due distinti motivi: in primo luogo lo user agent implementa una policy che blocca l'accesso verso porte riservate a servizi conosciuti (fanno eccezione solo la 80 e la 443). In seconda istanza le comunicazioni viaggiano all'interno di un nuovo e specifico protocollo, chiamato con molta fantasia 'The WebSocket Protocol (http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03)' che richiama per certi aspetti, soprattutto durante l'handshake (http://en.wikipedia.org/wiki/WebSockets#WebSocket_Protocol_Handshake), una conversazione HTTP. Le specifche L'utilizzo di queste API è assolutamente didascalico, ci troviamo di fronte infatti a poco più di tre metodi; vediamoli insieme: var echo_service = new WebSocket('ws://echo.websocket.org'); La creazione di un nuovo WebSocket richiede come unico parametro obbligatorio l'url verso la quale si vuole stabilire la connessione. Il protocollo può essere ws o wss, dove il secondo indica la richiesta di una connessione sicura. Opzionalmente è possibile passare al costruttore anche una stringa o un array di sub-protocolli: valori arbitrari utili per comunicare al server un elenco di servizi che l'oggetto in costruzione può supportare. Ad esempio un server di chat potrebbe rispondere solo a richieste con protocollo 'server_di_chat', e via dicendo... echo_service.onmessage = function(event){ alert(event.data); } Una volta creato un nuovo WebSocket, il funzionamento dello stesso diventa praticamente identico, nella forma, a quello già esposto per la comunicazione tra Worker: la funzione associata all'handleronmessage viene invocata ogniqualvolta dal server proviene un messaggio, echo_service.onopen = function(){ echo_service.send("hello!"); } mentre la funzione send provvede all'invio, verso il server remoto, del testo passato come argomento. Da notare che l'invio deve essere necessariamente subordinato alla condizione di avvenuta connessione, notificata tramite l'evento onopen. Esistono altri eventi all'interno del ciclo vita di un WebSocket: onclose e onerror; vediamoli insieme completando l'esempio: <!doctype html>
  • 107.
    <html> <head> <title>WebSocket: Echo Server </title> <script> append = function(text){ document.getElementById("eventi_websocket").insertAdjacentHTML('beforeend', "<li>" + text + ";</li>" ); } window.onload = function(){ var echo_service = new WebSocket('ws://echo.websocket.org'); echo_service.onmessage = function(event){ append("messaggio ricevuto") alert(event.data); echo_service.close(); } echo_service.onopen = function(){ append("connessione effettuata") echo_service.send("hello!"); } echo_service.onclose = function(){ append("connessione chiusa"); } echo_service.onerror = function(){ append("errore nella connessione"); } } </script> </head> <body> <ul id="eventi_websocket"> </ul> </body> </html> In questo esempio è stato introdotto anche il metodo close, utile per terminare una connessione. Eseguiamo l'esempio all'interno di Chromium (figura 1): Figura 1 (click per ingrandire)
  • 108.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/1.jpg) Prima di proseguireè bene ricordare che il server utilizzato per questo esempio (demo (esempi/lezione_websockets/websocket.html)): echo.websocket.org ha la peculiarità, come il nome stesso suggerisce, di rispondere ad ogni messaggio con lo stesso testo ricevuto. Un esempio Nel prossimo capitolo estenderemo il progetto guida in modo che ogni FiveBoard sia connessa ad un server centrale; creeremo quindi un viewer: una pagina html capace di connettersi ad una FiveBoard registrata presso il server e leggerne il testo mano mano esso viene digitato. Ecco lo schema (figura 2): Figura 2 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/2.jpg) Per poter raggiungere questo obiettivo è necessario scrivere anche del codice in un linguaggio server- side. Tale codice servirà per creare e istruire il WebSocket Server, al quale le varie pagine dovranno connettersi; ai fini di questo esempio non è necessario cercare un servizio di hosting sul quale installare il codice del WebSocket Server: la nostra macchina di sviluppo andrà benissimo. Per questa
  • 109.
    implementazione utilizzeremo Ruby(http://ruby.html.it), un linguaggio di programmazione elegante e conciso. L'installazione dell'interprete Ruby è veramente facile ed il codice che utilizzeremo molto leggibile. Per prima cosa colleghiamoci al portale ufficiale: http://www.ruby-lang.org/it/downloads/ (http://www.ruby-lang.org/it/downloads/) e selezioniamo la procedura di installazione dedicata al nostro sistema operativo, quindi apriamo una console di comando (a volte chiamata anche terminale) e digitiamo: gem install em-websocket Per installare la libreria necessaria allo sviluppo del WebSocket Server (per alcuni sistemi operativi è necessario anteporre sudo all'istruzione). Creiamo ora un file 'websocket_server.rb' e modifichiamone il contenuto come segue: require 'rubygems' require 'em-websocket' EventMachine.run { @channels = Hash.new {|h,k| h[k] = EM::Channel.new } EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do | ws| ws.onopen do sid = nil fiveboard_channel = nil ws.onmessage do |msg| command, value = msg.split(":", 2); case command when 'registra' fiveboard_channel = @channels[value] sid = fiveboard_channel.subscribe { |txt| ws.send(txt) } when 'aggiorna' fiveboard_channel.push('testo:' + value) end end ws.onclose do fiveboard_channel.unsubscribe(sid) end end end puts "Il server è correttamente partito" } Seppur possiate essere non abituati a questo linguaggio il codice è tutto sommato comprensibile e succinto, ecco la spiegazione dell'algoritmo: Con l'istruzione WebSocket.start(.. l'applicazione si mette in attesa di connessioni websocket sulla porta 8080; ogni connessione in ingresso viene memorizzata nella variabile ws e causa l'esecuzione delle successive istruzioni (quelle comprese nell'attiguo blocco do..end).
  • 110.
    Alla ricezione diun messaggio attraverso una connessione ws (ws.onmessage) il server si comporta dividendo il testo ricevuto secondo la solita convenzione 'comando:valore' ed agendo in modo diverso a seconda che il comando sia 'registra' o 'aggiorna'. Nel caso il messaggio sia 'registra:titolo_del_documento' il server aggiungerà la connessione attuale ad un canale che porta il nome del valore del messaggio (in questo caso 'titolo_del_documento'). In questo modo tutte le pagine che vorranno 'osservare' il documento 'A' non dovranno far altro che inviare al WebSocket Server il messaggio 'registra:A'. Nel caso il messaggio sia 'aggiorna:testo_del_documento' il server si comporterà semplicemente inviando lungo il canale associato alla connessione corrente il valore del messaggio (in questo caso 'testo_del_documento'), propagandolo in questo modo a tutte le connessioni registrate al canale. Infine con l'istruzione ws.onclose do... si gestisce, in caso di disconnessione del client, la rimozione di ws dal canale presso il quale si era registrata. Eseguiamo il server portandoci con il terminale nella posizione dello script e digitando: ruby websocket_server.rb Un messaggio, 'Il server è correttamente partito', dovrebbe confermare la bontà del nostro operato. Dedichiamoci ora alle API Javascript ed alla loro implementazione, per prima cosa editiamo il file 'js/application.js' per fare in modo che ogni FiveBoard si registri presso il server ed invii segnali di aggiornamento ad ogni modifica del testo, ecco il codice da inserire all'interno della funzione window.onload: // all'interno di window.onload, js/application.js // queste due istruzioni sono state spostate dalla loro precedente posizione titolo_fiveboard = prompt("Seleziona il titolo per questa FiveBoard"); document.title = "FB: " + titolo_fiveboard; // creazione di un nuovo socket verso il server Ruby websocket = new WebSocket('ws://0.0.0.0:8080'); websocket.onopen = function(){ // invio del comando 'registra' websocket.send("registra:" + titolo_fiveboard); } // ad ogni variazione di input segue l'invio del comando 'aggiorna' / / verso il server Ruby document.forms['form_da_ricordare'].elements['testo_da_ricordare'].oninput = function (event){ websocket.send("aggiorna:" + event.target.value); } Anche in questo caso l'implementazione risulta abbastanza leggibile; unico appunto da fare sull'evento oninput, anch'esso novità introdotta dalle specifiche HTML5, che viene invocato ad ogni attività di input (pressione di un tasto, copia ed incolla, drag and drop,...) sull'elemento in oggetto. Completiamo l'esempio con la creazione della semplicissima pagina 'viewer.html': <!doctype html> <html>
  • 111.
    <head> <title>FiveBoardViewer</title> <script> window.onload = function(){ var documento_da_visionare = prompt("Inserisci il nome del documento che vuoi osservare"); var websocket = new WebSocket('ws://0.0.0.0:8080'); websocket.onopen = function(){ document.title = "VB: " + documento_da_visionare; websocket.send("registra:" + documento_da_visionare); } websocket.onmessage = function(evento){ nome_comando = evento.data.split(":")[0] valore_comando = evento.data.substr(nome_comando.length + 1); switch (nome_comando){ case 'testo': document.getElementById('documento_in_visione').value = valore_comando; break; } } } </script> </head> <body> <textarea id="documento_in_visione" readonly>Aspettando il primo aggiornamento...</ textarea> </body> </html> In questo caso la pagina è istruita nel reagire alla ricezione di un messaggio da parte del WebSocket Server; il valore ricevuto viene infatti gestito con la solita convenzione 'comando:valore' e, nel caso il comando sia 'testo', il valore viene inserito all'interno di una textarea preposta. Bene, eseguiamo una prova di funzionamento (http://www.html.it/guide/esempi/html5/esempi/lezione_websockets/fiveboard/index.html): sincerandoci di aver lanciato il WebSocket Server navighiamo prima sulla pagina 'index.html' e creiamo un nuovo documento 'esempio1', quindi apriamo una nuova finestra e puntiamola all'indirizzo di 'viewer.html': alla richiesta del nome del documento inseriamo anche qui 'esempio1' in modo da legare il viewer alla fiveboard. Ora proviamo a digitare alcuni caratteri sulla prima finestra e noteremo che, attraverso il server, questi sono propagati in tempo reale alla seconda! Ovviamente il tutto funzionerebbe anche se la conversazione avvenisse tra due macchine distinte attraverso un server remoto; nell'immagine seguente si può notare una fiveboard aperta sull'iPad ed il suo corrispondente viewer visibile su di un portatile; in alto i messaggi di log del WebSocket Server (figura 3). Figura 3 (click per ingrandire)
  • 112.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/3.jpg) Conclusioni I WebSocket, sononella loro estrema semplicità, degli strumenti incredibilmente potenti; a riprova di questo fatto la rete ha già incominciato ad offrire interessanti prospettive di utilizzo nonostante l'innegabile giovinezza di queste API. Tra le soluzioni degne di nota merita sicuramente una citazione Pusher (http://pusherapp.com/), un servizio che offre la possibilità di gestire eventi real-time attraverso l'utilizzo di WebSocket. Un po' più artigianale, ma altrettanto valido è http://jsterm.com/ (http://jsterm.com/), un'applicazione che consente di collegarsi a server remoti utilizzando un proxy, scritto in node.js, tra il protocollo WebSocket e Telnet. Recentemente lo sviluppo delle API è stato frenato dalla scoperta di una seria vulnerabilità [documento PDF] (http://www.adambarth.com/experimental/websocket.pdf) legata al potenziale comportamento di un caching proxy che può essere indotto, attraverso l'utilizzo di WebSocket ad-hoc, a modificare il contenuto delle informazioni consegnate ad altri client. La vulnerabilità è stata correttamente risolta dalla versione 0.7 del protocollo ma sfortunatamente le versioni 4 e 5 di Firefox, rilasciate prima del fix, hanno i websocket disabilitati per default; la versione 6 del browser dovrebbe però ripristinare il funzionamento out-of-the-box di questa interessantissima feature. Tabella del supporto sui browser API e Web Applications WebSockets No 4.0+ (parziale) 5.0+ 7.0+ 11.0+ (parziale) WebSockets API Le WebSockets API introducono, nella loro estrema semplicità, una funzionalità tra le più attese ed
  • 113.
    emulate: la possibilitàdi stabilire e mantenere una connessione dati tra browser e server remotosulla quale far transitare messaggi in entrambe le direzioni. Le attuali specifiche, che lasciano ben poco spazio per implementazioni del genere, hanno, nel corso degli anni, dato luogo a workaround più o meno esotici tra i quali l'utilizzo di socket in Flash pilotati via Javascript e della famosa tecnica di long polling (l'utilizzo continuo di chiamate AJAX mantenute aperte fino alla ricezione del dato o al tempo di timeout). Le nuove API offrono invece un meccanismo ben più semplice grazie all'oggetto WebSocket, al metodo send e all'evento onmessage. Prima di passare alla visione delle specifiche e al dovuto esempio di implementazione è importante ricordare che questa tecnologia non consente di creare connessioni verso altri, ben conosciuti protocolli, come ad esempio telnet, SMTP, IRC, etc., per due distinti motivi: in primo luogo lo user agent implementa una policy che blocca l'accesso verso porte riservate a servizi conosciuti (fanno eccezione solo la 80 e la 443). In seconda istanza le comunicazioni viaggiano all'interno di un nuovo e specifico protocollo, chiamato con molta fantasia 'The WebSocket Protocol (http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03)' che richiama per certi aspetti, soprattutto durante l'handshake (http://en.wikipedia.org/wiki/WebSockets#WebSocket_Protocol_Handshake), una conversazione HTTP. Le specifche L'utilizzo di queste API è assolutamente didascalico, ci troviamo di fronte infatti a poco più di tre metodi; vediamoli insieme: var echo_service = new WebSocket('ws://echo.websocket.org'); La creazione di un nuovo WebSocket richiede come unico parametro obbligatorio l'url verso la quale si vuole stabilire la connessione. Il protocollo può essere ws o wss, dove il secondo indica la richiesta di una connessione sicura. Opzionalmente è possibile passare al costruttore anche una stringa o un array di sub-protocolli: valori arbitrari utili per comunicare al server un elenco di servizi che l'oggetto in costruzione può supportare. Ad esempio un server di chat potrebbe rispondere solo a richieste con protocollo 'server_di_chat', e via dicendo... echo_service.onmessage = function(event){ alert(event.data); } Una volta creato un nuovo WebSocket, il funzionamento dello stesso diventa praticamente identico, nella forma, a quello già esposto per la comunicazione tra Worker: la funzione associata all'handleronmessage viene invocata ogniqualvolta dal server proviene un messaggio, echo_service.onopen = function(){ echo_service.send("hello!"); } mentre la funzione send provvede all'invio, verso il server remoto, del testo passato come argomento. Da notare che l'invio deve essere necessariamente subordinato alla condizione di avvenuta connessione, notificata tramite l'evento onopen. Esistono altri eventi all'interno del ciclo vita di un WebSocket: onclose e onerror; vediamoli insieme completando l'esempio: <!doctype html>
  • 114.
    <html> <head> <title>WebSocket: Echo Server </title> <script> append = function(text){ document.getElementById("eventi_websocket").insertAdjacentHTML('beforeend', "<li>" + text + ";</li>" ); } window.onload = function(){ var echo_service = new WebSocket('ws://echo.websocket.org'); echo_service.onmessage = function(event){ append("messaggio ricevuto") alert(event.data); echo_service.close(); } echo_service.onopen = function(){ append("connessione effettuata") echo_service.send("hello!"); } echo_service.onclose = function(){ append("connessione chiusa"); } echo_service.onerror = function(){ append("errore nella connessione"); } } </script> </head> <body> <ul id="eventi_websocket"> </ul> </body> </html> In questo esempio è stato introdotto anche il metodo close, utile per terminare una connessione. Eseguiamo l'esempio all'interno di Chromium (figura 1): Figura 1 (click per ingrandire)
  • 115.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/1.jpg) Prima di proseguireè bene ricordare che il server utilizzato per questo esempio (demo (esempi/lezione_websockets/websocket.html)): echo.websocket.org ha la peculiarità, come il nome stesso suggerisce, di rispondere ad ogni messaggio con lo stesso testo ricevuto. Un esempio Nel prossimo capitolo estenderemo il progetto guida in modo che ogni FiveBoard sia connessa ad un server centrale; creeremo quindi un viewer: una pagina html capace di connettersi ad una FiveBoard registrata presso il server e leggerne il testo mano mano esso viene digitato. Ecco lo schema (figura 2): Figura 2 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/2.jpg) Per poter raggiungere questo obiettivo è necessario scrivere anche del codice in un linguaggio server- side. Tale codice servirà per creare e istruire il WebSocket Server, al quale le varie pagine dovranno connettersi; ai fini di questo esempio non è necessario cercare un servizio di hosting sul quale installare il codice del WebSocket Server: la nostra macchina di sviluppo andrà benissimo. Per questa
  • 116.
    implementazione utilizzeremo Ruby(http://ruby.html.it), un linguaggio di programmazione elegante e conciso. L'installazione dell'interprete Ruby è veramente facile ed il codice che utilizzeremo molto leggibile. Per prima cosa colleghiamoci al portale ufficiale: http://www.ruby-lang.org/it/downloads/ (http://www.ruby-lang.org/it/downloads/) e selezioniamo la procedura di installazione dedicata al nostro sistema operativo, quindi apriamo una console di comando (a volte chiamata anche terminale) e digitiamo: gem install em-websocket Per installare la libreria necessaria allo sviluppo del WebSocket Server (per alcuni sistemi operativi è necessario anteporre sudo all'istruzione). Creiamo ora un file 'websocket_server.rb' e modifichiamone il contenuto come segue: require 'rubygems' require 'em-websocket' EventMachine.run { @channels = Hash.new {|h,k| h[k] = EM::Channel.new } EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do | ws| ws.onopen do sid = nil fiveboard_channel = nil ws.onmessage do |msg| command, value = msg.split(":", 2); case command when 'registra' fiveboard_channel = @channels[value] sid = fiveboard_channel.subscribe { |txt| ws.send(txt) } when 'aggiorna' fiveboard_channel.push('testo:' + value) end end ws.onclose do fiveboard_channel.unsubscribe(sid) end end end puts "Il server è correttamente partito" } Seppur possiate essere non abituati a questo linguaggio il codice è tutto sommato comprensibile e succinto, ecco la spiegazione dell'algoritmo: Con l'istruzione WebSocket.start(.. l'applicazione si mette in attesa di connessioni websocket sulla porta 8080; ogni connessione in ingresso viene memorizzata nella variabile ws e causa l'esecuzione delle successive istruzioni (quelle comprese nell'attiguo blocco do..end).
  • 117.
    Alla ricezione diun messaggio attraverso una connessione ws (ws.onmessage) il server si comporta dividendo il testo ricevuto secondo la solita convenzione 'comando:valore' ed agendo in modo diverso a seconda che il comando sia 'registra' o 'aggiorna'. Nel caso il messaggio sia 'registra:titolo_del_documento' il server aggiungerà la connessione attuale ad un canale che porta il nome del valore del messaggio (in questo caso 'titolo_del_documento'). In questo modo tutte le pagine che vorranno 'osservare' il documento 'A' non dovranno far altro che inviare al WebSocket Server il messaggio 'registra:A'. Nel caso il messaggio sia 'aggiorna:testo_del_documento' il server si comporterà semplicemente inviando lungo il canale associato alla connessione corrente il valore del messaggio (in questo caso 'testo_del_documento'), propagandolo in questo modo a tutte le connessioni registrate al canale. Infine con l'istruzione ws.onclose do... si gestisce, in caso di disconnessione del client, la rimozione di ws dal canale presso il quale si era registrata. Eseguiamo il server portandoci con il terminale nella posizione dello script e digitando: ruby websocket_server.rb Un messaggio, 'Il server è correttamente partito', dovrebbe confermare la bontà del nostro operato. Dedichiamoci ora alle API Javascript ed alla loro implementazione, per prima cosa editiamo il file 'js/application.js' per fare in modo che ogni FiveBoard si registri presso il server ed invii segnali di aggiornamento ad ogni modifica del testo, ecco il codice da inserire all'interno della funzione window.onload: // all'interno di window.onload, js/application.js // queste due istruzioni sono state spostate dalla loro precedente posizione titolo_fiveboard = prompt("Seleziona il titolo per questa FiveBoard"); document.title = "FB: " + titolo_fiveboard; // creazione di un nuovo socket verso il server Ruby websocket = new WebSocket('ws://0.0.0.0:8080'); websocket.onopen = function(){ // invio del comando 'registra' websocket.send("registra:" + titolo_fiveboard); } // ad ogni variazione di input segue l'invio del comando 'aggiorna' / / verso il server Ruby document.forms['form_da_ricordare'].elements['testo_da_ricordare'].oninput = function (event){ websocket.send("aggiorna:" + event.target.value); } Anche in questo caso l'implementazione risulta abbastanza leggibile; unico appunto da fare sull'evento oninput, anch'esso novità introdotta dalle specifiche HTML5, che viene invocato ad ogni attività di input (pressione di un tasto, copia ed incolla, drag and drop,...) sull'elemento in oggetto. Completiamo l'esempio con la creazione della semplicissima pagina 'viewer.html': <!doctype html> <html>
  • 118.
    <head> <title>FiveBoardViewer</title> <script> window.onload = function(){ var documento_da_visionare = prompt("Inserisci il nome del documento che vuoi osservare"); var websocket = new WebSocket('ws://0.0.0.0:8080'); websocket.onopen = function(){ document.title = "VB: " + documento_da_visionare; websocket.send("registra:" + documento_da_visionare); } websocket.onmessage = function(evento){ nome_comando = evento.data.split(":")[0] valore_comando = evento.data.substr(nome_comando.length + 1); switch (nome_comando){ case 'testo': document.getElementById('documento_in_visione').value = valore_comando; break; } } } </script> </head> <body> <textarea id="documento_in_visione" readonly>Aspettando il primo aggiornamento...</ textarea> </body> </html> In questo caso la pagina è istruita nel reagire alla ricezione di un messaggio da parte del WebSocket Server; il valore ricevuto viene infatti gestito con la solita convenzione 'comando:valore' e, nel caso il comando sia 'testo', il valore viene inserito all'interno di una textarea preposta. Bene, eseguiamo una prova di funzionamento (http://www.html.it/guide/esempi/html5/esempi/lezione_websockets/fiveboard/index.html): sincerandoci di aver lanciato il WebSocket Server navighiamo prima sulla pagina 'index.html' e creiamo un nuovo documento 'esempio1', quindi apriamo una nuova finestra e puntiamola all'indirizzo di 'viewer.html': alla richiesta del nome del documento inseriamo anche qui 'esempio1' in modo da legare il viewer alla fiveboard. Ora proviamo a digitare alcuni caratteri sulla prima finestra e noteremo che, attraverso il server, questi sono propagati in tempo reale alla seconda! Ovviamente il tutto funzionerebbe anche se la conversazione avvenisse tra due macchine distinte attraverso un server remoto; nell'immagine seguente si può notare una fiveboard aperta sull'iPad ed il suo corrispondente viewer visibile su di un portatile; in alto i messaggi di log del WebSocket Server (figura 3). Figura 3 (click per ingrandire)
  • 119.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_websocket/3.jpg) Conclusioni I WebSocket, sononella loro estrema semplicità, degli strumenti incredibilmente potenti; a riprova di questo fatto la rete ha già incominciato ad offrire interessanti prospettive di utilizzo nonostante l'innegabile giovinezza di queste API. Tra le soluzioni degne di nota merita sicuramente una citazione Pusher (http://pusherapp.com/), un servizio che offre la possibilità di gestire eventi real-time attraverso l'utilizzo di WebSocket. Un po' più artigianale, ma altrettanto valido è http://jsterm.com/ (http://jsterm.com/), un'applicazione che consente di collegarsi a server remoti utilizzando un proxy, scritto in node.js, tra il protocollo WebSocket e Telnet. Recentemente lo sviluppo delle API è stato frenato dalla scoperta di una seria vulnerabilità [documento PDF] (http://www.adambarth.com/experimental/websocket.pdf) legata al potenziale comportamento di un caching proxy che può essere indotto, attraverso l'utilizzo di WebSocket ad-hoc, a modificare il contenuto delle informazioni consegnate ad altri client. La vulnerabilità è stata correttamente risolta dalla versione 0.7 del protocollo ma sfortunatamente le versioni 4 e 5 di Firefox, rilasciate prima del fix, hanno i websocket disabilitati per default; la versione 6 del browser dovrebbe però ripristinare il funzionamento out-of-the-box di questa interessantissima feature. Tabella del supporto sui browser API e Web Applications WebSockets No 4.0+ (parziale) 5.0+ 7.0+ 11.0+ (parziale) Drag and Drop
  • 120.
    La soluzione alDrag and Drop proposta dalle specifiche HTML5 (http://dev.w3.org/html5/spec/editing.html#dnd) va ben oltre la metafora classica emulata in modo eccelso (http://jqueryui.com/demos/draggable/) da librerie come JQurey e similari. Certo, anche quel tipo di comportamento è possibile, ma stiamo solo raschiando la superficie di una feature ben più complessa, articolata e potente. Un assaggio più consistente delle reali capacità del Drag and Drop è offerto dalla versione HTML5 del famoso client di posta Gmail: trascinando un file al di sopra della pagina per la composizione di un nuovo messaggio si evidenza una zona verde, rilasciando il documento al di sopra di tale zona possiamo assistere all’upload dello stesso; ed il tutto funziona anche con più file contemporaneamente! Prima di passare ad una analisi delle specifiche è bene quindi capire che questo meccanismo non è stato studiato solo per riordinare liste o spostare oggetti in un carrello ma deve essere inteso come vero e proprio sistema di input sia per informazioni interne alla pagina sia esterne. Le specifiche Gli attori coinvolti in queste API sono essenzialmente tre, e non è necessario che siano tutti presenti ad ogni implementazione. Stiamo parlando dell’oggetto che subisce il drag, della struttura che contiene i dati che intendiamo trasferire attraverso il drop, e dell’oggetto che accetta il drop. Ecco come funziona una sessione completa di Drag and Drop: <ul id="elementi_da_trascinare" ondragstart="iniziaTrascinamento(event)"> <li draggable="true" data-icona="http://bit.ly/h5Khyt" data-valore="0.5">50 Cents</l i> <li draggable="true" data-icona="http://bit.ly/gnyFfh" data-valore="1" > 1 Euro </li > <li draggable="true" data-icona="http://bit.ly/eXq6y5" data-valore="2" > 2 Euro </li > <li draggable="true" data-icona="http://bit.ly/hiftBg" data-valore="10"> 10 Euro </l i> </ul> In primo luogo è necessario l’utilizzo dell’attributo globale draggable="true" per specificare gli elementi che vogliamo rendere trascinabili; utilizziamo inoltre i campi data-* per salvare informazioni che potranno tornare utili più tardi, come l’url dell’icona della moneta e il suo valore numerico. Il passo successivo consiste nel definire la funzione associata all’evento che notifica l’inizio di un trascinamento (ondragstart), nel nostro caso l’evento è stato associato all’ul, in quanto sottende l’intero elenco degli elementi con il comportamento drag abilitato. <script> iniziaTrascinamento = function(evento){ evento.dataTransfer.setData("text", event.target.dataset.valore); evento.dataTransfer.effectAllowed = 'copy'; icona = document.createElement('img'); icona.src = evento.target.dataset.icona; evento.dataTransfer.setDragImage(icona, -10, -10); } </script> Nell’argomento evento lo user-agent si premura di fornirci tutto il necessario per gestire il
  • 121.
    trascinamento dell’elemento. Nellaprima riga della funzione viene invocata la struttura dataTransfer, atta a contenere i dati che vogliamo siano disponibili all’oggetto incaricato del drop; con il metodo setData richiediamo poi l’inserimento di un nuovo valore all’interno di tale struttura. Stando alle specifiche potremmo identificare questo valore con un’etichetta (usando il primo argomento, ad esempio ‘valuta’), in questo modo saremmo poi in grado di istruire l’area di drop ad accettare solo il rilascio di elementi con tale etichetta; nella realtà questa funzionalità non è ancora supportata in modo sufficiente (http://code.google.com/p/chromium/issues/detail?id=31037) e funziona solo con etichette text o url. Come secondo argomento alla chiamata specifichiamo invece il contenuto del campo ‘data-valore’ dell’elemento che sta subendo il trascinamento. Nella riga successiva, con l’istruzione evento.effectAllowed = 'copy' segnaliamo allo user-agent che l’azione di drag è da intendersi come copia e non, ad esempio, come spostamento (in quel caso sarebbe stato 'move'). È importante notare che tale segnalazione non implica di per sé nessun cambiamento alla dinamica dello user-agent nei confronti di questo drag and drop (se non qualche accortezza grafica, come vedremo poi) ma ci da ancora una volta la possibilità di filtrare in fase di drop gli elementi che rilasciamo accettando, ad esempio, solamente azioni di tipo 'copy'. Le due righe di codice seguenti servono invece per creare una immagine avente come src il campo data-icona dell’elemento che stiamo trascinando; infine con l’ultima istruzione,evento.dataTransfer.setDragImage(icona, -10, -10), comunichiamo al dataTransfer di usare l’immagine appena generata come oggetto da visualizzare durante il trascinamento. Se proviamo ad includere i due frammenti di codice in una classica struttura HTML5 e ad eseguirli con Chromium avremo questo effetto (figura 1): Figura 1 Bene, ora creiamo l’oggetto di drop e gestiamo l’azione di rilascio: ... </ul> <img id="portamonete" dropzone="copy s:text" draggable="false" src="http://bit.ly/gZH4H5" ondragenter="ingressoInZonaDiDrop(event)" ondragleave="uscitaZonaDiDrop(event)" ondragover="trascinamentoInZonaDiDrop(event)"
  • 122.
    ondrop="rilascioDellOggettoTrascinato(event)" > <p> Il portamonete contiene: <output id="sommatoria">0</output> euro</p> Così come ogni elemento HTML può essere impostato come trascinabile grazie all’attributo draggable=true, anche il ruolo di area di drop può essere delegato a qualsiasi tag. In questo caso abbiamo scelto di utilizzare un’immagine di un portamonete nel quale riporremo i nostri risparmi. Diamo una scorsa agli attributi che abbiamo impostato: dropzone: con questo attributo, purtroppo ancora poco supportato, è possibile specificare in modo sintetico ed efficace le varie tipologie di dati per i quali l’area di drop è abilitata. I primi comandi, separati da uno spazio, indicano le tipologie di effectAllowed supportate (in questo caso solamente copy) mentre le restanti istruzioni, nel formato s:testo o f:testo rappresentano in caso di s:testole etichette supportate, o i mime-type supportati nel caso di f:testo. La particella s indica che il contenuto del dataTransfer deve essere di natura testuale (come nell’esempio che stiamo studiando), mentre la particella f implica che l’area di drop accetti solamente file, come ad esempio succede nel caso dell’aggiunta di allegati alla Gmail. Validi esempi di valorizzazione di questo attributo sono i seguenti: -- Accetta copy per file di tipo png e jpeg dropzone="copy f:image/png f:image/jpeg" -- Accetta move e copy per etichette "valuta" dropzone="move copy s:valuta" draggable: conosciamo già questo attributo; in ogni caso è importante segnalare che qui è stato utilizzato per informare lo user-agent di non rendere trascinabile questa immagine. Infatti nella maggioranza dei browser le immagini lo sono di default; ondragenter, ondragmove e ondragleave: questi tre eventi vengono invocati quando l’elemento viene trascinato all’interno di un’area di drop (ondragenter), mosso all’interno della stessa(ondragmove) e infine spostato nuovamente all’esterno dell’area (ondragleave); ondrop: l’evento chiave dell’intero processo, viene invocato al rilascio di un elemento al di sopra di un area di drop. Ottimo! Non ci resta che completare questa panoramica delle specifiche osservando una possibile implementazione degli eventi appena descritti: ... ingressoInZonaDiDrop = function(evento){ evento.target.src = "http://bit.ly/gRo6cc"; evento.preventDefault(); } uscitaZonaDiDrop = function(evento){ evento.target.src = "http://bit.ly/gZH4H5"; } trascinamentoInZonaDiDrop = function(evento){ evento.dataTransfer.dropEffect = 'copy';
  • 123.
    evento.preventDefault(); } rilascioDellOggettoTrascinato = function(evento){ sommatoria = document.getElementById("sommatoria"); sommatoria.innerHTML = parseFloat(sommatoria.innerHTML) + parseFloat (evento.dataTransfer.getData("text")); evento.target.src = "http://bit.ly/gZH4H5"; } ... L’istruzione che merita maggiore attenzione è sicuramente preventDefault() che, se posizionata all’interno di un evento, impedisce al browser di attivare il proprio comportamento di default. In questo caso la funzione in questione è stata utilizzata in ingressoInZonaDiDrop per impedire che il browser negasse l’accesso al drop (comportamento di default) ed in trascinamentoInZonaDiDrop per evitare l’animazione di ‘ritorno’ degli elementi draggati al loro rilascio. Per il resto le due funzioni associate a ondragenter e ondragleave si preoccupano solamente di alternare, tra chiusa e aperta, l’immagine del portamonete all’ingresso (o uscita) di un elemento trascinato al di sopra di esso. trascinamentoInZonaDiDrop deve invece preoccuparsi nuovamente di segnalare il supporto al solo effetto copy; questo accorgimento non sarebbe necessario con un pieno supporto dell’attributo dropzone. Infine la funzione legata all’evento ondrop contiene l’importante metodo getData(etichetta), indispensabile per recuperare dal dataTransfer l’ultimo oggetto inserito con l’etichetta richiesta. Eseguiamo il tutto in Chromium notando anche come l’icona del mouse si modifichi durante l’azione di drag and drop ad indicare la copia (figura 2): Figura 2 (click per ingrandire)
  • 124.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_drag/2.jpg) Ecco la demo(http://www.html.it/guide/esempi/html5/esempi/lezione_drag/demo.html). Un esempio Visto che non abbiamo ancora sperimento il drag and drop di file, utilizziamo il progetto guida per fare alcune prove; in particolare implementiamo la possibilità di rilasciare un file di testo al di sopra di una textarea per fare in modo che in questa vi si riversi il contenuto. Questo esempio ci darà anche l’occasione di toccare con mano la potenza delle nuove File API (http://www.w3.org/TR/FileAPI/), in particolare approfondiremo gli oggetti Javascript File, FileList e FileReader: ottimi compagni nel caso sia necessario accedere agli attributi o al contenuto di un file selezionato (o trascinato) per l’upload. Incominciamo modificando il markup del file index.html per attivare una drop area sulla textarea: <label>Cosa hai in mente? <textarea name="testo_da_ricordare" required autofocus placeholder="La lista della spesa, il teatro di questa sera ..." dropzone="copy f:text/plain" ondragover="consentiIlDrop(event)" ondrop="caricaIlContenutoTestuale(event,this)"> </textarea> </label>
  • 125.
    Fin qui nientedi strano, la parte divertente è tutta racchiusa nella definizione delle due funzioni. Vediamole insieme nel file 'application.js': consentiIlDrop = function(evento){ evento.dataTransfer.dropEffect = 'copy'; evento.preventDefault(); } caricaIlContenutoTestuale = function(evento,element){ var files = evento.dataTransfer.files; var reader = new FileReader(); reader.onload = function(evento){ element.value = element.value + evento.target.result; } for(file in files){ reader.readAsBinaryString(files[file]); } } La funzione caricaIlContenutoTestuale merita un approfondimento: il metodo files chiamato su dataTransfer ritorna un elenco di File racchiusi in un oggetto di tipo FileList, che per i nostri scopi è assimilabile ad un array. Successivamente, nella variabile reader, viene caricata un istanza di FileReader, una classe predisposta esclusivamente alla lettura del contenuto degli oggetti di tipo File. Nelle righe successive viene impostata l’azione associata all’evento onload di reader, che viene invocato al completamento dell’azione di lettura di un file; all’interno di questa azione il contenuto testuale del file (evento.target.result) è concatenato al testo già presente nella textarea. L’ultimo frammento di questa funzione cicla su tutti i file presenti in files; successivamente su ognuno di essi viene richiesta la lettura al reader attraverso il metodo readAsBinaryString. Anche per questo esempio abbiamo preparato una demo (http://www.html.it/guide/esempi/html5/esempi/lezione_drag/fiveboard/index.html). Funziona al momento solo su Chromium. Per il test basta creare un file di testo, salvarlo (magari sul desktop) e trascinarlo nella textarea. Tabella del supporto sui browser API e Web Applications Drag and Drop No 3.5+ 3.2+ 2.0+ No Geolocation API Le Geolocation Api non sono contenute all’interno dell’HTML5 ma fanno parte di quell’insieme di
  • 126.
    documenti (http://dev.w3.org/geo/api/spec-source.html) chegravitano attorno alle specifiche; la loro funzionalità è abbastanza semplice, definire una struttura dati atta a contenere vari dati geospaziali e impostare alcune funzioni di accesso a tali dati. Nessuna menzione viene fatta in merito ai meccanismi che il browser deve utilizzare per recuperare questi dati, ogni piattaforma è infatti tenuta ad utilizzare al meglio le informazioni provenienti dal device. Su di un dispositivo mobile di ultima generazione avremo quindi un set di coordinate provenienti dal sensore GPS, mentre su di un portatile potremo avvalerci del posizionamento legato all’ip della connessione internet. Per proteggere la privacy dell’utente è inoltre fatto divieto di utilizzo delle suddette informazioni senza preventivo consenso esplicito. Le specifiche Esistono praticamente solo due metodi a disposizione, fra l’altro entrambi utili allo stesso scopo: ottenere la posizione corrente. La differenza tra i due, getCurrentPosition e watchPosition, va ricercata nella loro periodicità, mentre il primo metodo fornisce il dato una sola volta, il secondo si attiva automaticamente ogniqualvolta la posizione cambi, ad esempio perché l’utente sta passeggiando. La sintassi per invocare questi metodi è la seguente: navigator.geolocation.getCurrentPosition(inCasoDiSuccesso, opzInCasoDiErrore, opzioni) ; navigator.geolocation.watchPosition(inCasoDiSuccesso, opzInCasoDiErrore, opzioni); Il primo argomento contiene i riferimenti ad una funzione da eseguire al corretto recupero delle informazioni geospaziali, il secondo argomento, opzionale, punta invece ad una funzione che verrà invocata in caso di errore. L’ultimo parametro, anch’esso facoltativo, può essere utilizzato per specificare alcune opzioni utilizzando una struttura {opzione1: valore1, ...}. Le opzioni disponibili sono 3: enableHighAccuracy (true/false): questo flag può essere utilizzato per notificare allo user- agent la necessità o meno di ottenere dati il più accurati possibile. L’opzione è stata introdotta in quanto il recupero di informazioni più dettagliate richiede di solito maggiore dispendio di tempo ed energia e, in particolare su device mobile, potrebbe risultare fastidiosa per l’utente; timeout (millisecondi): l’opzione rappresenta il tempo massimo concesso al browser per recuperare la posizione dell’utente. In caso di fallimento verrà invocata la funzione associata al secondo argomento; maximuAge (millisecondi): indica al browser di effettuare una ricerca preventiva nella cache di un dato geospaziale non più vecchio dei millisecondi specificati. Se disponibile tale dato verrà restituito come posizione corrente, altrimenti verrà eseguita la procedura classica. La funzione invocata in caso di successo deve accettare un singolo argomento, un oggetto di tipo Position che contiene tutte le informazioni recuperate così come un timestamp delle data e ora di recupero. inCasoDiSuccesso = function(position){ alert( "Posizione delle: " + position.timestamp.getHours() + ":" + position.timestamp.getMinutes() + "n" + "Accuratezza delle coordinate: " + position.coords.accuracy + " mt; n" + "Latitudine: " + position.coords.latitude + " gradi; n" + "Longitudine: " + position.coords.longitude + "gradi; n" +
  • 127.
    "Accuratezza dell'altezza: "+ position.coords.altitudeAccuracy + " mt; n" + "Altezza: " + position.coords.altitude + " mt; n" + "Direzione: " + position.coords.heading + " gradin " + "(0 = Nord, 90 = Ovest, 180 = Sud, 270 = Est);n" + "Velocita: " + position.coords.speed + " m/s;" ); } Nel frammento di codice appena illustrato possiamo vedere tutte le informazioni estraibili dalla struttura Position; chiaramente, a seconda del device sul quale viene effettuata l’interrogazione, non tutte saranno sempre disponibili; in tal caso il loro valore sarà impostato a null. Nell’immagine seguente il risultato dell’interrogazione eseguita dal mio portatile domestico (notate l’accuratezza!): Figura 1 - (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_geolocation/ishot-437.png) In caso invece si verifichi un errore la funzione preposta deve accettare anch’essa un parametro, un oggetto di tipo PositionError contenente un codice di errore ed un messaggio ad uso di debug, rispettivamente nei metodi code e message. opzInCasoDiErrore = function(error){ alert( "Errore " + error.code + ": " + error.message); } In ultimo ricordiamo che un invocazione del metodo watchPosition può essere successivamente interrotta utilizzando la funzione clearWatch, come nell’esempio seguente: <!doctype html> <html> <head> <title> Un esempio di watchPosition</title> <script>
  • 128.
    var id_watch =null; inCasoDiSuccesso = function(position){ document.getElementById("posizione_corrente").insertAdjacentHTML('beforeend', "<li> Lat: " + position.coords.latitude + ", Lon: " + position.coords.longitude + ); "</li>" ); } sospendiLaRicezione = function(){ navigator.geolocation.clearWatch(id_watch); } window.onload = function(){ id_watch = navigator.geolocation.watchPosition(inCasoDiSuccesso); } </script> </head> <body> <h1>La tua posizione attuale</h1> <menu type="toolbar"> <button type="button" onclick="sospendiLaRicezione()"> sospendi la ricezione di dati geospaziali </button> </menu> <ul id="posizione_corrente"> </ul> </body> </html> Sono disponibili le demo per il primo (http://www.html.it/guide/esempi/html5/esempi/lezione_geolocation/geolocation.html) e per il secondo esempio (http://www.html.it/guide/esempi/html5/esempi/lezione_geolocation/geolocation_watchposition.html ). Conclusioni Come avete potuto notare questo set di API è contemporaneamente facilissimo da utilizzare ed estremamente potente. Le possibilità offerte si dilatano enormemente se ci si concentra sul mercato mobile dove la stragrande maggioranza dei device di ultima generazione è equipaggiata con ottimi sensori GPS e bussole. Tabella del supporto sui browser API e Web Applications Geolocation No 3.6+ 5.0+ 5.0+ 10.6+
  • 129.
    Canvas Una tela trasparente Ilcanvas è un elemento HTML5 rappresentato dal tag <canvas>. Può essere inteso come il corrispettivo digitale di una tela trasparente: uno spazio all'interno di una pagina web sul quale insistere con specifiche API (http://www.whatwg.org/specs/web-apps/current-work/multipage/the- canvas-element.html#the-canvas-element) adatte a tracciare linee, cerchi, rettangoli, immagini e altro ancora. Il canvas è, in estrema sintesi, una grande matrice di pixel, ognuno dei quali modificabile singolarmente nelle sue quattro componenti RGBA, rosso, verde, blu e alpha, la trasparenza. Tutti siamo a conoscenza di questo elemento HTML5 per una sua semplice attitudine: la possibilità di replicare i comportamenti fino ad oggi appannaggio della tecnologia Flash. In questo articolo esploreremo le numerose API disponibili e ne sperimenteremo una buona parte per poi concentrarci successivamente su di una interessante evoluzione del progetto guida. Le specifiche Prima di addentrarci nella giungla dei vari metodi a disposizione per disegnare sul canvas vediamo brevemente l'architettura che contraddistingue il markup dell'elemento: <canvas width="200px" height="200px" id="demo_canvas"> Contenuto da mostrare in caso il canvas non sia supportato. (quindi anche informazioni per i motori di ricerca). </canvas> Gli unici due attributi disponibili, oltre ai canonici globali, sono width e height, rispettivamente larghezza e altezza dell'elemento. Sotto il profilo JavaScript i metodi disponibili direttamente attraverso questo elemento sono due: toDataUrl e getContext. Il primo, toDataUrl, è fantastico nella sua semplicità: ritorna infatti il contenuto del canvas secondo il protocollo 'data URL' (http://tools.ietf.org/html/rfc2397). Questo schema memorizza file di ogni tipo in lunghissime sequenze di caratteri, che poi possono essere salvati su disco o, se come nel caso del canvas siano immagini, essere utilizzate come attributo src di tag img. Nei prossimi esempi vedremo un utilizzo tangibile di questo metodo, per ora invece concentriamoci su getContext, vera e propria 'porta' verso le API di disegno. getContext, quando invocato, ritorna un oggetto detto contesto del disegno: var canvas = document.getElementById("demo_canvas"); var contesto = canvas.getContext("2d"); In questa lezione esploreremo solamente i metodi legati al contesto di disegno '2d', come specificato dall'argomento utilizzato nello snippet, ma sappiate che esiste già una versione sperimentale di contesto '3d' che sottende un set di API completamente diverso, basato sulle OpenGL ES 2.0 e detto WebGL. Ma torniamo al contesto '2d': possiamo dividere le funzionalità in esso racchiuse in categorie: path, modificatori, immagine, testo e pixel per pixel; nelle prossime sezioni affronteremo in dettaglio ognuno di questi aspetti. Prima di proseguire è però importante evidenziare che il contesto è un oggetto stateful, che mantiene cioè al suo interno dei valori che influiscono sulle operazioni
  • 130.
    successive; aspettiamoci quindidelle direttive di questo tipo: imposta colore di linea verde; imposta percorso della linea da (0,0) a (10,10); traccia tutti i percorsi di linea. In questo caso, se avessimo usato le corrette API, avremmo ottenuto una linea verde tra i punti (0,0) e (10,10). L'insieme dei valori memorizzati in un contesto viene detto drawing state. Path I metodi di questa categoria sono tutti funzionali al disegno di frammenti geometrici come linee, archi e quant'altro: ecco un esempio omnicomprensivo: disegnaBarchetta = function(contesto){ contesto.beginPath(); // sposta il cursore del path nella posizione 40,170 contesto.moveTo(40,170); // crea un subpath linea fino alla posizione 260,170 contesto.lineTo(260,170); // crea un subpath arco che sia tangente alle due linee (260,170 - 150,250) // (150,250 - 40,170) con diametro 150 contesto.arcTo(150,250,40,170,150); contesto.lineTo(40,170); contesto.moveTo(150,170); contesto.lineTo(150,40); contesto.rect(150,40,60,30); // disegna tutti i subpath del path corrente sul canvas usando la // configurazione del drawing state contesto.stroke(); // riempi tutti le aree inscritte dal path corrente usanto la configurazione // del deawing state contesto.fill(); } Il concetto principe è il path, ovverosia un insieme di punti uniti fra loro da definite primitive geometriche (arco, linea,...), dette subpath. Ogni contesto possiede solamente un path attivo per volta. Per aggiungere un subpath al path attivo si possono utilizzare metodi come lineTo, arcTo, rect e altri. Metodi come stroke e fill vengono applicati a tutti i subpath del path attivo. Per resettare il path attivo, rimuovendo quindi tutti i subpath non ancora disegnati, si utilizza l'istruzione beginPath(). Eseguiamo l'esempio appena mostrato per ottenere il risultato seguente (figura 1): Figura 1
  • 131.
    Oltre ai subpathmostrati ce ne sono altri, ma il meccanismo di generazione resta sempre lo stesso, la lista completa è disponibile nel documento ufficiale delle specifiche (http://dev.w3.org/html5/2dcontext/#complex-shapes-paths). Modificatori Le funzioni all'interno di questo gruppo insistono sul drawing state di un canvas, modificando il modo in cui i path, le immagini ed i testi vengono disegnati. Nella categoria si annoverano matrici di trasformazione (scale, rotate, translate), gestione della trasparenza globale e delle funzioni di composizione nonché impostazioni di colore, di linea e di gradiente. Suona complicato ? Facciamo un po' di prove: contesto.scale(0.5,0.5); contesto.rotate(0.5); contesto.translate(300,-100); disegnaBarchetta(contesto); Ecco il risultato, i modificatori impostati vengono applicati alla barchetta (figura 2): Figura 2
  • 132.
    Utilizziamo ora trasparenzae composizione: contesto.globalAlpha = 0.5; disegnaBarchetta(contesto); contesto.translate(40,-0); contesto.globalCompositeOperation = "source-out"; disegnaBarchetta(contesto); L'attributo globalAlpha definisce (da 0.0 a 1.0) il livello di trasparenza che dovrà essere applicato a quanto verrà disegnato sul canvas. globalCompositeOperation ci permette invece di specificare in che modo vogliamo che le immagini vengano sovrapposte: è possibile utilizzare un set di parole chiave per indicare un ben preciso comportamento; in questo caso con source-out chiediamo allo user agent di effettuare una somma tra le trasparenze ottenendo un risultato come questo (figura 3): Figura 3
  • 133.
    Attenzione, al momentonon tutti i browser reagiscono allo stesso modo alle definizioni legate a GlobalCompositeOperation. Infine esaminiamo anche le operazioni legate ai modificatori di colore: contesto.strokeStyle = "#FF0000"; contesto.lineWidth = "5"; var gradiente = contesto.createRadialGradient(150, 150, 30, 150, 160, 100); gradiente.addColorStop(0.5, '#0000FF'); gradiente.addColorStop(0.1, '#000000'); contesto.fillStyle = gradiente; disegnaBarchetta(contesto); Con strokeStyle e fillStyle possiamo impostare le nostre preferenze su come intendiamo debba essere disegnata la linea di contorno degli oggetti (stroke) ed il loro riempimento (fill). Entrambi gli attributi possono essere valorizzati con stringhe, oggetti di tipo gradient ed oggetti di tipo pattern. In questo caso è mostrato l'utilizzo di una semplice stringa di colore rosso per lo stroke e di un gradient radiale con una variazione di colore dal nero al blu per il fill. L'utilizzo dell'oggetto pattern è simile, il metodo da utilizzare è contesto.createPattern(immagine, opzionaleRipetizione) dove il secondo argomento riprende le scelte effettuabili via CSS (no-repeat, repeat-x, ecc..) mentre il primo può essere valorizzato con con un elemento img, un altro canvas o perfino un elemento video. Ecco il risultato che si ottiene utilizzando il codice visto (figura 4): Figura 4
  • 134.
    Prima di passareal prossimo gruppo di funzioni citiamo brevemente anche la possibilità di implementare un ombreggiatura, ecco le istruzioni: contesto.shadowColor = '#003300' contesto.shadowOffsetX = 10; contesto.shadowOffsetY = 10; contesto.shadowBlur = 30; disegnaBarchetta(contesto); Ed il risultato (figura 5): Figura 5
  • 135.
    Immagine L'unico metodo appartenentea questo gruppo si chiama drawImage ed è disponibile in un certo numero di varianti; il primo argomento può infatti essere sia un elemento immagine, sia un altro canvas, sia un elemento di tipo video. Il risultato dell'operazione è sempre quello di disegnare sul contesto invocante l'immagine prescelta, rispettando alcuni parametri dimensionali opzionali. Ecco un esempio: var immagine = new Image(); immagine.src = "http://img227.imageshack.us/img227/7092/marei.jpg"; contesto.drawImage(immagine,0,0); disegnaBarchetta(contesto); E il risultato (figura 6): Figura 6
  • 136.
    In questo casochiediamo che l'immagine sia disegnata a partire dall'angolo in alto a sinistra del canvas (coordinate 0,0); possiamo però anche specificare una dimensione di destinazione e perfino un rettangolo di partenza in modo da prelevare solo una porzione di immagine utilizzando i numerosi argomenti opzionali messi a disposizione dalle specifiche. Testo Come il nome del gruppo suggerisce, questi metodi permettono di scrivere porzioni di testo all'interno di un canvas, eccone un esempio: disegnaBarchetta(contesto); contesto.globalCompositeOperation = "source-out"; contesto.font = "34px Georgia" contesto.strokeText("Va va va barchetta", 20, 100); contesto.fillText("va, naviga naviga naviga", 20, 140); contesto.fillText("naviga e mai si", 20, 180); contesto.fillText("fermera'...", 20, 220); L'attributo font può essere valorizzato allo stesso modo del suo omonimo su CSS, per il resto fillText provvede a scrivere il testo desiderato partendo dalla posizione x, y specificata come secondo e terzo argomento; anche strokeText si comporta in modo simile ma con una piccola variante che si può facilmente evincere dal risultato dell'esecuzione dell'esempio. Da notare inoltre il comportamento interessante dell'applicazione di un particolare tipo di composizione (figura 7): Figura 7
  • 137.
    Pixel per Pixel Comegià anticipato in precedenza, il canvas non è nient'altro che una matrice di valori in formato RGBA: non stupisce che sia quindi possibile insistere su ogni singolo e specifico pixel della stessa; ecco un esempio: disegnaBarchetta(contesto); var dati_immagine = contesto.getImageData(0,0, contesto.canvas.width, contesto.canvas.height); var array_dati_immagine = dati_immagine.data; for(var i = 0; i < array_dati_immagine.length; i +=4 ){ array_dati_immagine[i ] = Math.round(Math.random() * 255); array_dati_immagine[i+1] = Math.round(Math.random() * 255); array_dati_immagine[i+2] = Math.round(Math.random() * 255); } dati_immagine.data = array_dati_immagine; contesto.canvas.width = contesto.canvas.width; contesto.putImageData(dati_immagine, 0,0); Il fulcro dello snippet è da ricercarsi nelle due funzioni getImageData e putImageData che rispettivamente prelevano e 'stampano' sul canvas una porzione di immagine delineata dagli argomenti passati. Tale porzione, un oggetto di classe ImageData, possiede in una sua proprietà, data, un lunghissimo array contenente le componenti RGBA di ogni pixel. Il ciclo for presentato nel codice precedente insiste proprio su questo array assegnando ad ognuna delle tre componenti RGB un valore randomico. Ecco il risultato (figura 8): Figura 8
  • 138.
    Per una verificadiretta di quanto visto nel corso della lezione è disponibile questa demo (http://www.html.it/guide/esempi/html5/esempi/lezione_canvas/index.html). Ora che abbiamo almeno un'idea delle potenzialità dello strumento che stiamo analizzando, accingiamoci ad una sperimentazione pratica attraverso il progetto guida. Lo vedremo nella prossima lezione. Tabella del supporto sui browser Grafica e Multimedia <canvas> 9.0+ 2.0+ 3.1+ 2.0+ 10.0+ Canvas: un esempio pratico Un esempio Ora che abbiamo almeno un'idea delle potenzialità dello strumento che stiamo analizzando, accingiamoci ad una sperimentazione pratica attraverso il progetto guida. Quello che faremo sarà dare la possibilità agli utenti della fiveboard di disegnare utilizzando un canvas, lo stesso verrà poi memorizzato insieme al testo sul già noto localStorage. Introduciamo anche in questo contesto il concetto diEventoCustom, la possibilità cioè di lanciare arbitrariamente eventi in tutto e per tutto simili a quelli utilizzati dal DOM. Questa feature ci sarà utilissima per mantenere un codice elegante e contemporaneamente sincronizzare salvataggio e recupero di testo e canvas dal localStorage; aggiungiamo ad application.js il seguente metodo:
  • 139.
    lanciaEvento = function(nome_evento){ var evento = document.createEvent("CustomEvent"); evento.initCustomEvent(nome_evento, true, true, titolo_fiveboard); document.dispatchEvent(evento); } Eliminiamo successivamente dallo stesso file le funzioni salvaIlDato e recuperaIlDato sostituendole con le seguenti: salvaAppunti = function(evento){ localStorage.setItem("fb_" + evento.detail, document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value ); alert("Appunti salvati"); } recuperaAppunti = function(evento){ document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value = localStorage.getItem("fb_" + evento.detail); alert("Appunti recuperati"); } document.addEventListener('salvadato', salvaAppunti); document.addEventListener('recuperadato', recuperaAppunti); Bene, ora abbiamo creato le due funzioni e le abbiamo registrate rispettivamente agli eventi custom ‘salvadato’ e ‘recuperadato’; ora non ci resta che scatenare questi eventi al click dei bottoni del file index.html, modifichiamo quindi come segue: ... <menu type="toolbar"> <button type="button" onclick="lanciaEvento('salvadato');"> Memorizza quanto scritto </button> <button type="button" onclick="lanciaEvento('recuperadato');"> Recupera l'ultimo testo memorizzato </button> </menu> ... Ottimo! Ora possiamo aggiungere un canvas poco prima della fine della pagina e procedere con il nostro esperimento: ... <canvas id="drawboard"></canvas> </form> ...
  • 140.
    In un filechiamato canvas.js, sempre nella cartella js, scriviamo: var ctx = null; var started = false; iniziaDisegno = function(evento){ ctx.beginPath(); ctx.moveTo(evento.offsetX,evento.offsetY); started = true; } disegna = function(evento){ if(started){ ctx.lineTo(evento.offsetX,evento.offsetY); ctx.stroke(); } } fermaDisegno = function(evento){ ctx.closePath(); started = false; } salvaCanvas = function(evento){ localStorage.setItem("canvas_fb_" + evento.detail, ctx.canvas.toDataURL('image/png') ); alert("Canvas salvato"); } recuperaCanvas = function(evento){ var immagine_salvata = localStorage.getItem("canvas_fb_" + evento.detail); if(immagine_salvata == null) return; var img = new Image(); img.src = immagine_salvata; ctx.canvas.width = ctx.canvas.width; ctx.drawImage(img, 0, 0); alert("Canvas recuperato"); } attivaIlCanvas = function(evento){ ctx = document.querySelector('canvas').getContext('2d'); ctx.canvas.addEventListener('mousedown' , iniziaDisegno,false ); ctx.canvas.addEventListener('mousemove' , disegna ,false ); ctx.canvas.addEventListener('mouseup' , fermaDisegno ,false ); ctx.canvas.addEventListener('mouseleave', fermaDisegno ,false ); document.addEventListener('salvadato', salvaCanvas ); document.addEventListener('recuperadato', recuperaCanvas ); }
  • 141.
    window.addEventListener('load' ,attivaIlCanvas,false); Approfondiamo unpo’ quanto scritto: la funzione appena qui sopra ‘attivaIlCanvas’ viene eseguita al load della pagina e si preoccupa di registrare tutti gli handler necessari alla gestione delle funzioni di disegno e di registrazione. In particolare iniziaDisegno, disegna e fermaDisegno utilizzano alcune funzioni che abbiamo esplorato nelle specifiche per disegnare delle spezzate che seguono la posizione del mouse. salvaCanvas e recuperaCanvas sono invece funzioni che vengono attivate dai nostri eventi custom; in questo caso la parte interessante è da ricercarsi nell’utilizzo del metodo toDataUrl che trasforma il contenuto del canvas in una stringa, rendendolo quindi memorizzabile all’interno dell’hash localStorage. In recuperaCanvas poi, la stessa stringa contenente l’immagine viene utilizzata come attributo src di un oggetto Image e successivamente ridipinta sul canvas. Ricordiamoci di richiedere il file canvas.js all’interno della pagina index.html e proviamo la nostra creazione (http://www.html.it/guide/esempi/html5/esempi/lezione_canvas/fiveboard/index.html) (figura 1): Figura 1 Conclusioni Il canvas è uno strumento incredibilmente vasto, potente ed articolato. La panoramica offerta da questa lezione introduce in modo abbastanza esauriente l’insieme delle tematiche anche se per non dilungarsi troppo non ne approfondisce nessuna. Nonostante questa evidente ricchezza il canvas resta comunque uno strumento di basso livello e permane in uno stato di difficile utilizzo a meno che non sia supportato dai dovuti framework, come ad esempio processing.js (http://processingjs.org/), canto.js (http://www.davidflanagan.com/2010/07/cantojs-an-impr.html) e l’ottimo Easel.js
  • 142.
    (http://easeljs.com/). In questalezione si è inoltre volutamente ignorato il tema dell’interazione tra canvas e video, che verrà affrontato nel dovuto dettaglio nella prossima lezione di questa guida. Video L'introduzione della possibilità di riprodurre filmati in un browser senza l'ausilio di plug-in di terze parti rappresenta di per sé una grossa novità: vanno ad esaurirsi infatti tutte le tematiche legate alla proliferazione del plug-in utilizzato ed alla portabilità dello stesso (basti pensare ai mille grattacapi generati dal connubio Flash - iOs). Ma c'è molto di più! Portando il player video all'interno del DOM si sono rese disponibili tutta una serie di interessantissime possibilità di interazione: ad esempio il tag video può essere modificato utilizzando i CSS; lo stesso effetto può essere poi ovviamente applicato anche ai pulsanti di controllo. Inoltre l'elemento espone una ricca interfaccia Javascript che ci consente di effettuare senza alcune fatica moltissime efficaci manipolazioni. Infine ricordiamo che il canvas, trattato nella lezione precedente, ha la facoltà di recuperare fotogrammi da un elemento video e di manipolarli; più avanti mostreremo questa potente tecnica in azione. Le specifiche Prima di iniziare la sperimentazione è necessario procurarsi dei video da utilizzare: per queste demo abbiamo scelto il trailer del cartone 'Big Buck Bunny', distribuito in licenza Creative Commons e disponibile per il download all'indirizzo http://www.webmfiles.org/demo-files/ (http://www.webmfiles.org/demo-files/) e dal sito ufficiale (http://www.bigbuckbunny.org/index.php/trailer-page/); l'intero esempio e tutti i file necessari sono allegati al pacchetto zip (http://www.html.it/guide/esempi/html5/esempi/esempi_html5_api.zip) che contiene tutti gli esempi della guida. Incominciamo dal markup, ecco un esempio di implementazione: <video poster="big_buck_bunny/poster.jpg" controls> <source src="big_buck_bunny/trailer.mp4" type="video/mp4" > <source src="big_buck_bunny/trailer.ogg" type="video/ogg" > <source src="big_buck_bunny/trailer.webm" type="video/webm"> </video> L'attributo poster ci consente di specificare una immagine che verrà utilizzata all'interno dell'area di riproduzione prima che il video venga eseguito; controls invece indica che richiediamo la presenza dei classici controlli, come play, pausa, volume, barra di avanzamento e quant'altro. Ci sono altri attributi utilizzabili a livello di video, vediamoli: autoplay: se presente questa stringa di testo indica allo user agent di provvedere alla riproduzione del video appena si raggiungono le condizioni minime di buffer necessarie; loop: se il browser rileva questa stringa inizierà nuovamente la riproduzione del filmato una volta che questo è giunto al termine; preload: per questo attributo sono previste tre opzioni: none, metadata e auto. Con none si indica allo user-agent di non effettuare nessun download preventivo del contenuto del file video, il buffer inizierà quindi ad essere riempito alla prima pressione del tasto play; con metadata invece si richiede che vengano recuperate almeno le informazioni essenziali,
  • 143.
    come la duratadel filmato e le sue dimensioni. Infine con auto, che è anche la valorizzazione di default, si lascia al browser totale libertà di scelta; audio: possiamo valorizzare questo attributo a 'muted' ottenendo in questo modo che il player video non riproduca nessun suono. Nell'ultima versione delle specfiche la proprietà è stata sostituita da un più consono muted che deve essere valorizzato a true Passiamo a source e al grande dilemma che questi elementi sottendono: i codec. Ogni elemento di questo tipo infatti indica al browser la disponibilità dello stesso contenuto video in formati di codifica diversi. Ad oggi non esiste un singolo codec che sia supportato dai maggiori browser in commercio: la scelta più compatibile in assoluto è il nuovissimo, e open-source, formato .webm/ vp8, che però non è ben accetto da Safari (e quindi da iPhone et-simila), per il quale la scelta obbligata è invece il classico .mp4 con codifica h264. Per ogni source è necessario quindi specificare un attributo src, un type ed opzionalmente anche un codec, da valorizzare con una lista di stringhe contenente il dettaglio della codifica audio e video, separati dalla virgola. Eseguendo il primo esempio otterremo questo risultato (figura 1): Figura 1 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_video/1.png) Ecco la demo (http://www.html.it/guide/esempi/html5/esempi/lezione_video/video.html). Ma non è finita. Insieme agli elementi di tipo source possiamo anche specificare tag track che rappresentano vari elementi testuali che si possono combinare con il video in modi diversi. Sono un esempio di track sottotitoli, tracce, che contengono testo ed onomatopee di effetti speciali, e sono di solito utilizzate dai non udenti, descrizioni, che sono invece composte da testo predisposto per la sintesi audio da parte di sintetizzatori vocali, di solito utilizzati da persone non vedenti, capitoli ed altri dati: <video poster="big_buck_bunny/poster.jpg" controls> <source src="big_buck_bunny/trailer.mp4" type="video/mp4" > <source src="big_buck_bunny/trailer.ogg" type="video/ogg" > <source src="big_buck_bunny/trailer.webm" type="video/webm"> <track kind="subtitles" src="big_buck_bunny/bunny.it.vtt" srclang="it" label="Sottotitoli in Italiano"> </video> Le specifiche legate all'elemento in questione non sono però, ad oggi, supportate da nessuno dei principali browser in commercio; la stessa estensione di file (.vtt) proposta dal W3C come standard per i sottotitoli è praticamente sconosciuta e priva di documentazione. Le API Parallelamente al markup sono state introdotte tutta una serie di API utilissime per interfacciarsi con oggetti video. Iniziamo dai più semplici, intuitivi ed utili: <!doctype html> <html> <head> <title>Big Buck Bunny, il trailer</title> <script> var video;
  • 144.
    eseguiIlPlay = function(){ video.play(); } eseguiIlPause = function(){ video.pause(); } cambiaVolume = function(evento){ video.volume = evento.target.value; } window.addEventListener('load',function(e){ video = document.querySelector('video'); }) </script> </head> <body> <menu type="toolbar"> <button type="button" onclick="eseguiIlPlay()" > Play </button> <button type="button" onclick="eseguiIlPause()"> Pause </button> <label> Volume: </label> <input type="range" min="0.0" max="1.0" step="0.1" value="0.5" oninput="cambiaVolume(event)"> </menu> <video poster="big_buck_bunny/poster.jpg"> <source src="big_buck_bunny/trailer.mp4" type="video/mp4" > <source src="big_buck_bunny/trailer.ogg" type="video/ogg" > <source src="big_buck_bunny/trailer.webm" type="video/webm"> </video> </body> </html> Come potete notare, il controllo base del flusso e del volume è alquanto semplice, esistono però alcune funzioni più particolari; ad esempio la proprietà playbackRate di un oggetto video può essere valorizzata con cifre che indicano quale vogliamo che sia la velocità di riproduzione. Interessanti sono anche i metodi che gravitano attorno alle tematiche temporali: duration, currentTime e initialTime ritornano rispettivamente la durata in secondi del filmato, la posizione corrente e la posizione dalla quale è partita la riproduzione (tipicamente 0). Il metodo currentTime può essere inoltre valorizzato causando uno spostamento della posizione del video al secondo richiesto. Ecco due funzioni che mostrano rispettivamente come raddoppiare la velocità di riproduzione e modificare la posizione corrente esattamente a metà della lunghezza del video: doppiaVelocita = function(){ video.playbackRate = 2.0; } posizionatiAlCentro = function(){ video.currentTime = Math.round(video.duration / 2); } Per attivarle aggiungiamo due pulsanti nella toolbar: <menu type="toolbar">
  • 145.
    ... <buttontype="button" onclick="doppiaVelocita()"> x2 </button> <button type="button" onclick="posizionatiAlCentro()"> Centro </button> ... </menu> Eseguiamo il codice finora prodotto per testare la bontà di quanto sviluppato (figura 2): Figura 2 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_video/2.png) L'ultima parte del capitolo riguardante le API di questo elemento è dedicata ad una classe interessante denominata TimeRanges e composta da un attributo: length e due metodi start(n) e end(n). Alcuni metodi dell'elemento video ritornano oggetti di questo tipo che contengono informazioni in merito a collezioni di intervalli temporali; ecco un esempio (aggiungiamolo alla demo precedente): generaTesto = function(intervalli){ var testo = "Ci sono: " + intervalli.length + " intervalli"; for(var i=0; i < intervalli.length; i++){ testo +="ntda: " + intervalli.start(i) + " a:" + intervalli.end(i) + " secondi" } return testo; } ottieniInformazioni = function(){ var caricato = video.buffered; var riprodotto = video.played; var ricercabile = video.seekable;
  • 146.
    alert( "Alcune informazionisul video n" + "= Caricato =n" + generaTesto(caricato) + "n" + "= Riprodotto =n" + generaTesto(riprodotto) + "n" + "= Ricercabile =n" + generaTesto(ricercabile) ); } Creiamo il bottone corrispondente, lanciamo la demo e sinceriamoci dell'effettivo funzionamento: <button type="button" onclick="ottieniInformazioni()"> Info </button> Nello screenshot in figura 3 possiamo verificare il risultato: Figura 3 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_video/3.png) Anche in questo caso è disponibile una demo (http://www.html.it/guide/esempi/html5/esempi/lezione_video/video_api_1.html). Gli eventi L'elemento video comunica attraverso un elenco lunghissimo di eventi, i principali sono facili e facilmente comprensibili, come ad esempio play, playing, pause e waiting; per la lista completa rimandiamo alle specifiche W3C (http://dev.w3.org/html5/spec/Overview.html#mediaevents). MediaController API Solo un piccolo accenno ad un set di API di recentissima pubblicazione; grazie alle MediaController API e con l'ausilio di un nuovo attibuto mediagroup, disponibile sia per elementi audio che video, è
  • 147.
    possibile chiedere allouser agent di gestire la riproduzione di più oggetti multimediali in modo sincronizzato. Facciamo un esempio: <video poster="big_buck_bunny/poster.jpg" controls mediagroup="anteprime"> <source src="big_buck_bunny/trailer.mp4" type="video/mp4" > <source src="big_buck_bunny/trailer.ogg" type="video/ogg" > <source src="big_buck_bunny/trailer.webm" type="video/webm"> </video> <video poster="big_buck_bunny/poster.jpg" controls mediagroup="anteprime"> <source src="mixer/movies/src/ballo_green_screen.mp4" type="video/mp4" > <source src="mixer/movies/src/ballo_green_screen.ogg" type="video/ogg" > <source src="mixer/movies/src/ballo_green_screen.webm" type="video/webm"> </video> in questo caso, avendo lo stesso valore per l'attributo mediagroup, entrambi i video vengono associati allo stesso oggetto di tipo MediaController generato a runtime dal browser. Questo oggetto diventa quindi responsabile della gestione temporale dei due filmati e ne coordina la riproduzione in sincrono. Per accedere a questo oggetto la sintassi è estremamente semplice: // ritorna il MediaController associato al video, se presente document.querySelector("video").controller così come semplici sono i metodi e gli eventi disponibili in quanto coincidono quasi completamente con quelli dei MediaElements (es: play, pause, volume e tutto quanto visto in questa lezione). Purtroppo nessun browser ad oggi implementa questo promettente set di API ma, visto l'interesse sul tema, l'attesa non dovrebbe rivelarsi troppo lunga. Video e Canvas: un esempio pratico In questa lezione vedremo come manipolare flussi video in tempo reale con l'ausilio di un canvas. Perché il tutto funzioni è necessario che sia il video che la pagina che costruiremo risiedano sullo stesso dominio. Questo per prevenire l'intervento di alcune policy di sicurezza del browser che impediscono di fatto la modifica di materiale multimediale che non provenga dalla stessa origine della pagina. Ecco uno schema di quanto realizzeremo: Figura 4 (click per ingrandire)
  • 148.
    (http://www.html.it/guide/esempi/html5/imgs/lezione_video/4.png) Utilizzeremo due canvas.Nel primo replicheremo i fotogrammi del video, mentre nel secondo andremo a inserire il risultato della nostra elaborazione sull'array di pixel estratti dal primo. Il procedimento sarà quindi il seguente: Avviare la riproduzione del video; Utilizzare la funzione drawImage per disegnare sul primo canvas il contenuto del fotogramma corrente del video; Con la funzione getImageData recuperare dal canvas il fotogramma precedentemente disegnato, questa volta però come array di pixel modificabili; Effettuare un ciclo su tutti i pixel recuperati apportando le modifiche volute; Utilizzare la funzione putImageData per disegnare l'array di pixel modificato sul secondo canvas; Ripetere dal secondo punto la procedura. Ecco il codice necessario: <!doctype html> <html> <head> <title> Canvas e Video </title> <script> decomposizioneColori = function(video, context, context_nascosto, colori){ if(video.paused || video.ended) return false; context_nascosto.drawImage(video,0,0); var fotogramma = context_nascosto.getImageData(0,0, context_nascosto.canvas.width,context_nascosto.canvas.height); var fotogramma_data = fotogramma.data; var rosso = colori.rosso.checked; var verde = colori.verde.checked; var blu = colori.blu.checked;
  • 149.
    for(var i=0; i< fotogramma_data.length; i+=4){ if (!rosso) fotogramma_data[i ] = 0; if (!verde) fotogramma_data[i+1] = 0; if (!blu ) fotogramma_data[i+2] = 0; } fotogramma.data = fotogramma_data; context.putImageData(fotogramma,0,0); setTimeout(function(){ decomposizioneColori(video, context, context_nascosto, colori) },0); } aspettaIlPlay = function(evento){ var video = document.querySelector('video'); video.addEventListener("play", function(evento){ var context = document.querySelector('#canvas_principale').ge tContext('2d'); var context_nascosto = document.querySelector('#canvas_elaborazione'). getContext('2d'); context.canvas.width = context_nascosto.canvas.width = video.clientWid th; context.canvas.height = context_nascosto.canvas.height = video.clientH eight; decomposizioneColori(evento.target,context,context_nascosto, document. forms.colori); },true); } window.addEventListener("load",aspettaIlPlay,true); </script> </head> <body> <canvas id="canvas_principale"></canvas> <canvas id="canvas_elaborazione" style="display:none;"></canvas> <video poster="big_buck_bunny/poster.jpg" controls> <source src="big_buck_bunny/trailer.mp4" type="video/mp4" > <source src="big_buck_bunny/trailer.ogg" type="video/ogg" > <source src="big_buck_bunny/trailer.webm" type="video/webm"> </video> <form name="colori"> <fieldset> <legend>Usa le checkbox per controllare i tre canali (RGB)</legend> <label> Rosso <input type="checkbox" name="rosso" checked></label> <label> Verde <input type="checkbox" name="verde" checked></label> <label> Blue <input type="checkbox" name="blu" checked></label> </fieldset> </form> </body> </html>
  • 150.
    Il funzionamento siconcentra in decomposizioneColori; qui vengono eseguite tutte le operazioni che abbiamo citato. All'interno del ciclo che scorre tutti i pixel (nelle componenti RGBA) un semplice controllo pilotato da una form 'spegne' le componenti di colore corrispondenti ad una checkbox non spuntata con un risultato simile a quello dell'immagine seguente: Figura 5 Merita un cenno anche l'interessante uso della funzione requestAnimationFrame (http://www.w3.org/TR/animation-timing/); il W3C ha predisposto questo metodo per meglio coordinare la gestione di animazioni all'interno di documenti web. A differenza dei suoi 'cugini' setTimeout e setInterval questa funzione applica alcune tecniche per ottimizzare le perfomance e mantenere basso l'utilizzo delle risorse del sistema. Purtroppo la maggior parte dei browser la considera ancora sperimentale e quindi antepone il proprio prefisso (moz, o, ms o webkit). Per risolvere questo problema esistono alcuni snippet di codice come quello proposto da Paul Irish
  • 151.
    (http://paulirish.com/2011/requestanimationframe-for-smart-animating/). Possiamo verificare iltutto nella demo conclusiva (http://www.html.it/guide/esempi/html5/esempi/lezione_video/video_canvas.html). Conclusioni In queste ultime due lezioni abbiamo esplorato tutto l'ecosistema che circonda la nascita del tag video. Prima di passare alla prossima lezione ricordiamo che intorno a questo elemento già si affacciano le prime librerie, capaci di aiutare nella personalizzazione del player e nella gestione di fallback su altre tecnologie nel caso l'HTML5 non sia supportato. In particolare segnaliamo l'ottimo video.js (http://videojs.com/), che sicuramente merita una visita di approfondimento. Audio La presenza del tag audio è naturale conseguenza dell'esistenza di video (http://xhtml.html.it/guide_preview/lezione/5003/video/). Fra l'altro i due elementi si assomigliano molto, sia in materia di markup che di API, al punto che nelle specifiche sono raggruppati all'interno della definizione di HTML Media Elements. L'utilizzo del tag audio sottende però aspetti assolutamente originali ed interessanti; soprattutto quando utilizzato per arricchire con effetti sonori una struttura HTML classica. Le specifiche La rappresentazione a markup dell'elemento ricalca perfettamente quella del video, anche se vi sono meno attributi disponibili, vediamo un esempio: <audio controls autoplay loop src="file_musicale.mp3"> In questo caso la definizione si risolve su di una singola linea e senza utilizzo del tag source perché è stato utilizzato l'attributo src, il cui compito è quello di evitare una scrittura troppo prolissa nel caso in cui il formato disponibile sia unico. Questo attributo anche se non è stato presentato nella lezione precedente è valido anche per il tag video, anche se a meno di un reale processo di unificazione dei codec ad oggi perde molta della sua utilità. In ogni caso la stessa definizione appena espressa si può scrivere anche nel formato esteso: <audio controls autoplay loop> <source src="file_musicale.mp3" type="audio/mp3"> </audio> Oltre agli attributi presentati nel codice qui sopra, deve essere citato anche preload; ognuno di questi è già stato illustrato nella lezione precedente e mantiene intatto il suo funzionamento anche su questo elemento. A differenza di video, l'elemento audio dispone anche di un proprio costruttore JavaScript, ecco la sintassi: var audio = new Audio("file_musicale.mp3"); In questo modo è possibile utilizzare musiche ed effetti audio senza necessariamente creare
  • 152.
    l'elemento HTML néricorrere a display:none o similari; tratteremo più approfonditamente di questo aspetto nella sezione dedicata all'esempio. Per quanto riguarda invece le API e gli eventi associati a questo elemento l'elenco è pressoché congruente con quello dell'elemento video; le stesse funzioni play, pause, playbackRate, duration,currentTime e tutto quanto legato ai TimeRanges mantengono inalterato il proprio comportamento in quanto appartenenti al gruppo HTMLMediaElements al quale entrambi gli elementi fanno parte. Un esempio Sfruttiamo la possibilità di creare oggetti audio utilizzando il costruttore JavaScript per studiare un meccanismo che ci consenta di associare ad ogni bottone un suono predefinito da riprodurre al click. Ecco il markup che useremo all'interno del body della pagina: <button type="button" data-effetto-audio="gatto_che_miagola.mp3" disabled> Il verso del gatto... </button> <button type="button" data-effetto-audio="cane_che_abbaia.mp3" disabled> Il verso del cane... </button> L'utilizzo degli attributi data-* ci consente in modo elegante di aggiungere un'informazione caratterizzante al pulsante, senza creare strane sovrastrutture. Ora il codice JavaScript dovrà occuparsi di scandagliare la pagina alla ricerca di pulsanti con suono associato e creare un oggetto audio con la sorgente che punti all'attributo che abbiamo definito. L'evento canplaythrough, verrà utilizzato per intercettare il momento in cui un dato oggetto audio ritiene di aver collezionato dati sufficienti per garantire la riproduzione dell'intero brano. All'accadere di tale evento il pulsante associato al suono verrà abilitato e un listener in ascolto sull'evento click garantirà l'esecuzione del suono alla pressione del bottone. Ecco il codice della demo completa: <!doctype html> <html> <head> <title>Il suono degli animali</title> <script> init = function(evento){ var bottoni = document.querySelectorAll('button'); for(var b=0; b < bottoni.length; b ++){ var effetto_audio = bottoni[b].dataset.effettoAudio; if(effetto_audio != ""){ var audio = new Audio(effetto_audio); audio.bottone_di_riferimento = bottoni[b]; audio.addEventListener('canplaythrough', function(evento_load){ var bottone = evento_load.target.bottone_di_riferimento; bottone.disabled = false; bottone.audio_di_riferimento = evento_load.target; bottone.addEventListener('click', function(evento_click){ evento_click.target.audio_di_riferimento.play(); }
  • 153.
    ); } ); } } } window.addEventListener('load',init); </script> </head> <body> <button type="button" data-effetto-audio="gatto_che_miagola.mp3" disabled> Il verso del gatto... </button> <button type="button" data-effetto-audio="cane_che_abbaia.mp3" disabled> Il verso del cane... </button> </body> </html> Il codice sembra a prima vista complicato ma è facilmente decomponibile in 3 fasi principali: Un ciclo for viene eseguito al caricamento della pagina su tutti gli elementi di tipo button; per ognuno di questi elementi viene istanziato un oggetto Audio. Su tale oggetto viene memorizzato un riferimento al bottone associato tramite la variabile bottone_di_riferimento e viene registrato un handler sull'evento canplaythrough. Quando un oggetto Audio colleziona un buffer di dati ritenuto dal browser tale da consentire la riproduzione integrale del suono, l'evento canplaythrough viene lanciato ed intercettato dalla nostra funzione che a quel punto crea, con un meccanismo simile al punto precedente, un handler sul pulsante presente nella variabile bottone_di_riferimento da attivarsi alla pressione dello stesso. Inoltre il pulsante viene abilitato (bottone.disabled=false) in modo da poter ricevere i click degli utenti. Il click dell'utente su di un pulsante attiva l'handler appena definito che non fa altro che riprodurre il suono presente nell'oggetto Audio ad esso associato. Eseguiamo l'applicazione appena sviluppata e sinceriamoci del suo corretto funzionamento: ecco l'esempio (http://www.html.it/guide/esempi/html5/esempi/lezione_audio/audio.html). Conclusioni L'elemento che abbiamo illustrato in questa sezione corre di pari passo con l'implementazione HTML5 del video, dal quale, abbiamo visto, eredita pressoché la totalità delle proprietà. Nonostante questo l'utilizzo dell'elemento audio si presta a scenari molto più variegati rispetto a quanto ci si potrebbe immaginare: parte di questa interessante flessibilità risiede proprio nella sua predisposizione ad essere creato direttamente da JavaScript. Tabella del supporto sui browser Grafica e Multimedia <audio> 9.0+ 3.5+ 4.0+ 5.0+ 10.5+ Codec audio
  • 154.
    MP3 Sì No Sì Sì No Wav No Sì Sì No Sì AAC Sì No Sì Sì No Ogg/Vorbis No Sì No Sì Sì SVG e MathML SVG SVG è un acronimo che significa Scalable Vector Graphic e indica una specifica modalità di definire elementi di natura grafica attraverso una sintassi XML, ecco un esempio: <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width="300px" height="300px" viewbox="0 0 300 300" xmlns="http://www.w3.org/2000/svg" version="1.1"> <title>Una barchetta in SVG</title> <desc>La stessa barchetta della lezione sui canvas ora in SVG</desc> <path d="M40,170 h220 A150,150 0 0,1 40,170 h110 v-130 v130 h-110 z" fill="black" stroke="black" stroke-width="1"/> <rect x="150" y="40" width="60" height="30" fill="black" stroke="black" stroke-width="1"/> </svg> La maggior parte dei browser in circolazione interpreta correttamente lo standard SVG, se proviamo a visualizzare questa pagina (http://www.html.it/guide/esempi/html5/esempi/lezione_svg/barchetta.svg) in Chrome otterremo questo risultato (figura 1): Figura 1 Nonostante il risultato sia a prima vista identico rispetto alla barchetta disegnata all’interno della lezione sull’elemento canvas, si può notare come a livello di codice ci sia una enorme differenza: mentre sul canvas è necessario insistere con istruzioni JavaScript che pilotano la creazione del disegno, qui ci troviamo di fronte ad un linguaggio XML fatto per descrivere l’immagine. Ma non è finita: da un lato (canvas) stiamo lavorando su di una matrice di pixel, che quindi non può essere ridimensionata senza subire perdite di qualità; dall’altro, con SVG, operiamo con grafica vettoriale, che preserva in modo assoluto lo stesso risultato a prescindere dalle operazioni di trasformazione applicate ad essa. MathML Anche MathML è un acronimo: Mathematical Markup Language; il fine di questa sintassi è quello di rendere possibile la visualizzazione a schermo di formule complesse, senza ricorrere al
  • 155.
    classico espediente dell’utilizzodi immagini create a partire da screenshot di applicazioni scientifiche. Anche in questo caso stiamo parlando di sintassi XML-like. A seguire un esempio della famosa equazione di Einstein secondo queste specifiche: <?xml version="1.0" encoding="UTF-8"?> <math xmlns="http://www.w3.org/1998/Math/MathML"> <semantics> <mrow> <mi>E</mi> <mi mathvariant="normal">=</mi> <msup> <mi mathvariant="italic">mc</mi> <mn>2</mn> </msup> </mrow> <annotation>E=mc^2</annotation> </semantics> </math> Per testare la sintassi del nostro esempio (http://www.html.it/guide/esempi/html5/esempi/lezione_svg/emc2.mml.html) è necessario munirsi della versione nightly (sperimentale) del browser WebKit (http://nightly.webkit.org/), tra i pochi a supportare queste specifiche. Questo è il risultato (figura 2): Figura 2 Novità in HTML5 Ed ecco la novità: le specifiche HTML5 prevedono l’integrazione di entrambi questi markup senza nessuna definizione aggiuntiva (a differenza della precedente integrazione con XHTML). Sarà quindi possibile scrivere: <!doctype html> <html>
  • 156.
    <title>Big Buck Bunny,il trailer</title> <body> <svg> <!-- elementi SVG --> </svg> <math> <!-- elementi MathML --> </math> </body> </html> E c’è di più: le due sintassi in questione potranno direttamente beneficiare dell’intero ecosistema HTML5 attorno a loro. Questo significa che una espressione MathML può, in questo contesto, variare a seguito della ricezione di alcuni dati tramite Ajax, oppure che una rappresentazione SVG può essere pilotata da alcuni eventi JavaScript attivati attraverso una form. Conclusioni Con queste due nuove frecce al suo arco, le specifiche HTML5 acquistano un nuovo livello di potenza. Forse non capiterà troppo spesso di implementare sintassi MathML per sistemi applicativi ma sicuramente lo stesso non si può dire per SVG. Anche il supporto a queste due specifiche è diverso, la Scalable Vector Graphic è pressoché presente su tutte le più recenti versioni dei browser, mentre il supporto per il markup per definire espressioni matematiche comincia ora ad affacciarsi nelle prime beta, come ad esempio Firefox 4 o la versione nightly di WebKit. In ogni caso esiste una interessante libreria JavaScript, MathJax (http://www.mathjax.org/), capace di interpretare linguaggio MathML e generare una corretta rappresentazione grafica sulla maggior parte dei browser in commercio. Una 'lavagna virtuale' Nel corso delle lezioni abbiamo assistito al graduale arricchimento del progetto guida; in questo articolo ne esploreremo alcune possibili evoluzioni attraverso l’utilizzo delle API HTML5 apprese finora. Il canvas nella dashboard È possibile fare in modo che dalla dashboard (dashboard.html) si possa visualizzare il contenuto, attuale e salvato, del canvas di una data fiveboard con un meccanismo simile a quello utilizzato per la memorizzazione su localStorage. Per ottenere questo risultato possiamo sostituire il blocco legato al comando ‘richiedi_testo’ nel file application.js con: case 'richiedi_testo': evento.ports[0].postMessage('testo_corrente:' + document.forms['form_da_ricordare'].elements['testo_da_ricordare'].value); evento.ports[0].postMessage('testo_memorizzato:' + localStorage.getItem("fb_" + titolo_fiveboard)); evento.ports[0].postMessage('canvas_corrente:' + ctx.canvas.toDataURL('image/png')); evento.ports[0].postMessage('canvas_memorizzato:'+
  • 157.
    localStorage.getItem("canvas_fb_" + titolo_fiveboard)); break; Aquesto punto dobbiamo far sì che la dashboard sappia ricevere ed interpretare correttamente questi 4 distinti messaggi, modifichiamo quindi il blocco che fa seguito al comando attendi_testo in dashboard.html: case 'attendi_testo': evento.ports[0].onmessage = function(e){ nome_comando = e.data.split(":")[0] valore_comando = e.data.substr(nome_comando.length + 1); elemento = document.getElementById(nome_comando); switch (elemento.tagName){ case 'CANVAS': elemento.width = elemento.width; if(valore_comando == 'null') return var context = elemento.getContext('2d'); var immagine = new Image(); immagine.src = valore_comando; context.drawImage(immagine,0,0); break; case 'TEXTAREA': if(valore_comando == 'null') valore_comando = ''; elemento.value = valore_comando; break; } } break; Nel frammento di codice appena esposto facciamo uso dell’attributo tagName per determinare il tipo di elemento che deve contenere le informazioni appena ricevute. In questo modo il comando ‘testo_memorizzato:contenuto_del_testo’ inviato dalla fiveboard si traduce nella valorizzazione dell’elemento ‘testo_memorizzato’ a ‘contenuto_del_testo’ nella dashboard. Completiamo questa evoluzione inserendo gli elementi di markup all’interno di dashboard.html: </ol> <section> <h1>In Osservazione:</h1> <textarea id="testo_corrente" placeholder="testo_corrente" readonly></textarea> <textarea id="testo_memorizzato" placeholder="testo_memorizzato" readonly></textarea > <canvas id="canvas_corrente">Canvas Corrente</canvas> <canvas id="canvas_memorizzato">Canvas Memorizzato</canvas> </section> Ecco uno screenshot dell’implementazione funzionante (figura 1):
  • 158.
    Figura 1 (clickper ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_progetto/1.jpg) Com’è possibile notare, ora alla pressione sul link ‘più informazioni’ la dashboard è in grado di mostrare sia testo che canvas, attuali e memorizzati, della fiveboard richiesta. Esportazione in SVG Potrebbe essere interessante, in questo contesto, offrire ai nostri utenti una esportazione in SVG del contenuto del canvas. In un prossimo futuro il task in questione potrà essere risolto in modo efficace utilizzando la funzione canvas.toDataUrl con parametro ‘image/svg+xml’ ; purtroppo ad oggi i browser supportano soltanto un'esportazione in formato png e quindi dovremo costruire qualcosa di più articolato per soddisfare questo comportamento.
  • 159.
    Iniziamo con l’aggiungerequesta funzione in canvas.js: EsportaInSVG = function(){ var bb = new BlobBuilder(); bb.append( "" + "<?xml version='1.0' standalone='no'?>" + "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'" + " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>" + "<svg width='300px' height='300px' viewbox='0 0 300 300'" + " xmlns='http://www.w3.org/2000/svg' version='1.1'>" + " <title>SVG: " + titolo_fiveboard + "</title>" + " <desc>Un esportazione in SVG del canvas disegnato</desc>" + " <path d='" + svg_path + " z' fill='transparent'" + " stroke='black' stroke-width='1'/>" + "</svg>" ); window.open(window.createObjectURL(bb.getBlob('image/svg+xml'))); } L’oggetto BlobBuilder fa parte delle File API: Writer (http://www.w3.org/TR/file-writer-api/), un set di funzionalità che gravitano attorno alle specifiche HTML5 e consentono di costruire veri e propri file da far scaricare all’utente o ai quali far puntare il browser. Al momento questi metodi sono ancora abbastanza sperimentali, nonostante già supportati in Chrome, Firefox e WebKit. In questo esempio la variabile bbviene valorizzata con un file SVG nel quale sono inseriti dinamicamente i contenuti delle variabili titolo_fiveboard, il titolo della fiveboard, e svg_path, della cui costruzione ci occuperemo a breve. Nell’ultima riga della funzione il browser apre una nuova finestra verso una URL univoca generata dal metodo createObjectURL. Tale URL punta al contenuto della variabile bb esposto con MIME image/svg+xml. Occupiamoci ora della generazione della variabile svg_path che dovrà contenere le istruzioni per ricostruire il percorso generato dall’utente sul canvas. Modifichiamo il file canvas.js come segue: var ctx = null; var started = false; var svg_path = ""; iniziaDisegno = function(evento){ ctx.beginPath(); ctx.moveTo(evento.offsetX,evento.offsetY); svg_path += "M " + evento.offsetX + " " + evento.offsetY + " "; started = true; } disegna = function(evento){ if(started){ ctx.lineTo(evento.offsetX,evento.offsetY); svg_path += "L " + evento.offsetX + " " + evento.offsetY + " "; ctx.stroke(); } }
  • 160.
    Il trucco stanel far seguire all’istruzione che insiste sul canvas la corrispondente variazione da applicare al path svg. In questo modo una particella ‘M x y’ verrà concatenata a svg_path per ogni istruzionemoveTo inviata al canvas, lo stesso accadrà per l’istruzione lineTo, seguita da una concatenazione di ‘L x y’. L’ultimo passo prima del completamento di questa evoluzione consiste nell’aggiungere il pulsante preposto a far scatenare la funzione EsportaInSVG: </button> <button type="button" onclick="EsportaInSVG();"> Esporta il canvas corrente in SVG </button> </menu> Per testare questa modifica ricorriamo a Chrome (figura 2): Figura 2 (click per ingrandire) (http://www.html.it/guide/esempi/html5/imgs/lezione_progetto/2.jpg)
  • 161.
    Ed ecco ilrisultato! Mentre la barchetta nella finestra in sfondo risiede su di un canvas quella in primo piano è creata a partire da un file SVG; notate inoltra il curioso URL di questa finestra, generato in tempo reale dalla funzione di cui abbiamo parlato poco fa. Non ci resta che rimandare alla demo (http://www.html.it/guide/esempi/html5/esempi/lezione_progetto/fiveboard/index.html) per un test. Il codice è disponibile per il download (http://www.html.it/guide/esempi/html5/esempi/lezione_progetto/fiveboard.zip). Un passo avanti Con quest’ultima evoluzione ci apprestiamo a congedare il progetto guida, utile assistente durante le passate lezioni. Per chi fosse interessato a mantenere questo applicativo come base per proprie sperimentazioni ecco alcuni possibili e sfidanti implementazioni effettuabili: Fare in modo che il viewer.html possa seguire in tempo reale anche l’atto del disegno sul canvas della fiveboard che sta osservando, così come oggi avviene per il testo. Questa modifica coinvolge la definizione di un protocollo snello per la trasmissione di dati di questo tipo attraverso un WebSocket. Refactoring! Perché non provare a fare in modo che i comandi che giungono alle pagine web nel formato scelto per convenzione come ‘nome_comando:valore_comando’ siano trasformati in eventi custom e propagati attraverso il DOM ? La sintassi potrebbe essere questa: worker.port.onmessage = function(evento){ nome_comando = evento.data.split(":")[0] valore_comando = evento.data.substr(nome_comando.length + 1); var evento_custom = document.createEvent("CustomEvent"); evento_custom.initCustomEvent(nome_comando, true, true, valore_comando); document.dispatchEvent(evento_custom); } In questo modo si potrebbe ottenere una struttura molto più elegante, nella quale ogni aspetto dell’applicazione si sottoscrive autonomamente agli eventi dei quali necessita. Conclusioni Con questa lezione si conclude il ciclo dedicato all’esplorazione delle nuove API rese disponibili dall’HTML5. Prima della conclusione di questa guida trova spazio un'ultima lezione, la prossima, incentrata su tutte le tematiche che legano le specifiche al mondo reale. Verranno quindi trattati temi come il feature detection ed elencate librerie che possono, di caso in caso, sopperire alla mancanza di particolari funzionalità. Proprio a questo tema ricordiamo che il progetto guida è nato e cresciuto con finalità didattiche e di sperimentazione e per questa ragione è molto carente in termini di compatibilità tra browser e graceful degradation; a ben pensare anche perseguire queste tematiche potrebbe essere un ottimo esercizio da svolgere in autonomia dopo aver letto la prossima lezione! Feature detection e strategie di fallback Come si comportano le API e i nuovi tag proposti dall'HTML5 nel mondo reale? Come dobbiamo
  • 162.
    comportarci se vogliamobeneficiarne? In quest'ultima lezione risponderemo a questa importantissima domanda esaminando alcune best-practice per una implementazione che possa arricchire le nostre applicazioni e portali web senza causare grattacapi e disagi. Una pioggia di tag Possiamo usare <article>, <section> o <footer> su browser come Internet Explorer 6? La risposta è sì, a patto di premunirsi di alcuni semplici e comodi strumenti capaci di far recepire questi nuovi elementi HTML5 anche a user-agent non più giovanissimi. Uno di questi tool, specificatamente progettato per i browser di casa Microsoft, prende il nome di html5shim (http://code.google.com/p/html5shim/) e può essere incluso nelle proprie applicazioni molto facilmente: <!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> Questo strumento fa sì che Internet Explorer possa applicare gli stili ai nuovi tag introdotti dall'HTML5. Ma ancora non basta. La maggior parte dei browser di non ultima generazione, infatti, non conoscendo direttamente i nuovi elementi, applica a loro un foglio di stile di base errato, assegnando ad esempio display:inline a tag come <article> o <section>. Per risolvere questa fastidiosa incombenza la soluzione migliore è utilizzare un foglio di stile capace di resettare le proprietà di questi e altri elementi attivando il comportamento atteso. HTML5 reset stylesheet (http://html5doctor.com/html-5-reset-stylesheet/) si presta in modo ottimale a questo obiettivo; l'unico passo da compiere è includerlo all'interno del proprio sito prima degli altri documenti .css. Feature detection vs. browser detection La corsa all'HTML5 intrapresa dai vari browser nell'ultimo periodo si può descrivere come una incrementale implementazione delle feature proposte dal W3C; il criterio con cui viene stabilita la priorità nella 'scaletta delle API da aggiungere' è influenzato da moltissimi fattori e differisce da prodotto a prodotto. Allo stato attuale delle cose non ha quindi particolarmente senso determinare il comportamento della propria applicazione sulla base dello user-agent che la sta eseguendo, quanto più cercare di intercettare la presenza o meno di una specifica feature. L'attività, potenzialmente noiosa di per sé, viene facilitata in modo estremo dalla presenza sul mercato di un utilissimo tool: Modernizr.js (http://www.modernizr.com/). Facciamo un esempio, sinceriamoci del supporto per il localStorage: if(Modernizr.localstorage) { alert("Rilevato il supporto per il localStorage"); } else { alert("Oh oh, nessun supporto per il localStorage"); } Semplice, elegante e molto comodo. L'installazione di questa libreria si risolve con una semplice inclusione e una speciale classe da applicare al tag <html>: <html class="no-js"> <head>
  • 163.
    <script src="/modernizr-1.6.min.js"></script> <script> // Ora puoi utilizzare Modernizr! </script> </head> Fallback e alternative Abbiamo scoperto che il nostro utente non supporta una certa funzionalità... e ora? Niente panico: nella maggior parte dei casi esistono valide alternative, o quantomeno interessanti opzioni di fallback. Vediamole nel dettaglio. Audio e Video In questo caso l'alternativa migliore rimane la collaudata tecnologia Flash. Esistono parecchie librerie sulla rete capaci di determinare autonomamente il supporto o meno al tag <video> e agire di conseguenza. Noi abbiamo scelto video.js (http://videojs.com/) per la sua maturità e per l'ottimo supporto di skin con le quali personalizzare il proprio player. L'utilizzo è semplicissimo e ben illustrato nella pagina di Getting Started (http://videojs.com/#getting-started). Canvas Abbiamo due soluzioni da segnalare come alternativa all'assenza canvas: la prima si chiama explorercanvas (http://code.google.com/p/explorercanvas/), una libreria Javascript sviluppata da Google per simulare il comportamento del <canvas> all'interno di Internet Explorer. Per attivarla è sufficiente richiamare lo script excanvas.js all'interno del proprio tag <head>: <!--[if IE]><script src="excanvas.js"></script><![endif]--> Da qui in poi è possibile utilizzare all'interno della pagina il tag <canvas> ed invocare su questo la maggior parte dei metodi previsti dalle specifiche W3C. L'altra tecnica di fallback prende invece il nome di FlashCanvas (http://flashcanvas.net/) ed emula il comportamento delle API HTML5 attraverso il sapiente utilizzo della tecnologia Flash. La versione 1.5, supera più del 70% dei test proposti da Philip Taylor (http://philip.html5.org/tests/canvas/suite/tests/), piazzandosi in questo modo sul primo gradino del podio delle alternative disponibili. Anche in questo caso per beneficiare di questa libreria è necessario solamente richiamare l'apposito file javascript: <!--[if lt IE 9]> <script type="text/javascript" src="path/to/flashcanvas.js"></script> <![endif]--> Geolocation Come simulare le API di geolocazione quando non sono supportate? Prima di disperare è importante ricordare che seppur non tutti browser implementano queste specifiche è comunque possibile alle volte accedere ad informazioni geospaziali utilizzando API proprietarie messe a disposizione da particolari device o plug-in esterni, come ad esempio Google Gears. geo-location-javascript (http://code.google.com/p/geo-location-javascript/) è una libreria che identifica le varie estensioni disponibili sul portatile (o smartphone) dell'utente e ne uniforma e centralizza le API permettendo di
  • 164.
    accedere ad informazionicome latitudine e longitudine usando gli stessi metodi proposti dal W3C. Ecco un esempio. Per prima cosa è necessario includere i necessari file javascript: <script src="http://code.google.com/apis/gears/gears_init.js" type="text/javascript">< /script> <script src="geo.js" type="text/javascript" ></script> Quindi è necessario inizializzare la libreria con il comando: if (!geo_position_js.init()) { // funzionalità di geolocazione non presenti (ne HTML5 ne attraverso estensioni // proprietarie...) } A questo punto è possibile invocare i classici metodi come da specifiche W3C, stando attendi a preporre l'oggetto geo_position_js: geo_position_js.getCurrentPosition(funzione_se_successo,funzione_se_errore); Drag and Drop In questo caso ci sono due tematiche da affrontare: la prima è di carattere 'workaround' e si focalizza sull'identificare soluzioni alternative che preservino lo stesso meccanismo di interazione del Drag and Drop HTML5. Per questo tipo di eventualità esistono potentissime librerie capaci di offrire esattamente questo tipo di comportamento: le più famose sono ovviamente JQueryUI (http://jqueryui.com/demos/draggable/), ma anche Scriptaculous (http://madrobby.github.com/scriptaculous/draggable/) e il nuovo Scripty2 (http://scripty2.com/doc/scripty2%20ui/s2/ui/behavior/drag.html). Tutto questa interazione di fallback, invece, viene meno nel caso le API previste dal W3C siano utilizzate espressamente per la loro funzione di input, quindi, ad esempio, per trascinare file all'interno della pagina web. In questo caso si può pensare di mantenere la funzionalità riducendo il potere dell'interfaccia e dell'interazione, realisticamente con l'utilizzo di normalissime form e di campi <input type=file>. WebSocket Appurata l'assenza del supporto WebSocket l'alternativa migliore rimane ripiegare su una tra le tecniche per soppiantare le quali sono nate proprio queste API HTML5: stiamo parlando di Comet e diwebsocket in Flash. Proprio a proposito di quest'ultima opzione merita un cenno l'ottimo, anche se poco documentato, web-socket-js (https://github.com/gimite/web-socket- js/blob/master/README.txt) capace di replicare il comportamento delle specifiche W3C utilizzando però un piccolo componente Flash iniettato dinamicamente all'interno della pagina. A questo indirizzo (https://github.com/gimite/web-socket-js/blob/master/sample.html) un esempio di utilizzo. WebWorkers Non esistono, al momento, soluzioni di ripiego per riparare l'assenza del supporto di questa parte delle specifiche HTML5 senza ricorrere ad estensioni di terze parti come le ottime WorkerPool API (http://code.google.com/intl/it-IT/apis/gears/api_workerpool.html) di Google Gears. L'unico workaround possibile è scrivere una versione del codice eseguito dal worker capace di funzionare all'interno del contesto della pagina ed eseguire quella nel caso in cui venga verificata l'assenza della feature (chiaramente perdendo il vantaggio dell'esecuzione asincrona). Nel caso invece si stia
  • 165.
    trattando si SharedWebWorkersil discorso si complica ulteriormente; è in linea teorica possibile emulare artigianalmente un comportamento di questo tipo utilizzando un area di storage comune (es: localStorage) per lo scambio di messaggi convenzionati tra le pagine dello stesso dominio. WebStorage e IndexedDB Come per i WebWorkers, anche qui la soluzione di fallback più completa in termini di funzionalità passa attraverso Google Gears: stiamo parlando delle Database API (http://code.google.com/intl/it- IT/apis/gears/api_database.html). E' però importante sottolineare che, mentre WebStorage e IndexedDB basano la loro filosofia di funzionamento su array associativi, Google Gears si appoggia ad una istanza di SQLite, un database relazionale che opera tramite classiche istruzioni SQL, In questo caso quindi offrire una soluzione alternativa all'assenza di questi componenti HTML5 potrebbe rivelarsi decisamente costoso. Offline Web Application Google Gears anche in questo caso. Le API si chiamano LocalServer (http://code.google.com/intl/it- IT/apis/gears/api_localserver.html) e presentano funzionalità simili a quelle offerte dalle implementazioni delle specifiche W3C. Shim e Polyfill a profusione Abbiamo dato solo un piccolo assaggio della quantità di librerie alternative disponibili per sopperire alle più svariate API HTML5. In realtà il numero di questi software di fallback è decisamente sostanzioso ed è bene introdurre alcune differenze di massima tra le varie tipologie di librerie disponibili. Definiamo come Shim una libreria che sopperisce ad una funzione mancante emulandone il comportamento ma richiedendo allo sviluppatore uno sforzo aggiuntivo in quando le API che mette a disposizione differiscono da quelle native in HTML5. Con Polyfill indichiamo invece uno Shim che però fornisce allo sviluppatore le medesime API offerte dalla controparte HTML5 rendendo così necessaria una singola implementazione. Quindi, ove possibile è meglio preferire un Polyfill ad uno Shim. Già, ma come trovarli? Si può consultare la lista dei migliori polyfill (https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross- Browser-Polyfills) redatta dagli sviluppatori di Modernizr. Un ultimo appunto: se il numero di file javascript dovesse diventare troppo sostanzioso ricordiamoci che è sempre possibile ricorrere ad un loader condizionale, come yepnope.js (http://yepnopejs.com/). Conclusioni Siamo alla fine, con queste ultime righe si conclude la guida HTML5 di html.it, in circa 50 lezioni abbiamo sorvolato un pianeta ricco di nuove ed interessanti opportunità, capaci di far evolvere le nostre creazioni web fino a dove pochi anni fa non ritenevamo possibile. In questa lezione abbiamo scoperto come le specifiche si intreccino con il mondo reale e quali sono i potenziali comportamenti di fallback da implementare nel caso di assenza di qualche particolare feature. In particolare ci siamo accorti che per tematiche di basso livello (WebStorage, IndexedDB e Offline Web Application) non esistono soluzioni alternative, se si esclude Google Gears, che però necessita di una installazione manuale da parte dell'utente finale. Un ultimo punto importante prima di concludere: non tutte le funzionalità proposte dall'HTML5 sono da considerarsi pronte per un uso in fase di produzione, alcune di esse infatti sono ancora in uno
  • 166.
    stadio di implementazionepoco stabile, oppure subiranno a breve importanti cambiamenti nelle loro API; per avere un elenco aggiornato di cosa è considerato utilizzabile in produzione e cosa no potete consultare la tabella di compatibilità posta in appendice a questa guida.