Questa è la traduzione dell'articolo A Guide to unobtrusive JavaScript Validation di Chris Campbell, pubblicato originariamente su Particletree il 21 giugno 2005. La traduzione viene qui presentata con il consenso dell'editore e dell'autore.
Recentemente ho postato un tutorial sulla validazione in AJAX e un lettore ha scritto un grande commento sul modo in cui la validazione client-side in Javascript dovrebbe essere non intrusiva per separare il livello dell'interazione (behavior) da quello del form. Ho subito pensato: "Bello! Ci darò un'occhiata e implementerò questa cosa!".
Bene, si dà il caso che il mondo del Javascript non intrusivo sia una specie di gigantesco vaso di Pandora pieno di problemi. E il problema più grande è questo: come far conoscere al JavaScript (usando solo un markup pulito) i differenti metodi che si devono usare su ciascun campo del form. Nel mio metodo intrusivo, si può vedere chiaramente che sto comunicando allo script se un campo è richiesto o no attraverso una funziona chiamata validateMe
sull'evento onblur
:
<form name="form1" id="form1" method="post"
action="formvalidation1.asp?validationtype=asp" onSubmit="return validate();">
<input type="text" name="name" tabindex="1" id="name"
onblur="validateMe(this.value, 'req_y/name', 'namemsg');"/>
Se siete consapevoli delle questioni che riguardano la separazione tra struttura e interazione (behavior), sapete che tutto quello che vedete qui sopra è piuttosto brutto. Certo, potremmo usare un generico 'attachevent' come facciamo sui rollover di immagini, ma non saremmo in grado di specificare dinamicamente più tipi di validazione per ciascun campo, cosa che è ormai uno standard nella progettazione di form complessi. Adottare regole universali di validazione per tutti i campi non è una buona idea e non rappresenta un approccio valido se si desidera riusare quelle regole in diversi progetti. A volte non vogliamo che un certo campo sia obbligatorio, ma magari vogliamo sempre verificare che un campo sia un indirizzo email ben formattato, e così via. Quello che vogliamo, insomma, è un JavaScript che sia flessibile, pulito e non intrusivo.
Per vostra fortuna, ho pensato di fare io per voi il lavoro sporco. Oggi vedremo tre differenti esempi funzionanti di validazione di form non intrusiva con JavaScript. Oltre a discutere i pro e i contro di ciascun metodo di validazione, fornirò il codice, pronto da scaricare e usare nei vostri progetti. Bello, no?
Il codice
Prima di proseguire, potete scaricare gli esempi o vederli direttamente qui:
- Validazione JavaScript non intrusiva che fa uso di campi nascosti
- Validazione JavaScript non intrusiva che fa uso di classi multiple
- Validazione JavaScript non intrusiva che fa uso di attributi e DTD personalizzati
Usare campi nascosti
Il primo metodo ci è fornito da onlinetools.org ed usa campi nascosti per registrare i requisiti di validazione. Ho modificato un po' lo script per renderlo più versatile. Ecco come dovrebbe essere il markup HTML su un tipico form:
<input type="hidden" name="required" id="required"
value="name,emailaddress" />
<input type="hidden" name="additional" id="additional"
value="emailaddress/email,zipcode/zip" />
<input type="text" name="name" tabindex="1" id="name" />
<input type="text" name="zipcode" tabindex="2" id="zipcode" />
<input type="text" name="emailaddress" tabindex="2" id="emailaddress" />
Nell'esempio, il primo campo nascosto ci dice che i valori 'name' e 'emailaddress' sono obbligatori secondo il loro id. Il secondo che 'emailaddress' deve essere in un formato di indirizzo email valido e che 'zipcode' deve essere anche lui in un formato valido di codice ZIP (il codice ZIP corrisponde al nostro CAP, n.d.t).
Il primo campo nascosto contiene gli id dei campi obbligatori separati da virgole. Il secondo campo nascosto è usato per contenere le informazioni di validazione relative a quei campi che richiedono una validazione aggiuntiva oltre al fatto di essere obbligatori (per esempio un indirizzo email valido). Passiamo questa informazione al JavaScript inserendo gli id dei campi seguiti da una slash (/) e dal modo secondo cui saranno validati.
Il mio dubbio rispetto a questo metodo è che pur fornendo un markup migliore, non sembra tanto elegante. Confonde solo il provare a interpretare quello che fa. Basta inserire un sizelimit="100"
e tutto diventa ancora più complicato. Sì, questo metodo non è intrusivo, ma non sembra scalare molto bene (immaginate come si presenterebbe il codice del campo nascosto su un form molto complesso e con più tipi di validazione). Questo metodo tende insomma a diventare poco gestibile su form molto lunghi e sembra piuttosto suscettibile di indurre in errore chi lo crea.
Usare l'attributo 'class'
Un altro metodo usa l'attributo class
per registrare i nostri requisiti di validazione. Ecco come sarebbe il markup HTML del nostro form:
<input type="text" name="name" tabindex="1" id="name" class="validate required name namemsg"/>
<input type="text" name="nick" tabindex="2" id="nick" class="validate notrequired none nickmsg"/>
<input type="text" name="email" tabindex="2" id="email" class="validate required email emailmsg" />
Come si vede, è piuttosto semplice aggiungere diversi livelli di validazione ad ogni campo. Il JavaScript verifica i nomi delle classi e trova facilmente le sue variabili perché sono separati da spazi. Questo metodo è decisamente di più facile lettura per un essere umano e anche semplice da implementare. Certo, il JavaScript diventerebbe un po' pesante se ci fosse necessità di un requisito sulla lunghezza massima, ma per il resto va bene.
Funziona perché il W3C ci consente di definire più proprietà nel contesto di un attributo class
:
Questo attributo assegna un nome di classe o un insieme di nomi di classe ad un elemento. Un qualsiasi numero di elementi può essere assegnato allo stesso nome o agli stessi nomi di classe. Una successione di nomi di classe deve essere separata con caratteri spazio bianco.
Dovremmo però porci una domanda: è questo un uso appropriato dell'attributo class
? La nostra pagina diventa più confusa e perde un po' del suo significato se alcuni classi definiscono il modo in cui appare e altre il modo in cui essa interagisce con l'utente? Le classi sono in genere legate alla presentazione di una pagina web. Nell'esempio visto qui sopra noi stiamo invece passando informazioni sul come vogliamo che si comportino i campi del form.
Se consideriamo quella che è l'autorità in questo ambito, il W3C suggerisce di usare le classi tenendo in mente la semantica:
Pensa al perché tu vuoi che un certo elemento appaia in quel modo, non a come esso dovrebbe apparire. L'aspetto può sempre cambiare, ma la ragione per cui si dà un certo aspetto ad un elemento rimane la stessa.
Nomi di classe come 'submenu' o 'warning' descrivono il contenuto. Un nome di classe come 'sfondorosa', invece, potrebbe non descrivere in modo appropriato il contenuto quando e se le cose cambieranno nel futuro. Ora, 'validate' non è certo pessimo come nome rispetto a 'sfondorosa', ma è tuttavia appropriato? Chi può dirlo.
È un ottimo metodo questo, ma ogni sviluppatore dovrebbe valutare bene e decidere se questo è un uso accettabile delle classi.
Creare attributi e DTD personalizzati
Se non volete inserire 'validate required email' nei nomi delle classi, potrete sempre aggiungere i vostri attributi personalizzati ai campi del form e usare una DTD personalizzata per validare comunque la pagina su cui li userete.
L'idea è stata divulgata da Peter Paul Koch su A List Apart e sul suo sito Quirksmode.
Il markup del nostro form sarebbe questo:
<input type="text" name="name" tabindex="1" id="name" required="true" message="namemsg"/>
<input type="text" name="zipcode" tabindex="2" id="zipcode" validate="zip" message="zipcodemsg"/>
<input type="text" name="emailaddress" tabindex="2" id="emailaddress" required ="true" validate="email" message="email"/>
Questo è davvero un bel form. Niente campi nascosti, niente giochi con i nomi delle classi. Se un campo è obbligatorio aggiungiamo un required="true"
. Se bisogna fare un'ulteriore validazione aggiungiamo una cosa tipo validazione="tipo_di_validazione"
. L'attributo message
, poi, ci dice dove sarà mostrato il messaggio di feedback. Molto semplice da usare, molto semplice da leggere.
Per validare il tutto secondo le specifiche, prendiamo la DTD del W3C e inseriamo queste righe:
<!ATTLIST input required (true|false) #IMPLIED>
<!ATTLIST input validate (email|zip|phone|none) #IMPLIED>
<!ATTLIST input message (zipcodemsg|emailaddressmsg|namemsg) #IMPLIED>
Quindi colleghiamo la nostra DTD personalizzata alla nostra pagina, con questa dichiarazione del doctype:
<!DOCTYPE html SYSTEM "http://www.particletree.com/examples/unobtrusive-validation/dtd/xhtml11-custom.dtd">
Prima di prendere il codice e provarlo, cerchiamo di imparare qualcosa in più su quello stiamo facendo. Una DTD o Document Type Definition è quello che inseriamo all'inizio delle nostre pagine web per aiutare i browser a determinare la lista degli elementi che è legittimo usare nelle pagine stesse. In genere è così:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
La DTD definita qui sopra, per esempio, ci dice che l'elemento (tag) <p>
è valido mentre non lo è <newparagraph>
. Quando validiamo una pagina, il codice viene verificato rispetto a quella DTD. Dal momento che required="true"
è un attributo che noi usiamo per i nostri campi di testo, la DTD di sopra non considera valido il nostro markup. Creando una DTD personalizzata con nuovi attributi legittimi e validi, noi possiamo tranquillamente usare required="yes"
oppure validate="zip"
.
Tecnicamente, però, non abbiamo bisogno di cambiare una DTD perché tutto funzioni. La pagina funzionerà bene aggiungendo attributi personalizzati e JavaScript può usare getAttribute()
per accedere a quei valori. Cambiamo la DTD altrimenti la pagina non è valida, una cosa che è piuttosto disdicevole, come ho potuto notare. Per saperne di più sulla creazione di DTD personalizzate, c'è un buon articolo su A List Apart.
Ora, una DTD personalizzata può essere utile se vogliamo rintracciare un problema in fase di programmazione ed avviene un errore. Se la pagina è valida rispetto ad una DTD (anche la nostra) possiamo attribuire l'errore a JavaScript e non preoccuparci del fatto che possa essere un errore nel markup XHTML. Dal momento che il W3C raccomanda di usare le sue DTD per la validazione, le DTD personalizzate non saranno considerate corrette dal validatore del W3C perché il loro lavoro è quello di creare standard a cui tutti dovrebbero conformarsi. Si può vedere la lista delle DTD legali del W3C qui. Per verificare la validità di una pagina rispetto ad un doctype personalizzato, si può usare il validatore di YoYo Design.
Purtroppo, c'è un lato oscuro in tutto ciò. Anche se tecnicamente possiamo creare le nostre DTD, così facendo rendiamo il web un luogo piuttosoto confuso e rendiamo praticamente inutile la presenza di un W3C che crea standard per lo sviluppo web. Inoltre, una pagina così fatta non sarebbe a prova di futuro. Le cose potrebbero infatti andare per il peggio se un produttore di browser o il W3C decidessero un giorno che required="true" significa un'altra cosa. Se siete curiosi di approfondire l'argomento DTD e volete pensare con lo sguardo volto al futuro, leggete qualcosa sui namespaces.
Ora che si tratta di trarre le conclusioni, non è facile dire quale dei tre metodi visti nel corso dell'articolo sia il migliore. Personalmente, preferisco il metodo basato sulle classi per la validazione dei miei form. La soluzione è un buon bilanciamento rispetto alle funzionalità che cerco ed è piuttosto versatile. E penso che il sacrificio rispetto alla semantica sia roba di poco conto. Anche se gli attributi personalizzati e le DTD ad hoc hanno un certo appeal e sono roba da veri nerd, creare una DTD personalizzata non è cosa che digerisco molto, specialmente se penso che il W3C lavora sodo per fornirmi validi strumenti di cui ho bisogno per creare un sistema di validazione efficace e riusabile.
Approfondimenti
Questi articoli e qusti siti sono stati grandi risorse nella concezione e nella stesura di questa guida: