laboratorio caffeina

in few words, just developing

 

Tag: javascript

 
 

Volevo farvi un appunto. Anzi, una @Annotazione

Intanto chiedo scusa per la mia assenza, vedrò di farmi perdonare:-)

Proseguendo da dove ero rimasto l'altra volta, ho controllato un po' in giro come le librerie JavaScript più diffuse trattano l'argomento della validazione dei form. Ho visto che l'unica cosa che hanno in comune è che ognuno la fa in modo differente dagli altri...

Ad esempio qui c'è la validazione dei campi di testo secondo Spry.

Volevo però fermarmi ad analizzare come avviene la validazione nella mia libreria preferita, ovvero jQuery. Ecco un esempio di validazione con il jQuery Validation Plugin.

Guardiamo un po' il sorgente: come posso indicare che un valore in un campo è sempre richiesto ed ha lunghezza massima 2?

HTML:
  1. <input type="text"
  2. class="{required: true, maxLength: 2}">

Argh... va be che anch'io avevo dettto che l'attributo class può essere utilizzato inserire delle annotazioni, ovvero delle informazioni "nascoste" associate ai nodi HTML, da usare poi per la validazione. Ma qui mi sembra che lo strumento venga utilizzato un po' disinvoltamente:-)

(Niente da ridire, in verità, anche io sono uno di quelli a cui piace ogni tanto sfruttare dei trucchetti..)

Però io suggerirei di inserire le annotazioni in un modo un po' diverso. Ovvero in un modo in cui nessun validatore, nessun esaminatore, nessun censore potrà mai avere a che ridire sul modo in cui queste annotazioni siano state inserite.

Ecco qua:

HTML:
  1. <!-- type: "integer", maxLength: 2
  2. --><input type="text">

Ecco fatto, tutto qua. Come si vede ho inserito le informazioni direttamente all'interno di un commento posto immediatamente prima del nodo a cui si riferiscono. Inoltre in questo modo posso costruire delle annotazioni di arbitraria complessità, su qualunque nodo HTML, e naturalmente non solo per la validazione. Perché semplicemente all'interno del commento è presente un frammento di testo JSON (senza le graffe) e così potrei scrivere, ad esempio:

HTML:
  1. <!-- id: 1020, type: "integer",
  2.      children: [101, 102, 103],
  3.      coords: {
  4.          x: 100,
  5.          y: 200
  6.      }
  7. --><input type="text" id="text1>

Qui ho specificato un campo "id" contenente un valore intero (magari letto da un database), che a questo campo sono legati in qualche modo degli altri nodi "children" (ad esempio per indicare che se uno solo di questi nodi è compilato devo compilare poi anche questo) e che associato al campo ho delle coordinate che andrò a gestire in un certo modo.

Come faccio poi via JavaScript per utilizzare questi dati "nascosti"? Con una bella funziona apposita:

JavaScript:
  1. function Annotation(node)
  2. {
  3.     // se è un nodo
  4.     if (node && node.previousSibling)
  5.     {
  6.         // cerco il primo commento precedente
  7.         for
  8.         (
  9.             node = node.previousSibling;
  10.             node && node.nodeType != 8;
  11.             node = node.previousSibling
  12.         )
  13.             ;
  14.         // se ho trovato il commento
  15.         if (node)
  16.         {
  17.             var ann = null;
  18.             try { eval("ann = {" + node.nodeValue + "}"); }
  19.             catch (e) { }
  20.             for (var item in ann)
  21.                 this[item] = ann[item];
  22.         }
  23.     }
  24. }

Che andrò poi ad utilizzare ad esempio così:

HTML:
  1. <!-- required: true, maxLength: 2
  2. --><input type="text" onclick="clicked(this);">
  3. <script type="text/javascript">
  4. function clicked(node)
  5. {
  6.     var ann = new Annotation(node);
  7.     alert("required = " + ann.required + ", maxLength = " + ann.maxLength);
  8. }
  9. </script>

 
 
 

Che stile, che classe!

Sto arrovellandomi per trovare, tra le tante soluzioni, quale sia la più elegante per risolvere il problema della validazione dei form.

Il problema è sempre lo stesso. Come indicare che un campo è obbligatorio, oppure deve contenere un numero intero, oppure una textarea deve avere al massimo un certo numero di caratteri?

Be' se fossimo in un mondo perfetto la soluzione ci sarebbe già, e si chiamerebbe HTML 5. Guardate ad esempio cosa hanno pensato bene quelli del WHATWG nella specifica Web Forms 2.0 sull'argomento validazione. Però in un mondo perfetto non siamo...

Ad esempio per specificare un campo obbligatorio cosa c'è di meglio di un attributo "required"? Poi basterebbe l'utilizzo di una semplice funzione JavaScript per controllarne la correttezza, come descritto per esempio nell'articolo JavaScript triggers di Peter-Paul Koch.

E allora il problema qual'è? La soluzione sembra già a portata di mano. Ebbene il problema è il seguente: la specifica HTML attuale, con l'ausilio dei famigerati DTD HTML, non permettono l'introduzione di attributi "alieni" all'interno di un elemento HTML.

Questo si guardi bene potrebbe essere contestato. Che fastidio potrebbe dare un attributo in più se gli attributi necessari fossero già presenti? Semplicemente il browser che non ne capisca il significato potrebbe scartare l'attributo, come già fanno adesso tutti i browser creati fino a questo momento!

Però il problema rimane. Una buona soluzione deve funzionare anche in caso di necessità di validazione HTML. Bisognerebbe trovare una soluzione di classe... che dico di classe, di "class"!

Vediamo come potremmo fare:

HTML:
  1. <input type="text" class="required integer">

Vi sembra brutto? Voi mi direte: l'attributo "class" serve ad altro, non è corretto utilizzarlo in questo modo. E invece io dico: cosa vuol dire "appartenere ad una classe"? Non significa che questa classe debba avere soltanto delle implicazioni sul modo in cui l'elemento viene visualizzato. Anzi è consigliato che il nome della classe ne sappresenti il significato e non l'aspetto. Ad esempio classe "errore" e non classe "rosso", perché magari un errore sarà sempre mostrato in rosso ma la classe ne deve indicare il significato e non l'aspetto, che devono rimanere indipendenti.

E qui volevo arrivare! Utilizzare una classe per indicare una caratteristica strutturale dell'elemento non va in conflitto con il fatto di specificare come quell'elemento deve essere visualizzato. Ad esempio io potrei volere che tutti i campi obbligatori abbiamo lo sfondo giallo, mentre tutti i campi interi abbiano un bordo rosso:

CSS:
  1. input.required { background-color: yellow; }
  2. input.integer { border: solid 1pt red; }

E nello stesso tempo una funzione JavaScript potrebbe controllarne la validazione:

JAVASCRIPT:
  1. function validate(node)
  2. {
  3.     try
  4.     {
  5.         if (containsClass(node, "required"))
  6.             checkRequired(node);
  7.         if (containsClass(node, "integer"))
  8.             checkInteger(node);
  9.         alert("ok");
  10.     }
  11.     catch (e)
  12.     {
  13.         alert(e.message);
  14.     }
  15. }
  16. function checkRequired(node)
  17. {
  18.     if (!node.value)
  19.         throw { message: "valore mancante", target: node };
  20.     return null;
  21. }
  22. function checkInteger(node)
  23. {
  24.     if (node.value && isNaN(parseInt(node.value, 10)))
  25.         throw { message: "non è un intero", target: node };
  26.     return null;
  27. }
  28. function containsClass(node, className)
  29. {
  30.     return node.className.indexOf(className)>= 0;
  31. }

Si veda bene che questo codice non vuol essere lo script migliore che si possa scrivere per gestire la validazione di un campo, ma solo un esempio su come semplice possa essere l'utilizzo di questo sistema. E le soluzione più semplici (a volte) sono le migliori:-) Questa come vi pare?

Naturalmente questo è solo l'inizio...

 
 
 

News Scroller

Ecco un piccolo esempio in cui si applicano le proprietà  presentate in Scroll boxes. Si tratta di una possibile visualizzazione di news a scorrimento automatico in un blocco DIV. Niente di innovativo e tanto meno completo, ma può considerarsi uno buono spunto per esercitarsi con javascript, css e xhtml ( AJAX usando una sigla audace ).

Primo step, HTML formato da un contenitore generale un wrapper che servira' come contenitore delle news e quindi le singole news:

HTML:
  1.     <div>
  2.         <div>
  3.             <h3>Title 1</h3>
  4.             <p>Lorem ipsum doler sit amet, consectetur...</p>
  5.         </div>
  6.         <div>
  7.             <h3>Title 2</h3>
  8.             <p>Lorem ipsum doler sit amet, consectetur...</p>
  9.         </div>
  10.         <div>
  11.             <h3>Title 3</h3>
  12.             <p>Lorem ipsum doler sit amet, consectetur...</p>
  13.         </div>
  14.     </div>
  15. </div>

Secondo step,CSS che nasconde l'overflow delle news:

CSS:
  1. #container{
  2.     margin: 20px;
  3.     width: 200px;
  4.     height: 200px;
  5.     /*Nasconde la parte in eccesso*/
  6.     overflow: hidden;
  7.     border: 1px solid #000
  8. }
  9.  
  10. div.news{
  11.     padding: 10px;
  12. }

Ultimo step, Javascript per dare moto al tutto:

JavaScript:
  1. //dichiarazione variabili globali
  2. var time;
  3. var scroll_limit;
  4.  
  5. //Alcune impostazioni fondamentali pre caricamento pagina
  6. function init(){
  7.     var container = document.getElementById("container");
  8.     var wrapper = document.getElementById("wrapper");
  9.     var client_height = container.offsetHeight;
  10.     var news_nodelist = wrapper.getElementsByTagName("DIV");
  11.        
  12.     /**
  13.      * Lo scroller deve resettarsi quando gli attuali
  14.      * elementi news sono stati tutti scorsi.
  15.      **/
  16.     scroll_limit = wrapper.offsetHeight;
  17.     var i = 0;
  18.     var current_element = news_nodelist[i];
  19.  
  20.     /**
  21.      * Si duplicano i nodi in modo da creare l'illusione
  22.      * dello scorrimento continuo. Si assume che ci siano
  23.      * abbastanza nodi (news) da duplicare.
  24.      **/
  25.     while(client_height&gt;= 0){
  26.         var current_element = news_nodelist[i];
  27.         client_height = client_height - current_element.scrollHeight;
  28.         wrapper.appendChild(current_element.cloneNode(true));
  29.         i = i + 1;
  30.     }
  31.     scroll("wrapper")
  32. }
  33. function scroll(element_id){
  34.     var wrapper = document.getElementById(element_id);
  35.     var container = wrapper.parentNode;
  36.     var scroll_top = container.scrollTop;
  37.     if( scroll_top + 1 &lt;scroll_limit ){
  38.         container.scrollTop = container.scrollTop + 1;
  39.     }else{
  40.         container.scrollTop = 0;
  41.     }
  42.     // Si richiama la funzione tra 25 millisecondi
  43.     time = setTimeout('scroll(\'wrapper\')', 25);
  44. }
  45.  
  46. function stopScroll()
  47. {
  48.     if (time) clearTimeout(time);
  49. }

Ovviamente la funzione init() deve essere richiamata al termine del caricamento della pagina HTML, ora sta a voi raffinare questo rozzo esempio di News Scroller.

Testato su:
Firefox 2.0.0.3 (Linux)
Mozilla 1.7.13 (Linux)
Epiphany 2.16.1 (Linux)
Galeon 2.0.2 (Linux)
IE 6.0 (Linux)

 
 
 

Scroll boxes

Il titolo forse non è dei più immediati, ma vorrei presentare delle proprietà degli elementi HTML utilizzabili con Javascript per creare degli scroller personalizzabili o automatici ( classico box delle news a scorrimento continuo ) o quant'altro preveda il movimento di un blocco scrollabile.

Innanzitutto presentiamo le proprietà  degli elementi HTML che utilizzeremo negli script.

clientHeight
Proprietà in sola lettura che ritorna il numero di pixels dell'altezza interna di un elemento, incluso il padding ma escluse l'altezza della scrollbar, del bordo o del margine.

JavaScript:
  1. var element = document.getElementById(id);
  2. var height = element.clientHeight;

clientWidtht
È il corrispettivo di clientHeight ma per la larghezza interna di un elemento.

JavaScript:
  1. var widht = element.clientWidtht;

scrollHeight
È una proprietà  in sola lettura che restituisce il numero di pixels dell'altezza di un elemento relativa al layout, quindi comprensiva di padding e bordi.

JavaScript:
  1. var scrollHeight = element.scrollHeight;

scrollWidth
È il corrispettivo di scrollHeight ma riguardante la larghezza un elemento.

JavaScript:
  1. var scrollWidth = element.scrollWidth;

scrollTop
È una proprietà  in lettura e scrittura che può assumere un valore qualsiasi intero positivo rappresentate la distanza in pixel tra il lato superiore e la prima parte visibile dell'elemento countenuto.

JavaScript:
  1. var scrollTop = element.scrollTop;

scrollLeft
È il corrispettivo di scrollHeight ma riguardante la larghezza un elemento.

JavaScript:
  1. var scrollLeft = element.scrollLeft;

Schema riassuntivo
Schema riassuntivo

Note: le proprietà presentate sino ad ora sono state introdotto dal object model di MSIE, quindi non sono nè una specifica nè una raccomanandazione W3C. In ogni caso sono implementate anche dal render Gecko ( Firefox, Mozilla, Netscape, etc. )

Queste proprietà si applicano a numerosi elementi, per avere maggiori dettagli consultate pure la MSDN DHTML library

 
 
 

Form highlighting

Dopo l'autofocus, altro script a mio parere utilissimo è quello dell'highlighting degli input field e delle textarea, ovvero un semplice meccanismo javascript + css che permette di evidenziare il campo della form attualmente in uso. Vi propongo una funzione sviluppata in maniera che sia abbastanza adattabile ai diversi casi.

JavaScript:
  1. window.onload = function(){
  2.   /*applico l'highlight a tutti i campi di input*/
  3.   var inputFields = document.getElementsByTagName("INPUT");
  4.   for(i=0; i&lt;inputFields.length; i++){
  5.     var field = inputFields.item(i);
  6.     if ( field != undefined){
  7.       if ( field.type=="text" || field.type=="password" ){
  8.         /*onfocus cambio la classe corrente con quella focus*/
  9.     field.onfocus = function(){this.className="focus";}
  10.         /*onblur ripristino la classe di default*/
  11.     field.onblur = function(){this.className="";}
  12.       }
  13.     }
  14.   }
  15.   /*stesso vale per le textarea*/
  16.   var textareas = document.getElementsByTagName("TEXTAREA");
  17.   for(i=0; i&lt;textareas.length; i++){
  18.     var textarea = textareas.item(i);
  19.     textarea.onfocus = function(){this.className="focus";}
  20.     textarea.onblur = function(){this.className="";}
  21.   }
  22. }

Chiaramento a questo script va assocciato un opportuno foglio di stile con almeno una classe focus.

CSS:
  1. input.focus{
  2.   border: 1px solid #a00;
  3. }
  4. textarea.focus{
  5.   border: 1px solid #a00;
  6. }
  7. input, textarea{
  8.   padding: 1px;
  9.   background-color: #eee;
  10.   border: 1px solid #999;
  11. }

Visto che sostenere l'utilità di questi script può essere discutibile, vi propongo un esempio completo di una tipica form di contatti.

N.B: Questi script non trattano campi del tipo checkbox, ma in quel caso bisognerebbe studiare una soluzione ad hoc che evidenzi l'intera area contente tutti i radio button o checkbox e non il singolo, poichè la check box o i radio button sono uno o più valori per un campo.

Testato su:
Firefox 2.0.0.3 (Linux)
Mozilla 1.7.13 (Linux)
Epiphany 2.16.1 (Linux)
Galeon 2.0.2 (Linux)
IE 6.0 (Linux)

 
 
 

Autofocus e form usability

Leggendo l'articolo di Diego, Accessibilità  ed usabilità  nelle form, mi sono risorte in mente alcune questioni sulle form che reputo interessanti.

Prima fra tutte l'autofocus, uno script muove il cursore dell'utente direttamente su uno specifico campo di input al caricamento della pagina, come avviene in Google. Per il form proposto nell'articolo sopra citato lo script sarebbe:

JavaScript:
  1. /*window.onload equivale all'evento onload del tag body*/
  2. window.onload = document.forms[0].s.focus();

I problemi sorgono in quelle pagine in cui convivo più forms, spesso per esempio una form di login e una di ricerca. Capita quindi che l'utente vorrebbe autenticarsi, lo script sposta il cursore sul campo di ricerca e l'utente finito di digitare lo username compone la password, così oltre alla perdita di tempo si aggiunge magari la vista in chiaro della password nel campo di ricerca. Per risolvere questo problema riporto uno script che ho ritoccato in modo da renderlo più generico.

JavaScript:
  1. window.onload=function(){
  2.   var isfocused = false;
  3.   /*ottengo tutti i tag input della pagina HTML*/
  4.   var inputFields = document.getElementsByTagName("INPUT");   
  5.   /*scorro con un ciclo for ogni elemento della nodeList*/
  6.   for(i=0; i&lt;inputFields.length; i++){
  7.   var current = inputFields.item(i);
  8.     /*considero solo gli elementi definiti e di tipo text*/
  9.     if(current != undefined){
  10.       if(current.type == "text" || current.type == "password")
  11.       {
  12.         /*controllo se nessun valore è stato cambiato*/
  13.         if(current.value != current.defaultValue){
  14.           isfocused = true;
  15.         }
  16.       }
  17.     }
  18.   }
  19.   var textarea = document.getElementsByTagName("TEXTAREA");   
  20.   for(i=0; i&lt;textareas.length; i++){
  21.     var current = textareas.item(i);
  22.     if(current !== undefined){
  23.       if(current.value != current.defaultValue){
  24.         isfocused = true;
  25.       }
  26.     }
  27.   }
  28.   /*se nessun valore di default cambia scatta l'autofocus*/
  29.   if(!isfocused){
  30.     inputFields.item(0).focus();
  31.   }
  32. }

Testato su:
Firefox 2.0.0.3 (Linux)
Mozilla 1.7.13 (Linux)
Epiphany 2.16.1 (Linux)
Galeon 2.0.2 (Linux)
IE 6.0 (Linux)

 
 
 

Introduzione DOM e Javascript

La sinergia DOM e Javascript, tecnologie alla base di AJAX (Asynchronous JavaScript and XML), insieme ai fogli di stile permette la creazione di interfacce utente Web interattive e accattivanti, vediamo quindi di introdurne i principi fondamentali.

DOM
Il Document Object Model (DOM) è un insieme di interfacce di programmazione (API) che forniscono un modello strutturato per documenti XML e dialetti derivati, come XHTML (ovviamente anche HTML), nata dal lavoro del World Wide Web Consortium (W3C).

Prendiamo una pagina HTML può essere vista in un browser o il rispettivo codice sorgente in un editor di testo, in entrambi i casi è un documento a cui il DOM fornisce una rappresentazione equivalente fatta di nodi e oggetti ai quali attribuisce proprietà  e metodi.

Consideriamo un paragrafo di una ipotetica pagina HTML:

HTML:
  1. <p align="center">
  2.    <strong>Lorem</strong> ipsum dolor sit amet...
  3. </p>

può essere rappresentato con il seguente albero DOM:

Esempio albero DOM.

Esistono element nodes, text nodes and attribute nodes e altri ancora ( comment nodes per esempio ), da notare che tra i nodi ci sono precise relazioni di parentela, nell'esempio riportato il nodo P è padre del nodo STRONG e del nodo di testo, questi ultimi quindi sono fratelli.

Per approfondire i dettagli delle specifiche DOM niente di meglio che leggerle direttamente sul documento ufficiale W3C.

Javascript
Ora passiamo al lato puramente programmativo, Javascript implementa nativamente le API DOM. Incorpora nell'oggetto document appunto l'albero DOM della pagina HTML corrente. Quindi di seguito un po' di codice Javascript esemplificativo.

Navigare l'abero DOM

JavaScript:
  1. /*ottenere il nodo <strong>*/
  2. var strong = document.getElementsByTagName('STRONG')[0];
  3.  
  4. /*raggiunre il nodo <strong>*/
  5. var p = strong.parentNode;
  6.  
  7. /*infine il text node*/
  8. var nodeList = p.childNodes;
  9. var testo_semplice = nodeList[1];

NB: nel caso delle liste di nodi, sconsiglio di usare gli indici per raggiungere il nodo desiderato, poichè gli a capo vengono codificati come text node e potremmo quindi sbagliare i conti.

Modificare l'albero DOM
e quindi il relativo documento HTML.

JavaScript:
  1. /*modificare il nodo <strong>*/
  2. strong.firstChild.nodeValue='ModLorem';
  3.  
  4. /*eliminare il nodo <strong>*/
  5. p.removeChild(strong);
  6.  
  7. /*creare un nuovo nodo <u>Lorem</u>*/
  8. var u = document.createElement('U');
  9. var testo = document.createTextNode('Lorem');
  10. u.appendChild(testo);

Per avere i dettagli completi vi consiglio di visionare la Gecko DOM Reference.

In questo articolo ho voluto introdurre le basi concettuali che sostengono le applicazioni che prossimamente vi mostrerò! ;)

 
 
 
Chiudi
E-mail It