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

Le 7 regole del Javascript non intrusivo

I consigli di Christian Heilmann per avere codice più pulito, usabile e facile da mantenere senza sacrificare le funzionalità del sito
I consigli di Christian Heilmann per avere codice più pulito, usabile e facile da mantenere senza sacrificare le funzionalità del sito
Link copiato negli appunti

Questa è la traduzione dell'articolo The seven rules of Unobtrusive JavaScript di Christian Heilmann. Come l'originale, è pubblicato e distribuito su HTML.it con una Licenza Creative Commons.

Negli ultimi anni, sviluppando, insegnando e implementando un linguaggio come Javascript, ho elaborato le regole che seguono a proposito del Javascript non intrusivo. Nello specifico, quella che segue è la traccia di un intervento che ho tenuto nel corso di un workshop per la conferenza Paris Web 2007 tenutasi a Parigi.

Spero che possano aiutarvi a comprendere perché è una buona idea pianificare e implementare Javascript in maniera non intrusiva. Questo modo di procedere mi ha aiutato a rilasciare i miei lavori più velocemente, con una qualità più elevata e con una maggiore facilità nella manutenzione del codice.

1. Non fate supposizioni

Probabilmente la più importante caratteristica del Javascript non intrusivo è che ti fa smettere di fare supposizioni:

  • Non devi supporre e aspettarti che Javascript sia disponibile; piuttosto va implementato come se fosse un'utile aggiunta.
  • Non devi aspettarti che i browser supportino certi metodi o certe proprietà: devi testarle prima di renderle disponibili.
  • Non devi aspettarti che l'HTML corretto sia a tua disposizione, ma verifichi che lo sia e se non lo è non fai nulla.
  • Mantieni le funzionalità che implementi indipendenti dal dispositivo con cui si accede al sito.
  • Valuti che altri script possono interferire con queste funzionalità e rendi quanto più sicuro possibile lo scopo dei tuoi script.

La prima cosa da considerare prima di iniziare a progettare uno script è studiare bene la parte HTML che si intende attivare e verificare cosa si può usare per raggiungere l'obiettivo che ci siamo prefissi.

2. Trovare agganci e relazioni

Prima di lavorare su uno script bisogna guardare alla base da cui si parte. Se l'HTML non è ben strutturato non c'è un modo facile per creare una buona soluzione di scripting. Molto probabilmente, o si creerà troppo markup con Javascript, o l'applicazione dipenderà troppo da Javascript.

Ci sono diverse cose da considerare nel markup HTML. Le prime due sono agganci e relazioni.

Agganci nell'HTML

Gli agganci nel contesto del codice HTML sono innanzitutto gli ID degli elementi. Ad essi si può accedere tramite il più veloce dei metodi DOM, getElementById. Gli ID sono un eccellente e sicuro punto d'appoggio fin quando sono definiti in maniera univoca all'interno di un documento HTML valido (IE ha un bug relativo agli attributi name e id, ma le migliori librerie Javascript riescono a superarlo). Inoltre, gli ID sono facili da testare.

Altri agganci sono gli elementi HTML che possono essere intercettati tramite il metodo getElementsByTagName e le classi CSS. Queste ultime non possono essere intercettate con un metodo DOM nativo in tutti i browser, ma ci sono molti metodi alternativi per che consentono di sfruttare getElementsByClassName.

Relazioni nell'HTML

L'altra cosa interessante rispetto all'HTML è la presenza di relazioni nel contesto del markup. Ci si potrebbe porre queste domande:

  • Come posso raggiungere questo elemento nel modo più veloce e con meno passi nell'attraversamento del DOM?
  • Su quale elemento ho bisogno di intervenire per raggiungere facilmente gli elementi figli che devo cambiare?
  • Quali attributi o informazioni possiede un certo elemento che possa sfruttare per collegarlo ad un altro?

Attraversare il DOM ha dei costi e può essere un'operazione lenta. Ecco perché è una buona idea lasciarlo ad una tecnologia che è già in uso sul browser.

3. Lasciare l'attraversamento del DOM agli 'esperti'

È una cosa piuttosto interessante che lo scripting del DOM e l'attraversamento dello stesso DOM con i suoi metodi e proprietà (getElementsByTagName, nextSibling, previousSibling, parentNode, etc) sembri essere motivo di confusione per diverse persone. È interessante dal momento che in fondo facciamo la stessa cosa con un'altra tecnologia: i CSS.

I CSS sono una tecnologia che prende un selettore CSS e attraversa il DOM per accedere ad uno specifico elemento e cambiarne le sue caratteristiche visuali. Una porzione di Javascript piuttosto complessa può essere facilmente essere sostituita con un solo selettore CSS.

var n = document.getElementById('nav');
if(n){
var as = n.getElementsByTagName('a');
if(as.length > 0){
for(var i=0;as[i];i++){
as[i].style.color = '#369';
as[i].style.textDecoration = 'none';
}
}
}

è la stessa cosa di

#nav a{
color:#369;
text-decoration:none;
}

È davvero un aiuto utilissimo e su tecniche di questo tipo si può sempre contare. Per esempio assegnando dinamicamente classi agli elementi che sono in alto nella gerarchia nel DOM oppure alterando gli ID. Se semplicemente si aggiunge una classe al body del documento usando il DOM è possibile offrire al designer la possibilità di definire sia la versione statica sia quella dinamica del documento:

JavaScript:
var dynamicClass = 'js';
var b = document.body;
b.className = b.className ? b.className + ' js' : 'js';

CSS:
/* static version */
#nav {
....
}
/* dynamic version */
body.js #nav {
....
}

4. Capire i browser e gli utenti

Una parte davvero importante del Javascript non intrusivo consiste nel comprendere il modo in cui i browser funzionano (e soprattutto il modo in cui sbagliano...) e nel comprendere cosa gli utenti si aspettano che accada. È facile superare i limiti con Javascript e creare un'interfaccia completamente nuova o diversa. Interfacce con il drag&drop, sezioni della pagina che possono essere mostrate e nascoste con un click, scrollbar e slider possono essere creati con Javascript. Ma al di là degli aspetti tecnici dell'implementazione, c'è altro. Dovremmo sempre chiederci:

  • La mia nuova interfaccia funzionerà in maniera indipendente dal dispositivo? E se non funziona, come dovrebbe essere la versione alternativa che offro agli utenti?
  • La nuova interfaccia che sto costruendo sta seguendo le regole del browser o quelle delle interfacce più ricche e complesse da cui deriva (si può navigare in un menu a più livelli con il cursore del mouse o abbiamo bisogno di spostarci con il tasto TAB?)
  • Qual è la funzionalità che ho bisogno di offrire e che dipende da Javascript?

Quest'ultima non è proprio una questione complicata: si può usare il DOM per creare elementi HTML dinamicamente nel caso in cui ciò sia necessario. Un esempio sono i link del tipo "Stampa questa pagina". I browser non offrono un modo per stampare la pagina che non si appoggi a Javascript, ecco perché link di questo tipo andrebbero creati con il DOM. Lo stesso ragionamento si può applicare ai titoli cliccabili che servono ad espandere e nascondere sezioni di contenuto. Questi titoli non possono essere attivati con la testiera, ma con i link sì. Per creare titoli cliccabili bisognerebbe usare Javascript per inserire i link nel loro contesto e allora tutto va bene. Anche chi usa la tastiera per navigare potranno espandere e nascondere tali contenuti.

Grandi risorse per trovare soluzioni a questo tipo di problema sono le gallerie di design pattern. Per sapere invece cosa funziona e cosa non funziona in base al dispositivo, l'unico aiuto è l'esperienza. Prima di tutto bosogna conoscere e capire il concetto di 'gestione degli eventi'.

5. Capire gli eventi

La gestione degli eventi è il passo successivo per arrivare a implementare un Javascript non intrusivo. Il punto non è come rendere tutto trascinabile con il drag&drop o come aggiungere eventi inline. Il punto consiste nel capire che la gestione degli eventi è vera separazione. Separiamo HTML, CSS e Javascript, ma con la gestione degli eventi andiamo molto più lontano.

Gli elementi nel documento sono lì che aspettano gestori di eventi per sentire un cambiamento che sta accadendo proprio a loro. Se ciò avviene, i gestori rintracciano un oggetto magico (normalmente sotto forma di un parametro chiamato e) che dice a loro cosa è accaduto a cosa e cosa può essere fatto.

La cosa veramente interessante rispetto alla gestione degli eventi è che essa non accade solo all'elemento che vogliamo raggiungere, ma anche agli elementi superiori nella gerarchia del DOM (non accade tuttavia a tutti gli eventi, focus e blur, per esempio, non si comportano così). Ciò consente di assegnare un singolo gestore di eventi, per esempio, ad una lista di navigazione e di usare poi i metodi per la gestione degli eventi per raggiungere gli elementi che ci interessano. La tecnica è chiamata 'event delegation' e ha diversi vantaggi:

  • Abbiamo bisogno di testare solo se esiste un singolo elemento, non tutti.
  • Possiamo dinamicamente aggiungere e rimuovere nuovi elementi figli senza rimuovere o aggiungere nuovi gestori di eventi.
  • Possiamo rispondere allo stesso evento su differenti elementi.

L'altra cosa da ricordare è che è possibile fermare la propagazione degli eventi agli elementi genitori e che si possono reimpostare le azioni di default degli elementi HTML come i link. A volte questa non è proprio una buona idea dal momento che i browser applicano tali azioni per qualche buona ragione. Un esempio è quello dei link che puntano a target presenti nella pagina. Consentire che essi possano essere seguiti fa sì che l'utente possa bookmarkare lo stato dei nostri script.

6. Comportarsi bene con gli altri

Il nostro codice sarà difficilmente l'unico script usato nel documento. È allora importantissimo far sì che il codice non abbia una funzione globale o nomi di variabili con cui gli altri script possano interferire. Ci sono diversi modi per evitare che ciò avvenga. Il metodo di base consiste nell'istanziare ogni variabile usando la parola chiave var. Supponiamo di avere questo script:

var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}

C'è una variabile globale chiamata nav e funzioni chiamate init, show e reset. Le funzioni possono accedere alla variabile e tra di loro con il nome:

var nav = document.getElementById('nav');
function init(){
show();
if(nav.className === 'show'){
reset();
}
// do stuff
}
function show(){
var c = nav.className;
// do stuff
}
function reset(){
// do stuff
}

Si può evitare tutto questo codice globale inserendolo nel contesto di un oggetto usando il letterale di oggetto, trasformando così le funzioni in metodi e le variabili in proprietà. Si devono definire i metodi e le variabili con un nome seguito dai due punti e bisogna separare l'uno dall'altro con la virgola:

var myScript = {
nav:document.getElementById('nav'),
init:function(){
// do stuff
},
show:function(){
// do stuff
},
reset:function(){
// do stuff
}
}

Ad ognuno di essi si può accedere dall'esterno e dall'interno dell'oggetto mettendo prima il nome dell'oggetto:

var myScript = {
nav:document.getElementById('nav'),
init:function(){
myScript.show();
if(myScript.nav.className === 'show'){
myScript.reset();
}
// do stuff
},
show:function(){
var c = myScript.nav.className;
// do stuff
},
reset:function(){
// do stuff
}
}

Gli svantaggi di questo pattern consistono nel fatto di dover ripetere il nome dell'oggetto ogni volta che si voglia accedere ad esso da un altro metodo e che tutto ciò che abbiamo nell'oggetto è pubblicamente accessibile. Che fare se si volesse rendere accessibile ad un altro script presente nel documento solo alcune parti?Per questo scopo è possibile usare questo pattern:

var myScript = function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
// public methods and properties wrapped in a return
// statement and using the object literal
return {
public:function(){

},
foo:'bar'
}
}();

Si può accedere alle proprietà pubbliche e ai metodi che sono restituiti allo stesso modo in cui si fa con un letterale di oggetto, in questo caso myScript.public() e myScript.foo. C'è però un'altra cosa fastidiosa: se si vuole accedere ad un metodo pubblico da un altro o da un metodo privato, è necessario ricorrere nuovamente ai lunghi nomi (il nome dell'oggetto principale può in effetti diventare molto lungo). Per evitare tutto ciò basta definirli come metodi privati e restituire solo un oggetto con sinonimi:

var myScript = function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
}
function show(){
// do stuff
// do stuff
}
function reset(){
// do stuff
}
var foo = 'bar';
function public(){

}
// return public pointers to the private methods and
// properties you want to reveal
return {
public:public,
foo:foo
}
}();

Ciò consente di avere una certa consistenza nella scrittura del codice e permette anche di scrivere sinonimi più corti quando vengono rivelati.

Se non si vuole rivelare nessun metodo o nessuna proprietà al mondo esterno, si può avvolgere tutto il blocco di codice in una funzione anonima e richiamarla subito dopo che è stata definita:

(function(){
// these are all private methods and properties
var nav = document.getElementById('nav');
function init(){
// do stuff
show(); // no need for prepended object name
}
function show(){
// do stuff
}
function reset(){
// do stuff
}
})();

Questo è un grande pattern per una funzionalità che deve essere eseguita solo una volta e non ha dipendenze su altre funzioni.

Seguendo questi consigli il nostro codice funzionerà bene per gli utenti, per la macchina su cui viene eseguito, per gli altri sviluppatori. C'è tuttavia un altro gruppo di cui tenere conto.

7. Lavorare per il prossimo sviluppatore

L'ultimo passo per rendere gli script realmente non intrusivi è pensare allo sviluppatore che dopo di noi dovrà prendersi carico della sua gestione. Dobbiamo prendere in considerazione tali questioni:

  • I nomi delle variabili e delle funzioni sono tutti logici e facili da capire?
  • Il codice è strutturato logicamente? Si può "leggerlo" dall'inizio alla fine?
  • Le dipendenze sono chiare?
  • Sono state commentate le parti che potrebbero risultare confuse?

La cosa più importante è capire che l'HTML e il CSS in un documento possono cambiare con più probabilità del Javascript. Dunque è una buona idea non avere nessun nome di classe e di ID o stringhe che saranno mostrate all'utente finale seppellite da qualche parte nel codice. Meglio separarle all'interno di un oggetto di configurazione:

myscript = function(){
var config = {
navigationID:'nav',
visibleClass:'show'
};
var nav = document.getElementById(config.navigationID);
function init(){
show();
if(nav.className === config.visibleClass){
reset();
};
// do stuff
};
function show(){
var c = nav.className;
// do stuff
};
function reset(){
// do stuff
};
}();

In questo modo chi deve mantenere il codice sa esattamente cosa cambiare e come cambiarlo senza dover alterare il resto del codice.


Ti consigliamo anche