Aggiungere le gestures a jQuery

Che stiate lavorando ad un progetto in Cordova, oppure ad una web app, potrebbero tornarvi utili le gestures: swipe, rotate, pinch…tutti eventi che normalmente non sarebbero intercettabili con il semplice jQuery.

Ebbene, ai nostri progetti ci viene incontro un bellissimo plugin di $, ossia JGestures ( che trovate qui:https://jgestures.codeplex.com/documentation ).

Bisogna solamente fare attenzione che il DOM sia stato caricato completamente, quindi bisognerà intercettare gli eventi solamente una volta .ready.

Nel nostro caso abbiamo impostato un test nella seguente maniera:

	$('#page_test').bind('swipeone','li',function(event, obj){
		console.log(event);
		console.log(obj);
		console.log('swipeone');
	});

All’interno del nostro elemento #page_test, abbiamo un UL e vari elementi LI: vogliamo che, scorrendo con il dito sull’elemento della lista, venga effettuata una azione specifica.

Considerate che swipeone è diverso da swipetwo (scorrimento di un dito o di due dita).

Ricordatevi di richiamare i due oggetti event e obj all’interno della funzione di callback, altrimenti non potrete usufruire di varie informazioni quali la durata, il punto di partenza, la lunghezza in pixel dello scorrimento, etc.

Fate sempre riferimento alla documentazione ufficiale…e buona navigazione da mobile!

Jsonp e parse file json errato

Lavorando su un app con Phonegap mi sono imbattuto in una rogna non da poco, risolta grazie a qualche invocazione dell’Altissimo e un paio d’ore di sbattimento.
La problematica era di risolvere l’annoso problema dell’errore di console di Chrome Origin null is not allowed by Access-Control-Allow-Origin.
Leggendo vari post su internet, la problematica è capitata a tantissima gente e la risoluzione della stessa non sembra così palese come può sembrare.

Io stesso, soprattutto nei blog italiani, non ho trovato molte soluzioni rapide.
A parte la quantità indecente di soloni che si beano della propria conoscenza, cosa che mediamente fa incazzare come quando si paga l’IMU, che preferiscono rompere i maroni ore ed ore, invece che dare velocemente la soluzione, come se far faticare la gente fosse, in Italia, uno sport nazionale. Nei blog americani, invece, ci si imbatte più spesso in persone più malleabili e meno snob.

Veniamo velocemente al codice:

$.ajax({
		url:"http://www.vostrodom.it/pagina.asp",
		type:"GET",
		dataType:"json",
		data:"id=" + ID 
}) [...]

Molto semplicemente, una bella chiamata con jQuery.ajax, ad una pagina sul vostro sito, con il passaggio di una variabile querystring id.
Se usate Google Chrome, noterete un bel messaggio di errore, che avvisa che non possono essere effettuate chiamate CrossDomain, ossia fuori dal dominio della pagina stessa.
La problematica avviene soprattutto se avete lanciato una pagina da file:\\ .

Come ovviare al problema? Usando come dataType:jsonp e non json.
Occhio, le problematiche, usando questo sistema, diventano altre, però, per lo meno, una rogna, di dosso, ve la togliete. Per esempio, scordatevi il passaggio POST dei dati: con JsonP avvengono tutte via GET, con tutte le problematiche di dati e sicurezza che ne derivano. Se queste noie non vi interessano, risolvete in fretta il problema.

Risolto un problema, però, ne viene fuori un altro: jsonp utilizza una funzione javascript per ovviare al Domain Crossing. Non modificando niente, potrebbe venire fuori un errore del tipo Uncaught SyntaxError: Unexpected token.

Un passo alla volta.
Poniamo il caso che il vostro file json sia come nell’esempio seguente:

{
    "glossary": {
        "title": "example glossary" 
    }
}

Al file json manca un quid, ossia una funzione che conglobi tutto.
Modificate il vostro json aggiungendo, ad esempio, readJson([vostro-codice-json]).

readJson({
    "glossary": {
        "title": "example glossary" 
    }
})

Dopo di che, andiamo nel codice javascript a dirgli che la funzione callback che deve leggere si chiama readJson.

$.ajax({
		url:"http://www.vostrodom.it/pagina.asp",
		type:"GET",
		dataType:"json",
		data:"id=" + ID,
                jsonpCallback:	"readJson"
}) [...]

A questo punto, tutti gli odiosi messaggi d’errore della console dovrebbero sparire e, magicamente, potrete fare le vostre belle chiamate json esterne.

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.

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.

Semplice countdown in jQuery

Per continuare la nostra continua presentazione di plugin di jQuery che possano essere utili a tutti, oggi pongo alla vostra attenzione un semplice script per inserire, in qualsiasi punto della vostra pagina, un countdown verso una data indicata.

Lo script lo trovate all’indirizzo http://keith-wood.name/countdown.html, è molto semplice da configurare e fa semplicemente il suo dovere.

Eccovi un esempio del codice da inserire per visualizzare un countdown in jQuery in versione GIORNI-ORE-MINUTI-SECONDI:

var newYear = new Date();
newYear = new Date(newYear.getFullYear() + 1, 1 - 1, 1);
$('#defaultCountdown').countdown({until: newYear});

$('#removeCountdown').toggle(function() {
        $(this).text('Re-attach');
        $('#defaultCountdown').countdown('destroy');
    },
    function() {
        $(this).text('Remove');
        $('#defaultCountdown').countdown({until: newYear});
    }
);