jQuery: semplice validazione di un form

A differenza di qualche tempo fa, in cui ero piuttosto interessato a librerie esterne per validare i miei form, oggi preferisco scrivere e ottimizzare la validazione di volta in volta a seconda del sito in questione.

Troppe librerie, come jQueryTools o jQuery Validator, sono strettamente dipendenti dalla versione di jQuery utilizzata e vanno spesso in conflitto con altri elementi nel DOM.

Come procedere, allora, se siamo alle prime armi?
Un passaggio per volta, vediamo di arrivare ad una validazione completa.

Partiamo con il creare una pagina HTML ed inserirvi il riferimento ad un foglio di stile ed alla libreria jQuery:

<html>
<head>
<meta charset="utf-8">
<title>Form Validation</title>
<link href="stile.css" rel="stylesheet" type="text/css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
</head>
...

stile.css

body{
	font-size:12px;
	font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
}
/* selezioniamo tutti gli input con type=text e le textarea */
input[type=text], #form1 textarea{
	background:#F9F8F6;
	border:1px solid #706f6f;
	width:90%;
	padding:3px;
	font-size:12px;
	font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
	border-radius:3px;
}
/* diamo uno stile diverso alle select ed ai campi file */
select, #form1 input[type=file]{
	background:#CCCCCC;
	border:1px solid #706f6f;
	width:90%;
	padding:3px;
	font-size:12px;
	font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
	border-radius:3px;
}
button{
	background-color:#CCCCCC;
	border:1px solid #999999;
	width:90%;
	padding:3px;
	font-size:12px;
	font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;
	border-radius:3px;
}
button:hover{
	cursor:pointer;
	background-color:#b1b1b1;
}
/* stile che assegno ai campi errati */
.redborder{
	border:1px solid #C00 !important;
	box-shadow:0 0 3px #990000;
}
/* stile che assegno ai campi corretti */
.greenborder{
	border:1px solid #0C0 !important;
	box-shadow:0 0 3px #009933;
}

.errormsg{
	display:none;
}
.errormsg h2{
	color:#C00;
	margin:0px;
	padding:0px;
}
.errormsg ul{
	margin-top:0px;
	font-size:11px;
	color:#C00;
	font-weight:bold;

}
.waitmsg{
	display:none;
}

Form:

</pre>
<form id="form1" action="#" method="post" name="form1">
<label for="nome">Inserisci il tuo nome: </label>
<input id="nome" type="text" name="nome" data-description="Nome" />

<label for="cognome">Inserisci il tuo cognome: </label>
<input id="cognome" type="text" name="cognome" data-description="Cognome" />

<label for="email">Inserisci la tua E-Mail: </label>
<input id="email" type="text" name="email" data-description="E-Mail" />

<label for="marca">Seleziona la marca della tua auto: </label>
<select name="marca" data-description="Marca"> <option selected="selected" value="0">Seleziona una voce...</option></select>
<select name="marca" data-description="Marca"> <option value="1">BMW</option></select>
<select name="marca" data-description="Marca"> <option value="2">OPEL</option></select>
<select name="marca" data-description="Marca"> <option value="3">MERCEDES</option></select>
<select name="marca" data-description="Marca"> <option value="4">PORSCHE</option></select>

<input id="privacy" type="radio" name="privacy" value="1" data-description="Privacy" />
<label for="privacy">*<strong>si</strong>, ho letto e accetto il trattamento dei miei dati personali</label>

<button id="submitform1" type="button">Invia la richiesta</button>
<input id="errorState" type="hidden" name="errorState" value="0" />
<div class="errormsg">
<h2>Attenzione</h2>
</div>
<div class="waitmsg">
<h2><img src="img/ajax-loader3.gif" alt="" width="24" height="24" align="absmiddle" /> Attendere prego</h2>
</div>
</form>
<pre>

Alcuni tag sono custom, come ad esempio data-description.
Ho preferito gestire in maniera facoltativa il tag data-description, così da poter descrivere in maniera diversa, dal nome standard del campo, la label del campo stesso.
Nel caso in cui il campo sia
(input id=”email” type=”text”) e volessi scrivere “Il campo E-Mail non è corretto”, allora inserirò il valore “E-Mail” nel tag data-description.
Un sistema veloce, facile da customizzare, non obbligatorio e compatibile anche su IE.

Alla fine del form invece ho un campo con id=errorState e default value=0.
Durante il processo di validazione, mi appoggio al value di id=errorState, modificandolo in value=1 nel caso di errore in qualsiasi campo.
I messaggi di errore, invece, sono listati in un
(ul) dentro il div con class=errormsg.
Non ho inserito alcun input type=submit, ma un semplice button e ne intercetto il click con un semplicissimo bind(), sostituibile ( consigliato ) con un on(), visto che le nuove release di jQuery saranno poco gentili nei confronti di metodi deprecati.

Inserisco prima alcune funzioni:

// per pura velocità di scrittura del codice, mi creo una funzione tolog() ne commento il console.log() quando testo su IE.
function tolog(me){
  console.log(me);
}

// funzione che inserisce nella lista degli errori il singolo errore di validazione
function addToErroMsg(msg){
var msg_final = '</pre>
<ul>
	<li>' + msg + '</li>
</ul>
<pre>
';
  $('.errormsg ul').append(msg_final);
}

Ora intercetto il click del bottone di invio:

$('#submitform1').click(function(){

        // a tutti i campi del form che hanno la class=redborder rimuovo sia la classe redborder che greenborder
	$('#form1 input.redborder, #form1 select.redborder').removeClass('redborder').removeClass('greenborder');
        // svuoto la lista degli errori
	$('.errormsg ul').empty();
        // rimetto il valore di default di errorState
	$('#errorState').val('0');

        // valido i campi sottostanti. il tipo text può essere omesso, gli altri tipi devono essere citati
        // attualmente ho creato una validazione solamente per email, select e radio
	validField('nome');
	validField('cognome');
	validField('email','email');
	validField('marca','select');
	validField('privacy','radio');

        // se errorState==1 allora c'è stato un errore
	if($('#errorState').val()==1){
		$('.errormsg h2').html('Attenzione:');
		$('.errormsg').fadeIn();
	}else{
                // non c'è stato errore di validazione

		$('#submitform1').attr('disabled','disabled');   // disabilito il pulsante di invio
		$('.errormsg').hide();  // nascondo l'eventuale messaggio di errore validazione
		$('.waitmsg').fadeIn(); // mostro il messaggio di attesa
		$('#form1').submit();   // submit del form
	}

})

Ho commentato riga per riga il codice, così vi sarà molto semplice comprendere e personalizzare quando scritto.
Ora non resta che instanziare la funzione validField():

function validField(fieldName,tipo){

	// l'ID è fieldName: ora valuto se ho messo una descrizione diversa
	var fieldDesc = $('#' + fieldName).attr('data-description');
	if(fieldDesc==undefined){
		// il campo ha una descrizione diversa
		fieldDesc = fieldName;
	}

	// validazione per campo TEXT
	if(tipo==undefined || tipo=='text'){
		fieldValue = $('#' + fieldName).val();
		if(fieldValue.length==0){
			// validazione fallita, assegno la classe=redborder
			$('#' + fieldName).addClass('redborder').removeClass('greenborder');
			// errorState=1
			$('#errorState').val('1');
			// messaggio di errore, qui potete personalizzarlo
			addToErroMsg('Campo ' + fieldDesc + ' obbligatorio');
		}else{
			// la validazione è corretta, assegno la classe=greenborder
			$('#' + fieldName).addClass('greenborder').removeClass('redborder');
		}
	} // end type text

	if(tipo=='email'){

		fieldValue = $('#' + fieldName).val();
		if(fieldValue.length==0){
			$('#' + fieldName).addClass('redborder').removeClass('greenborder');
			addToErroMsg('Campo ' + fieldDesc + ' obbligatorio');
			$('#errorState').val('1');
		}else{
			if(fieldValue.indexOf('.')==-1 || fieldValue.indexOf('@')==-1){
				$('#' + fieldName).addClass('redborder').removeClass('greenborder');
				addToErroMsg('E-Mail non valida');
				$('#errorState').val('1');
			}else{
				$('#' + fieldName).addClass('greenborder').removeClass('redborder');
			}
		}
	} // end type email

	if(tipo=='select'){
		fieldValue = $('#' + fieldName + ' option:selected').val();
		if(fieldValue==0){
			$('#' + fieldName).addClass('redborder').removeClass('greenborder');
			addToErroMsg('Campo ' + fieldDesc + ' obbligatorio');
			$('#errorState').val('1');
		}else{
			$('#' + fieldName).addClass('greenborder').removeClass('redborder');
		}
	} // end type select

	if(tipo=='radio'){

		fieldValue = $('#' + fieldName).attr('checked');
		if(fieldValue==undefined){
			$('label[for="privacy"]').css('color','red');
			addToErroMsg('Campo ' + fieldDesc + ' obbligatorio');
			$('#errorState').val('1');
		}else{
			$('label[for="privacy"]').css('color','#999');
		}
	} // end type radio

} //end validField function

Abbiamo lavorato sullo stato di undefined di alcune variabili: su questa azione c’è sempre un aperto dibattito se non sia meglio operare diversamente.
Probabilmente si potrebbe procedere in maniera più “pulita”, ma in questo caso il tempo era poco e il risultato … accettabile.

Se volete vedere la demo di questo form, cliccate qui, mentre da qui scaricate lo zip con il codice.

Primi passi con jQuery Mobile: realizziamo la prima pagina

Per tutti coloro che hanno solamente sentito parlare di jQuery Mobile ed hanno bisogno di un piccolo tutorial sui primi passi in jQuery Mobile, vi proponiamo alcuni esempi su come procedere.

CODICE HTML INIZIALE:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>La mia prima pagina jQuery Mobile</title>
        <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    </head>
    <body>
    </body>
</html>

Iniziamo con l’includere alcune librerie ed il file CSS di jQuery Mobile, contenente i temi proposti di default da jQuery. Non dimenticate inoltre di includere la libreria di jQuery: nel nostro caso includeremo la libreria aggiornata alla versione 1.7.1.

Quello che vogliamo ottenere è la barra nel frontone, in alto, contenente un titolo d’esempio, ed un testo generico nel corpo.

Iniziamo, prima di tutto, inserendo nel BODY un oggetto che fungerà da pagina:

<div data-role="page" id="page1">
 ...
</div>

Come vedere, l’oggetto pagina è un semplice DIV. jQuery riconoscerà quel DIV come pagina grazie al tag data-role=page. Chiaramente andremo a dare un ID=page1 per poterla identificare facilmente.

All’interno di questa PAGE andiamo ad inserire due oggetti: un header ( con il titolo ) e un content.

<div data-role="page" id="page1">

    <div data-role="header">
       ...
    </div>
    <div data-role="content">
       ...
    </div>

</div>

Cosa abbiamo fatto?
Abbiamo semplicemente creato due oggetti diversi, un header ed un content, identificandoti grazie ai tag data-role=header e data-role=content. A questo elenco aggiungeremo, volendo, un data-role=footer, ma in questo caso non verrà affrontata.

Per poter evidenziare un titolo nell’header, andiamo ad inserire un H2:

<div data-role="page" id="page1">

    <div data-theme="b" data-role="header">
                <h3>
                    Titolo nel frontone
                </h3>
    </div>
    <div data-role="content">
       ...
    </div>

</div>

Se avete fatto attenzione, ho inserito un tag html H3 e un riferimento al tema.
Il tema di default avrebbe visualizzato l’header con uno sfondo nero, mentre forzando il tema a visualizzare il tema=b, sarà di sfondo blu.

Ora non resta che inserire un testo di esempio dentro l’elemento content.

<div data-role="page" id="page1">

    <div data-theme="b" data-role="header">
                <h3>
                    Titolo nel frontone
                </h3>
    </div>
    <div data-role="content">
                <div>
                    <h2>
                        <strong>
                            Lorem ipsum: titolo
                        </strong>
                    </h2>
                </div>
                <div>
                    <p>
                        <b>
                            <span>
                                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam facilisis,
                                dolor ultricies blandit molestie, nibh arcu eleifend purus, nec malesuada
                                erat mi ut sem.
                            </span>
                        </b>
                    </p>
                </div>
                <div>
                    <p>
                        <b>
                            <span>
                                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam facilisis,
                                dolor ultricies blandit molestie, nibh arcu eleifend purus, nec malesuada
                                erat mi ut sem.
                            </span>
                        </b>
                    </p>
                </div>
    </div>

</div>

Abbiamo inserito un semplicissimo codice html all’interno del div data-role=content.
Se tutto è andato come dovrebbe e abbiamo provato il codice soprastante con un browser come Chrome o Firefox, il risultato dovrebbe essere il seguente:

Vi siete persi qualche passaggio? Ecco il codice completo:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>
        </title>
        <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
        </script>
        <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js">
        </script>
    </head>
    <body>
        <div data-role="page" id="page1">
            <div data-theme="b" data-role="header">
                <h3>
                    Titolo nel frontone</h3>
            </div>
            <div data-role="content">
                <div>
                    <h2>
                        <strong>
                            Lorem ipsum: titolo</strong></h2>
                </div>
                <div>
                    <p>
                        <b>
                            <span>
                                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam facilisis,
                                dolor ultricies blandit molestie, nibh arcu eleifend purus, nec malesuada
                                erat mi ut sem.
                            </span>
                        </b>
                    </p>
                </div>
                <div>
                    <p>
                        <b>
                            <span>
                                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam facilisis,
                                dolor ultricies blandit molestie, nibh arcu eleifend purus, nec malesuada
                                erat mi ut sem.
                            </span>
                        </b>
                    </p>
                </div>
            </div>
        </div>
    </body>
</html>

Piccolo off topic:

Tutta la nostra solidarietà va agli amici emiliani, così duramente colpiti dalle scosse di questi giorni. Non appena riceveremo le informazioni corrette, pubblicheremo le modalità di acquisto di prodotti agroalimentari dalle ditte così duramente colpite dal disastro. Perchè acquistare il parmigiano al supermercato, quando lo si può acquistare direttamente dai produttori, così bisognosi di aiuto?

Motherboard fritta e sostituzione rapida

Black Friday

Venerdì scorso il PC desktop che uso per programmare ha deciso, in seguito ad un fulmine, di friggere e spegnersi. Cose che capitano, ma la cosa molto strana è che, nonostante un gruppo di continuità ed un alimentatore OCZ da 1250w, l’unica cosa a friggere sia stata la scheda madre, una Asus P6T Deluxe Palm Edition.

E’ una scheda che ha sempre fatto il suo lavoro, anche se ultimamente mi ha dato da pensare che non fosse il massimo dell’ingegneria: sia la mia che quella di mio padre, identiche e comperate assieme, hanno sempre dato qualche piccolo difetto.

Appurato che fosse la MB a partire, il problema nasceva dal fatto che, essendo datata, trovarne una uguale era realmente un problema: le poche schede nuove avevano un costo veramente fuori dal mondo, più di 395£ + spedizione dall’UK. Una soluzione non percorribile.

Abbandonata l’idea di trovarne una uguale, mi sono fatto convincere da un amico sistemista di prenderne una con il medesimo socket 1366 per provare a sostituirla a caldo: devo ammettere che avevo forti dubbi sulle possibilità che tutto andasse bene, ma la mia esperienza in fatto di sistemi è molto limitata, quindi ho preso per buono quanto consigliatomi.

Una nuova scheda madre: Asus Sabertooth X58

La nuova scheda è una Asus Sabertooth X58, veramente una gran bella scheda, potente, completa e dal costo contenuto, circa 220€ ivata.

Dopo aver smontato tutto e assemblato tutti i componenti, collegati i 4 dischi SATA, il masterizzatore SATA, ho fatto che eliminare una periferica IDE inutile e montato il processore i7 920 oltre ai 24gb di RAM.

Accendo il PC e … niente. Il nulla cosmico.
Entro nel BIOS e noto che il mio disco C non è elencato tra i dischi di boot. Il problema, forse, nasce dal fatto di averlo collegato in prima istanza sul canale SATA 6gb/s: probabilmente è un problema di incompatibilità, ma non ho avuto abbastanza tempo per fare le prove del caso.

Spostando il cavo SATA del disco primario sul canale standard 3gb/s, tutto è tornato normale: il disco è presente nell’elenco degli hard disk di boot.

Una volta corretto il boot list, il sistema sale e, come d’incanto, Windows mi chiede se voglio uno start normale o in modalità provvisoria. Quasi un miraggio. Evitare di installare di nuovo da zero Windows, tutti gli applicativi, la Suite Adobe, l’infrastruttura virtuale: sapete bene cosa significhi non dover scaricare giga di dati da internet per una installazione, per non parlare delle configurazioni custom perse.

Lo faccio partire in modalità provvisoria, Windows non chiede di essere attivato, la suite di Adobe non riconosce il mio account di Creative Cloud e il sistema parte con l’installazione di una serie di driver standard. Vanno tutti a buon fine, tranne quelli Marvell e i driver video: inserisco il DVD della scheda madre, installo i driver mancanti. Riavvio.

Un sogno. Funziona praticamente tutto alla perfezione.
Mi è bastato scaricare ed installare i driver NVidia aggiornati per le mie due GT260 in SLI e il sistema mi configura nuovamente i monitor riportando il mio sistema completo allo status quo pre-frittura mista di condensatori di scheda madre.

Ora il sistema è stabile, più veloce di circa il 20% e mi ritrovo con l’USB3 nativo on board e il supporto per nuovi processori. La Adobe Creative Suite mi chiede l’Adobe ID e mi conferma la correttezza e lancia correttamente gli applicativi.

La morale è: se avete una scheda simile alla Asus P6T e dovete gioco forza cambiarla, forse la mia esperienza può tornarvi utile.

jQuery: differenze tra .bind() .live() .delegate() e .on()

bind(),  live(),  delegate(),  on(): tutti uguali?

Una delle cose più incasinate in jQuery è capire la differenza che c’è realmente tra i vari metodi .bind(), .live(), .delegate() e .on(), poichè, di primo acchito, sembra che ognuno sia una semplice alternativa dell’altro.

Ovviamente così non è, magari qualche commento a riguardo può chiarire questi passaggi, permettendovi di decidere meglio cosa e come fare.

Per prima cosa dichiaro un po’ di codice HTML che userò per i miei esempi:

<ul id="giocatori" data-role="listview" data-filter="true">
    <!-- ... altri elementi di lista ... -->
    <li>
        <a href="giocatore_dettaglio.html?id=10">
            <h3>Mauro Bergamasco</h3>
            <p><strong>Flanker</strong></p>
            <p>Nazionale italiana di Rugby</p>
        </a>
    </li>
    <!-- ... altri elementi di lista ... -->
</ul>

Il metodo bind()

$( "#giocatori li a" ).bind( "click", function( e ) {} );
$( "#giocatori li a" ).click( function( e ) {} );
$( "#giocatori li a" ).hover( function( e ) {} );

Il metodo bind() collega il tipo di evento e l’handler direttamente nell’elemento DOM in questione. E’ sicuramente il più usato, grazie alla facilità d’uso, ma qualche problema di prestazioni lo causa. Considerate che .click() non è nient’altro che un’abbreviazione di bind() stesso … ecco perchè affermo che bind() è il più usato ;).
Il problema di prestazioni è dato dal fatto che l’handler dichiarato viene applicato a tutti gli elementi del DOM coinvolti dal comando, e quello che succede è vedere reiterato lo stesso handler N volte. Questa cosa non è il massimo a livello di prestazioni.

Il metodo live()

Bastano veramente poche parole per descrivere il metodo live(). Siete soliti usarlo? Allora maggiore sarà il tempo che dovrete impiegare per sostituirlo con un metodo alternativo migliore e, sopra ogni cosa, che non sia deprecato ufficialmente da jQuery dalla 1.7. Ebbene si, notizia ferale.

La problematica principale di live() era quando lo si usava in presenza di pagine molto ricche e soprattutto quando veniva “invocato” in maniera non specifica, utilizzando un selettore generico:

$( "#giocatori li" ).live( "click", function( e ) {} );

Il metodo delegate()

$( "#giocatori" ).delegate( "li a", "click", function( e ) {} );

Live() collega l’handler dell’evento al documento generale, mentre delegate() collega le informazioni, la catena selettore/evento ( “li a”&”click” ) direttamente all’elemento nel DOM ( “#giocatori” ). Questo sistema di collegamento direttamente nel DOM element ha permesso di risolvere molte problematiche di funzionamento, rispetto che infarcire il document di una sfilza di informazioni una dietro l’altra e, cosa ancora più importante, funziona correttamente con gli elementi aggiunti dinamicamente DOPO la fine del caricamento iniziale della pagina.

Il metodo on()

Quello che, stando alle affermazioni di jQuery stessa, dovrebbe essere il vostro obiettivo futuro è proprio on(), che prenderà in un colpo solo il posto di bind(), unbind(), live(), die(), delegate() e undelegate(). Guardare per credere:

// Bind
$( "#giocatori li a" ).on( "click", function( e ) {} );
$( "#giocatori li a" ).bind( "click", function( e ) {} );

// Live
$( document ).on( "click", "#giocatori li a", function( e ) {} );
$( "#giocatori li a" ).live( "click", function( e ) {} );

// Delegate
$( "#giocatori " ).on( "click", "li a", function( e ) {} );
$( "#giocatori " ).delegate( "li a", "click", function( e ) {} );

Come potete vedere, la potenza data a on() è notevole: a seconda di come lo chiamate in causa, lui diventa metamorfico e modifica il modo in cui l’evento è chiamato in causa.
Sicuramente l’instanziare un metodo solo, che prenda il posto di N metodi, aiuta a standardizzare il tutto, rendendolo anche più facile per jQuery da manipolare.
L’unico lato negativo, se così si può chiamare, è che, per poterlo usare, bisogna entrare un attimo nell’ottica di come cambia la gestione dell’evento a seconda di come richiamate il metodo.

Il suggerimento del maestro Zen è quello di studiare il metodo on() e cominciare ad usarlo in tutti i nuovi progetti dove userete la libreria jQuery dalla 1.7 in poi.

jQuery Mobile: prototipazione rapida con Codiqa

Che siate alle prime armi con jQuery Mobile o che sappiate già tutto quello che serve, Codiqa mette a disposizione uno strumento decisamente interessante per realizzare con un’interfaccia drag&drop le vostre applicazioni web.

L’idea di fondo è dare uno strumento semplice, veloce e intuitivo per creare una bozza di interfaccia da poter, alla fine della fase di design, scaricare e, magari, inviare a colui che dovrà mettere mano al codice.

Seguendo un po’ la falsariga del Sencha Architect 2, è uno strumento che permette di realizzare il vostro sito mobile senza conoscerne il codice, anche se l’ottimizzazione manuale è poi obbligatoria se si vuole raffinare il tutto.

Per quelli più attenti tra voi non sarà necessario aggiungere che il plugin di Codiqa è presente anche sul sito ufficiale di jQuery Mobile

Essendo Codiqa un servizio cloud, ha un costo mensile differente a seconda del profilo che preferite, ma se fate attenzione, in fondo alla tabella, c’è un link per attivare un’utenza gratuita con delle forti limitazioni ma utile per provare.