Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Graceful degradation e progressive enhancement

Graceful degradation e progressive enhancement, analisi di due approcci in grado di coniugare accessibilità e Javascript
Graceful degradation e progressive enhancement, analisi di due approcci in grado di coniugare accessibilità e Javascript
Link copiato negli appunti

Questa è la traduzione dell'articolo Graceful Degradation & Progressive Enhancement
di Tommy Olson, pubblicato originariamente su Accessites.org il 6 febbraio 2007. La traduzione viene qui presentata con il consenso dell'editore e dell'autore.

Graceful degradation (decadimento parziale) e progressive enhancement (miglioramento progressivo) sono due facce della stessa medaglia. In questo contesto entrambi i concetti sono utilizzati per rendere un sito accessibile a tutti i dispositivi fornendo al tempo stesso un'estetica e un'usabilità migliori ai browser più avanzati. La differenza consiste essenzialmente nell'approccio da cui si inizia.

Tempo fa ho scritto un articolo sui due diversi approcci possibili nel web design: approccio visuale vs. approccio strutturale. I concetti di graceful degradation e progressive enhancement sono strettamente correlati a questi due approcci.

Graceful degradation

Quello di graceful degradation è il più vecchio dei due concetti. Il termine è utilizzato anche in ambiti diversi dal web design, per esempio la meccanica o i sistemi elettrici.

La premessa teorica alla base della graceful degradation è questa: progettare prima per i dispositivi più avanzati, poi aggiungere meccanismi per controllare la resa su dispositivi meno capaci. In altre parole: ci si concentra sulla maggioranza prima di occuparsi di quelli che stanno ai margini. Tutto ciò è molto simile all'approccio visuale al web design, quello per cui la priorità è offrire un'esperienza estetica superiore alla maggior parte dei visitatori.

Un esempio familiare di graceful degradation è costituito dall'attributo alt sulle immagini. Quando è usato con attenzione dà un equivalente testuale che fornisce la stessa informazione fornita dall'immagine ad utenti che non possono vedere l'immagine stessa. L'equivalente testuale non è esteticamente altrettanto gradevole, e un'immagine può dire più di mille parole, così l'esperienza dell'utente ne risulta leggermente degradata.

Usare tabelle per il layout può essere visto come un'altra forma di graceful degradation: se la formattazione con i CSS non può essere applicata -per esempio nei vecchi browser- almeno si mantiene un layout di base e minimale. Ma tutto ciò non funziona bene su browser testuali come Lynx e su alcuni telefoni cellulari che non supportano le tabelle.

Un'altra situazione comune su siti progettati a partire dall'idea di graceful degradation è l'uso dell'elemento noscript. Si fornisce una certa funzionalità basata su Javascript e si aggiunge una versione più di base per i dispositivi che non supportano questo linguaggio o che lo hanno disabilitato. Un esempio potrebbe essere quello dei diffusissimi menu dinamici:

<script type="text/javascript" src="/menu.js"></script>
<noscript>
<ul id="menu">
<li><a href="/">Home</a></li>
<li><a href="/products/">Products</a></li>
<li><a href="/services/">Services</a></li>
</ul>
</noscript>

La funzione Javascript può creare un menu in cui le voci "Products" e "Services" contengono dei sotto-menu che compaiono o si espandono quando l'utente passa con il mouse sopra le voci principali. L'alternativa senza Javascript fornisce un accesso immediato solo alle voci principali, mentre i sotto-menu sono (presumibilmente) inclusi in elementi noscript simili a quello visto sulle rispettive pagine interne.

Questo approccio è pensato per la maggior parte degli utenti, quelli che navigano con browser grafici che supportano Javascript, ma si adatta in modo da funzionare anche nell'ultimo dei browser testuali. Inoltre, aggiunge i benefici delle ancore HTML usate a dovere a certi user agent come gli spider dei motori di ricerca, facendo così venire meno gli svantaggi dei menu Javascript ai fini dell'indicizzazione sui motori.

C'è un problema con noscript, però. Io potrei usare un browser che supporta Javascript e che lo abbia abilitato, ma potrebbe esserci un firewall aziendale che filtra il codice Javascript in ingresso per motivi di sicurezza. In quel caso l'elemento noscript non sarà preso in considerazione (perché il mio browser supporta gli script), ma non verrà applicato nemmeno il codice che crea il menu (perché viene filtrato dal firewall).

Quando la graceful degradation non funziona

Purtroppo il concetto di graceful degradation supera talvolta i limiti dell'accettabile, per cui il decadimento non è più tanto parziale. Il caso peggiore è di questo tipo:

<script type="text/javascript" src="/menu.js"></script>
<noscript>
<p>Please upgrade to a browser that supports JavaScript.</p>
</noscript>

Ciò è inaccettabile per qualunque sito web che aspiri ad essere serio e credibile, ma può essere accettato su piccoli siti amatoriali che si rivolgono ad un pubblico di amici e familiari.

Come web designer e sviluppatori, non abbiamo nessun diritto di dire ai nostri visitatori quale browser dovrebbero usare. Non abbiamo nemmeno il diritto di dire a qualcuno che deve abilitare Javascript. Non sappiamo perché uno naviga con Lynx e perché un altro ha disabilitato il supporto a Javascript. Magari non ha la possibilità di "aggiornare il browser" o di "abilitare gli script" per motivi di banda, di hardware o di budget, per le restrizioni applicate a livello aziendale, o perché non sta usando il proprio computer.

La graceful degradation è di gran lunga migliore che avere un sito del tutto inaccessibile ad alcuni utenti, ma non è il miglior approccio. Come per il design visuale, è spesso difficile iniziare da un set di funzionalità completo per poi rimuoverle una ad una senza incorrere in qualche errore.

Progressive enhancement

Il progressive enhancement è divenuto noto -almeno il nome- nel 2003, quando Steve Champeon iniziò ad usarlo su Webmonkey e durante la conferenza SXSW di Austin. Esso inizia esattamente all'estremo opposto rispetto alla graceful degradation: si inizia con la versione più di base, poi si aggiungono miglioramenti per quelli che hanno dispositivi in grado di gestirli. Ancora una volta, facendo il confronto con gli approcci al web design, il progressive enhancement è paragonabile all'approccio strutturale. Che inizia con il markup e aggiunge poi gli stili (cosa che è di per sé un esempio di progressive enhancement).

La più comune situazione riscontrabile in fatto di progressive enhancement è rappresentata dai fogli di stile CSS esterni. Essi vengono ignorati dai browser che non supportano i CSS, che così ottengono solo il semplice markup e lo rendono a schermo nel modo conseguente. Vengono però applicati dai browser più avanzati, migliorando così sia l'estetica sia l'usabilità per la maggior parte degli utenti.

Altri esempi di progressive enhancement sono le varie tecniche di image replacement, il metodo Flash satay e (talvolta) Ajax.

Il progressive enhancement, relativamente a Javascript, si sta sempre più diffondendo negli ultimi tempi. L'approccio è noto con il nome di 'Javascript non intrusivo'. Un script non intrusivo è ignorato dai dispositivi che non lo supportano, ma è applicato da quelli più avanzati. Esattamente come un foglio di stile esterno.

Rivediamo il menu visto in precedenza. Come dovrebbe essere implementato secondo i dettami del progressive enhancement?

Inizieremo con il creare il markup, cercando di ottenere una sorta di minimo comun denominatore: HTML. Un menu di navigazione è in fin dei conti e semanticamente una lista di link. L'ordine di questi link non influisce sul significato della lista nel suo complesso. Perciò sarà una lista non ordinata:

<ul>
<li><a href="/">Home</a></li>
<li><a href="/products/">Products</a></li>
<li><a href="/services/">Services</a></li>
</ul>

Questo codice funzionerà su tutti i browser, da Lynx e Mosaic fino all'ultima versione di Opera, Firefox e Safari. Googlebot e gli spider suoi cugini lo ameranno ugualmente.

Il passo successivo è di aggiungere dei miglioramenti per la vasta maggioranza degli utenti i cui browser supportano i CSS. Aggiungiamo infatti una serie di regole in un CSS esterno per dare uno stile al menu. Dopo aver aggiunto l'elemento link con un riferimento al foglio di stile esterno, il menu apparirà sicuramente migliore alla maggior parte dei visitatori. Questa aggiunta, però, non arreca alcun danno a chi usa Lynx o a Googlebot (Ok, dovranno scaricare qualche byte in più, ma è un download trascurabile anche su lente connessioni dial-up o su un cellulare GSM).

Possiamo però ulteriormente migliorare il menu aggiungendo l'effetto dinamico che espande le voci principali e mostra i sotto-menu, facendo ricorso ad un Javascript non intrusivo. Per ridurre il peso del codice cominciamo ad assegnare un id agli elementi della lista:

<li id="products"><a href="/products/">Products</a></li>
<li id="services"><a href="/services/">Services</a></li>

Quindi creiamo un file Javascript separato con un paio di funzioni:

function addProducts()
{
// Find the li element to which we will add a sub-menu
var parent = document.getElementById("products");
// Make sure it exists (fail silently)
if (parent) {
// Create a nested unordered list
var ul = parent.appendChild(document.createElement("UL"));
// Add the list items and links
var items = [ ["Blue Widgets", "blue"],
["Red Widgets", "red"] ];
for (var i = 0; i < items.length; ++i) {
var li = ul.appendChild(document.createElement("LI"));
var a = li.appendChild(document.createElement("A"));
a.href = "/products/" + items[i][1];
a.appendChild(document.createTextNode(items[i][0]));
}
}
}

La funzione addServices() sarà molto simile. Dopo ciò, dobbiamo assicurarci che il browser supporti la manipolazione del DOM:

function createSubMenus()
{
// Make sure that the DOM functions we will use are supported
// (fail silently)
if (typeof document.getElementById != "undefined"
&& typeof document.createElement != "undefined"
&& typeof document.createTextNode != "undefined")
{
addProducts();
addServices();
}
}

Questa funzione ci assicura che le principali funzioni DOM usate in addProducts() e addServices() sono supportate dal browser. Se così non è, la funzione non viene eseguita e non produce effetti. Non causa alcun danno, non apparirà nessun avviso di errore, nemmeno icone di warning. Questo approccio è definito 'object detection' ed è infinitamente meglio degli script basati sul browser sniffing vecchia scuola che smettevano di funzionare appena usciva un nuovo browser (o una nuova versione di un browser esistente).

Per finire, per far sì che il browser richiami queste funzioni appena la pagina è caricata, usiamo questo codice:

if (window.addEventListener) {
window.addEventListener("load", createSubMenus, false);
} else if (window.attachEvent) {
window.attachEvent("onload", createSubMenus);
} else {
window.onload = createSubMenus;
}

Si noti che questo pezzo di codice non è racchiuso in una funzione. Esso aggiungerà un event listener per l'evento load della finestra, richiamando la funzione che crea il nostro menu subito dopo che il caricamento del codice HTML si è concluso.

I browser moderni useranno la funzione addEventListener() definita nella specifica DOM Level 2, mentre Internet Explorer userà la sua funzione proprietaria attachEvent().

Quest'ultimo punto non è perfetto: rimpiazzerà tutti gli eventi onload creati da altri precedenti script. Non è proprio 'non intrusivo'. Nella vita reale dovremmo adottare una soluzione leggermente più complicata per questa situazione, ma non voglio rendere l'esempio più complicato.

Aggiungeremo un elemento script nella sezione HEAD della pagina per caricare il Javascript esterno:

<script type="text/javascript" src="/menu.js"></script>

Il passo finale sarà quello di aggiungere qualche regola CSS per i sotto-menu (per mostrarli e nasconderli), facendo sì che siano attivabili sia con il mouse sia con la tastiera.

Validazione di form

La validazione di form è un altro uso comune per Javascript, ma si può benissimo fare a meno di basarsi solo su Javascript. Se lo abbiamo fatto, qualcuno che ha disabilitato gli script client-side potrebbe inviare dati non validi, oppure qualche script malevolo potrebbe abusare del nostro form per chissà quali scopi.

La validazione dei form dovrebbe sempre avvenire a livello server, ma per ragioni di efficienza potremmo decidere di affidarci alla validazione client-side in aggiunta a quella lato server. Daremo una buona funzionalità agli utenti, dal momento che essi potranno ricevere un feedback più rapido, ma anche noi ne trarremo dei benefici perché tutto ciò riduce il numero di chiamate ai nostri server.

Usando i metodi appena evidenziati, possiamo aggiungere la nostra validazione Javascript non intrusiva e con la object detection. Un browser moderno con Javascript abilitato potrà contare su un processo di validazione migliorato, mentre ci appoggeremo alla validazione lato server per la minoranza di utenti che non possono usare gli script lato client.

Ajax

Di Ajax si parla tanto dall'anno scorso. Tutti devono usarlo, sia che serva davvero sia che non serva. Talvolta ho la sensazione che sia una risposta in cerca di una domanda, ma non c'è dubbio che si tratti di una tecnica utile in diverse situazioni.

Prendiamo in considerazione un form con due o più elementi select in cui ogni select dipende da quella precedente. Esempi tipici sono i menu con elementi tipo/sottotipo oppure regione/città. A seconda di cosa si seleziona nella prima lista, cambia il contenuto della seconda. Come possiamo far funzionare un simile meccanismo per tutti gli utenti?

Inizieremo ancora una volta dagli elementi di base. Non pensate agli script in questa fase. Pensate solo al puro e semplice HTML:

<form action="products.php" method="post">
<fieldset>
<legend>Product Type</legend>
<label for="type">Type</label>
<select name="type" id="type">
<option value="none" selected>- select a type -</option>
<option value="widgets">Widgets</option>
<option value="gadgets">Gadgets</option>
</select>
<input type="submit" name="chooseType" value="Select">
<br>
<label for="subtype">Subtype</label>
<select name="subtype" id="subtype" disabled>
<option></option>
</select>
<br>
<input type="submit" name="show" value="Show Products" disabled>
</fieldset>
</form>

La nostra lista primaria (Type) è popolata con i nostri tipi di prodotto: widget e gadget. La lista secondaria (Subtype) è vuota e disabilitata, e anche il pulsante principale di submit ("Show products") è disabilitato. Un utente deve scegliere prima un tipo di prodotto e poi attivare l'altro pulsante di submit ("Select"). Il form è inviato al nostro script lato server (products.php), che mostrerà nuovamente il form con la lista dei sottotipi popolata con gli opportuni valori (oppure mostrerà un messaggio di errore se non viene selezionato nessun sottotipo).

L'utente può a quel punto selezionare un sottotipo e inviare il form usando il pulsante "Show Products" (che ora non è più disabilitato). Se cambia idea e vuole selezionare un altro tipo, dovrà nuovamente attivare il pulsante "Select" per caricare il sottotipo corretto.

Non è certo il form più usabile del mondo. Ha bisogno di un noioso script lato server, ma funziona praticamente su tutti i browser. Anche su Lynx.

Noi, però, vorremmo renderlo più veloce e facile da usare. Sarebbe bello, per esempio, popolare la seconda lista non appena si cambia la selezione nella prima. Ciò richiede Javascript. Molto spesso, poi, le voci relative a tipi e sottotipi sono dinamiche e prese da un database, un fattore che si presta alla perfezione per una soluzione basata su Ajax.

Useremo una qualche forma di Javascript non intrusivo per identificare il supporto per le funzionalità necessarie (soprattutto il supporto dell'oggetto XMLHttpRequest). Se è supportato, aggiungeremo un gestore di eventi per la prima lista usando addEventListener() o attachEvent(), a seconda del browser dell'utente. Quindi andremo a rimuovere il pulsante "Select" usando la funzione DOM removeChild(), perché esso non più necessario e porterebbe solo confusione.

L'event listener aggiunto aprirà una connessione asincrona con il nostro server e posterà una richiesta HTTP specificando il tipo selezionato. Lo script lato server interrogherà il database e restituirà un risposta in formato XML che contiene i sottotipi corrispondenti, quelli che andranno a popolare la lista secondaria.

Questo è progressive enhancement: funziona per tutti, ma gli utenti con browser moderni e avanzati vedranno una versione più usabile. In un certo senso, li stiamo gratificando per il fatto di usare un buon browser, senza perciò essere sgarbati con gli utenti di Lynx o con i dipendenti di aziende particolarmente paranoiche sul versante della sicurezza. Non dobbiamo nemmeno affidarci all'elemento noscript, e dal momento che usiamo la object detetion invece che il browser sniffing, non dobbiamo nemmeno preoccuparci di dover aggiornare lo script per ogni nuova versione di browser.

Scegliere un metodo

Sia la graceful degradation sia il progressive enhancement aiutano nel creare un sito accessibile continuando però a fornire una maggiore usabilità a quelli che possono sfruttarla. E allora quale approccio dovremmo scegliere?

Il progressive enhancement è in genere da preferire, per la stessa ragione per cui l'approccio strutturale al web design è da preferire a quello visuale: si parte con gli elementi di base e si aggiungono poi gli abbellimenti. Quando progettiamo qualcosa dal nulla, dovremmo sempre ragionare in termini di progresive enhancement.

Se stiamo mantendendo un sito esistente, provare a migliorare l'accessibilità e l'aderenza agli standard, è cosa differente. A meno di non voler riscrivere tutto, la nostra unica opzione è la graceful degradation.

Ovviamente è anche possibile mescolare i due metodi su un sito, o anche sulla stessa pagina. Se abbiamo la possibilità di scegliere, comunque, dovremmo affidarci al progressive enhancement.

Testare

Testare l'accessibilità è più facile con il progressive enhancement che con la graceful degradation.

Se lavoriamo secondo il primo approccio, possiamo semplicemente creare la versione di base e verificare che funzioni. Quindi aggiungiamo i miglioramenti e verifichiamo che funzionino. OK, magari è una semplificazione: quando testiamo gli elementi aggiuntivi dobbiamo verificare che non intacchino la versione di base.

Se usiamo la graceful degradation, invece, dobbiamo adottare un approccio diverso. Le funzionalità avanzate sono già presenti e possono essere prontamente testate. Per verificare che la graceful degradation funzioni, dobbiamo disabilitare il supporto per tali funzionalità. Talvolta questo tipo di test richiederà test in browser differenti, ma in alcuni casi dovremo ricorrere necessariamente a Opera o Firefox con le rispettive toolbar per sviluppatori.

Conclusioni

Gli evangelisti dell'accessibilità come me sono spesso accusati di essere dei reazionari, nuovi Luddisti che aborrono tutto ciò che rende il web più divertente e piacevole. Alcuni di quelli che non sono d'accordo con noi arrivano ad affermare che noi non vogliamo che Flash e Javascript vengano usati sui siti.

Spero che questo articolo provi una volta per tutte che si sbagliano. Tutto ciò che migliora l'usabilità o l'estetica è buono. Ciò su cui io e altri mettiamo in guardia è il pericolo di affidarsi a tecnologie non standard, proprietarie o dipendenti dalla piattaforma. Tutto ciò che è richiesto per accedere all'informazione sul web è uno user agent che supporta HTTP e HTML. Uno user agent di questo tipo dovrebbe essere in grado di accedere all'informazione più importante su qualunque sito realizzato con professionalità.

La graceful degradation, e soprattutto il progressive enhancement, rendono possibile tutto ciò. È una cosa che richiede un po' di lavoro in più, ma ne vale sicuramente la pena.

Ti consigliamo anche