29 dicembre 2009

Wordpress + jQuery: Newsletter widget

Oggi vedremo un modo estremamente semplice di implementare (from scratch) un piccolo widget in un qualsiasi tema Wordpress, che consente agli utenti di iscrivere un indirizzo e-mail ad un servizio di newsletter. Il tutto sarà realizzato con jQuery, e non è altro che un'applicazione semplificata di quello che ho affrontato nel post precedente, sulla validazione di form con jQuery.

Ciò che andremo a creare l'ho sviluppato per la nuova versione dinamica del sito del Metodo Xiu Zhen Dao, che usa appunto Wordpress come CMS: il widget in questione è visibile nella barra laterale destra, in una qualsiasi pagina:



In questo caso, per rendere la vita semplice a noi e agli utenti, c'è un solo campo da riempire con il proprio indirizzo e-mail, e il widget riesce a validare correttamente questo campo, ad esempio controllando che sia presente il simbolo '@' e almeno un punto '.' (tutto questo sarà approfondito sotto):


I punti nodali sono due:
  1. Creare la struttura del form in un widget testuale
  2. Inserire il codice necessario al funzionamento del widget tramite un file esterno, da aggiungere al file header.php.
1. Markup HTML
Si deve innanzitutto creare la struttura HTML del form, e va inserita in un widget nuovo, di tipo testo: si va nel pannello di amministrazione di Wordpress, nella sezione Aspetto → Widget, e si sceglie la tipologia Testo ("Testo o HTML libero"). Ecco qua il mio widget compilato:



Il campo titolo del widget deve essere riempito a parte, mentre nel corpo del testo ho inserito il codice di un form assolutamente di base:

<div id="newsletter_form">
 Iscriviti alla newsletter del Metodo XiuZhenDao
 <form action="" method="post" id="form_news">
  <fieldset>
   <input id="email_news" name="email_news" type="text" value="la tua e-mail" />
   <input id="submit_news" type="submit" value="iscriviti" />
  </fieldset>
 </form>
</div>
Il CSS utilizzato per formattare il form è praticamente trascurabile, perché basta poco oltre a ciò che è già presente nello stile del tema Wordpress, dove generalmente gli input box e i pulsanti sono già stilati. Questo stile può essere inserito nel documento .css principale del tema, o in un qualunque altro foglio di stile incluso nella pagina dove si inserirà il widget.

/* newsletter widget */
#newsletter_form #email_news{
 width: 130px;
}
#newsletter_form #submit_news{
 margin-top: 5px;
 padding: 2px;
 cursor: pointer;
}
#newsletter_form .catched{
 font-style: italic;
}
La classe .catched ci servirà dopo, nel messaggio di successo.

2. validazione jQuery
È tempo di scrivere lo script di validazione: è sostanzialmente identico a quello che ho usato nel post precedente, ma in più stavolta implementeremo una validazione più professionale per il nostro campo e-mail (visto che è l'unico che abbiamo, stavolta!)

Ecco cosa fa lo script: quando l'utente clicca il pulsante "iscriviti" controlla che il campo sia stato riempito correttamente. Se ciò non si verifica, colora di rosso il campo; altrimenti invia un'e-mail all'amministratore con l'indirizzo inserito e visualizza un messaggio di successo.
(function($){
    $(document).ready(function(){
    
    // newsletter widget
 $('#email_news').click(function() {
  $(this).attr('value', '');
 });
 
 $("#submit_news").click(function() {
 
 // stile errore
 var errorclass = {
  'background' : '#ff435b',
  'color' : '#000',
  'borderColor' : '#000'
 }
 
 // analisi del dato inserito
 var email_news = $("input#email_news").val();
 var ln = email_news.length;
 var atpos = email_news.indexOf("@");
 var dotpos = email_news.lastIndexOf(".");
 
 // validazione campo e-mail
 if ((atpos < 1)||((dotpos - atpos) < 2)||(dotpos == ln-1)) {
  $("input#email_news").css(errorclass).focus();
     return false;
 }

 // passaggio dati, invio e-mail e messaggio di successo
 var dataString = 'email_news=' + email_news;  
 //alert (dataString); return false;
 $.ajax({  
  type: "POST",  
  url: "newsletter.php",  
  data: dataString,  
  success: function() {
   $('#newsletter_form').html("
"); $('#message_news').html('

Grazie! Hai iscritto alla newsletter

').append('').hide(); $('#message_news .catched').html(email_news); $('#message_news').fadeIn('slow'); } }); return false; }); }); })(jQuery);
Diamo una scorsa alla prima parte del codice:
  1. La prima istruzione (righe 5-7) serve soltanto per cancellare automaticamente il testo del campo quando l'utente ci clicca sopra per scrivere il suo dato.
  2. La riga 9 intercetta l'evento del click sul pulsante d'invio per dare inizio alle procedure di validazione.
  3. Le righe 12-16 definiscono una classe CSS che serve per modificare l'aspetto del campo input nel caso di errore.
La validazione del campo e-mail controlla che il dato inserito soddisfi le seguenti condizioni:
  1. Non deve essere nullo
  2. Deve contenere almeno un carattere '@' e almeno un punto '.'
  3. Il carattere '@' non deve essere all'inizio del dato inserito
  4. Il punto '.' deve essere ad almeno un carattere di distanza dopo '@' ma non può stare per ultimo.
Per fa questo, prima di tutto si memorizza il dato inserito dall'utente col metodo val(), e poi si esamina:
  1. se ne calcola la lunghezza col metodo length (riga 20)
  2. si individua la posizione del carattere '@' col metodo indexOf() (riga 21)
  3. si individua la posizione dell'ultimo punto '.' inserito col metodo lastIndexOf() (riga 22).
A questo punto, si può effettuare la validazione: le condizioni sono tutte singolarmente sufficienti a rendere errato un indirizzo e-mail, quindi sono collegate tra loro con un OR (||).
  1. Poiché il metodo indexOf() restituisce -1 se il carattere non è presente e 0 se è in prima posizione, con il controllo atpos < 1 si vede subito sia se '@' esiste o se è per primo
  2. Con dotpos - atpos < 2 ci si assicura che '@' e '.' non siano consecutivi, e che l'ultimo '.' sia a destra di '@'
  3. Con l'ultima condizione, dotpos == ln-1, si controlla invece se il punto '.' è in ultima posizione.
Questa validazione è molto simile a quella suggerita da w3schools.com nell'articolo JavaScript Form Validation.

Se la validazione non passa, si colora di rosso il campo applicandogli la classe .errorclass (riga 26).

Tutto quello che viene dopo riguarda il passaggio dei dati ad uno script php per l'invio dell'e-mail, ed è stato affrontato nel dettaglio nel post sulla validazione di form con jQuery.
In sostanza, si usa il metodo $.ajax, che ci consente anche di visualizzare un messaggio di successo, senza dover ricaricare questa o altre pagine.
Degno di nota, questa volta, l'inclusione del dato memorizzato nel messaggio di successo per l'utente, a conferma di quanto da lui inserito: basta inserire uno <span> con una classe per riconoscerlo (nel mio caso .catched) e passargli il valore memorizzato, col metodo html(val) (riga 40).

3. script PHP
L'ultima cosa da vedere è lo script che invia l'e-mail, e questo sì che è davvero minimale! Io l'ho scritto così:
<?php

$body = "indirizzo e-mail iscritto: ".$_POST['email_news'];
$header = "From: ".$_POST['email_news'];
 
mail("indirizzo@email.dominio", "Newsletter widget", $body, $header);
?>


4. file da includere
Ricapitolando, dopo aver inserito il codice HTML nel widget (sopra al punto 1), si deve inserire un file contenente lo script jQuery che abbiamo visto sopra (al punto 2) nel template header.php (e comunque all'interno dello <head> di ogni pagina su cui sarà visualizzato il widget), ad esempio in questo modo:
 
 <script type="text/javascript" src="newsletter_validation.js"></script>
Poi si deve mettere lo script php all'indirizzo specificato nel campo url del metodo $.ajax (forse la cosa più comoda è inserire un url assoluto dopo aver piazzato il file, ad esempio nella directory del tema di Wordpress).

06 dicembre 2009

jQuery: custom form validation

Avrei anche potuto intitolare questo post "principi di form validation", perché quello che vedremo è come sia possibile scriversi un semplice script di validazione form partendo da zero.
È possibile utilizzare il famoso plugin di jQuery chiamato appunto Validation creato da Jörn Zaeferrer, che aggiunge in maniera molto semplice delle funzioni personalizzabili per validare un form.
Noi qui non utilizzeremo questa strada, ma guarderemo da vicino quali sono i problemi e le necessità cui si va incontro se si vuole validare un form con jQuery, e ne ricaveremo la flessibilità che si ottiene da una maggiore comprensione.

Il nostro scopo finale è avere un form che viene validato senza il bisogno di caricare un'altra pagina e che invii un'email formattata arbitrariamente, e che al contempo sia sufficientemente flessibile da supportare delle variazioni alla ricetta generale.

L'esempio viene dal form contatti che ho implementato nella "tirata a lucido" del sito www.bfelettrotecnica.it, e la base da cui partiremo è pari pari quella proposta in questo tutorial di Net Tuts+ (a opera di Eric), e che consiglio di leggere comunque, prima di cominciare.



1. Markup HTML
Come si vede dall'immagine, il form contiene numerosi campi, molti obbligatori (ma non tutti), un select, una textarea e anche due radio per una selezione esclusiva: si consente all'utente di scegliere se rappresenta un privato oppure un'azienda, e di riempire dei campi che variano in base a questa scelta.

Ecco il codice del form:

<div id="contactform">
<form action="#" method="post">
 <fieldset>
  <legend>Richiesta Informazioni</legend>
  <ol>
   <li>
    <input id="privato" type="radio" value="privato" name="tipologia" /> <span class="radioin">Privato</span><br />
    <label for="nome">Nome e cognome <i>*</i></label>
    <input id="nome" name="nome" type="text" class="obb" />
    <label id='nome_err' class='error'>inserisci il tuo nominativo</label>
   </li>

   <li>
    <input id="societa" type="radio" value="azienda" name="tipologia" /> <span class="radioin">Azienda</span><br />
    <label for="azienda">Ragione sociale <i>*</i></label>
    <input id="azienda" name="azienda" type="text" class="obb" />
    <label id='azienda_err' class='error'>inserisci la ragione sociale</label>
    <label for="referente">Referente <i>*</i></label>
    <input id="referente" name="referente" type="text" class="obb" />
    <label id='referente_err' class='error'>inserisci il nominativo di un referente</label>
   </li>
            
   <li style="margin-top: 15px;">
    <label for="indirizzo">Indirizzo <i>*</i></label>
    <input id="indirizzo" name="indirizzo" type="text" class="obb" />
    <label id='indirizzo_err' class='error'>inserisci l'indirizzo dell'attività</label>  
    <label style="width: 75px;" for="cap">CAP <i>*</i></label>
    <input style="width: 50px" id="cap" name="cap" type="text" class="obb" />
    <label id='cap_err' class='error'>inserisci il codice di avviamento postale</label>
   </li>

   <li>
    <label for="citta">Città <i>*</i></label>
    <input id="citta" name="citta" type="text" class="obb" />
    <label id='citta_err' class='error'>inserisci il nome della città</label>  
    <label style="width: 75px;" for="provincia">Provincia <i>*</i></label>
    <input style="width: 50px;" id="provincia" name="provincia" type="text" class="obb" />
    <label id='provincia_err' class='error'>inserisci la sigla della provincia</label>
   </li>
            
   <li>
    <label for="telefono">Telefono</label>
    <input id="telefono" name="telefono" type="text" />
   </li>

   <li>
    <label for="email">e-mail <i>*</i></label>
    <input id="email" name="email" type="text" class="obb" />
    <label id='email_err' class='error'>inserisci il tuo indirizzo e-mail</label>
   </li>

   <li>
    <label for="area">Area d'interesse</label>
    <select id="area" name="area">
     <option value="void"> </option>
     <option value="centraline">Centraline di Controllo</option>
     <option value="plc">Programmazione PLC</option>
     <option value="Automazione">Automazione Industriale</option>
     <option value="Quadri Elettrici">Quadri Elettrici</option>
     <option value="Impianti Elettrici">Impianti Elettrici</option>
    </select>
   </li>

   <li>
    <label for="messaggio">Messaggio: <i>*</i></label>
    <textarea id="messaggio" name="messaggio" rows="5" cols="38" class="obb"></textarea>
    <label id='messaggio_err' class='error'>scrivi la tua richiesta</label>
   </li>
  </ol>
 </fieldset>
 <span id="input"><input id="submit" type="submit" value="Invia Messaggio" /></span>
 <span style="font-style: italic;">I dati personali verranno trattati nel rispetto della normativa sulla privacy.</span>
</form>
</div>

I punti degni di nota sono:
  • Nella prima riga, action="#", poiché utilizzeremo jQuery per gestire tutta la parte relativa al passaggio dei dati allo script php che invierà l'email finale.
  • I tag <label> dalla classe error sono dei messaggi di errore che verranno visualizzati qualora il rispettivo campo obbligatorio non sia stato compilato. Essi sono nascosti di default.
  • I campi obbligatori sono denotati come al solito dall'asterisco (*), inserito in un tag corsivo <i>, e dalla classe obb.
  • Il tag <div> che racchiude tutto il form si renderà necessario quando visualizzeremo un messaggio di successo per l'utente.

La classe "error" è dedicata ai messaggi d'errore, mentre la classe "errorinput" è per i campi riempiti erroneamente. Serve qualcosa che salti all'occhio, e in questi casi il colore d'elezione è il rosso. Io ho usato qualcosa di questo tipo, passato attraverso il CSS della pagina:

.error{
 color: red;
 font-style: italic;
 float: left;
}
.errorinput{
 border: 1px solid red;
 background: #ffcbcb;
 color: #000;
 margin-bottom: .5em;
}

La proprietà float mi è necessaria nelle righe con due tag input, per non distruggere il layout del form.

2. jQuery: form validation
2.0 validazione di base

La prima cosa da fare è nascondere i messaggi d'errore e prender possesso dell'evento click sul pulsante di invio del form, per poter eseguire le nostre validazioni, invece di inviare subito i dati:

$(document).ready(function(){

$('.error').hide();

$("#submit").click(function() {

// qui ci va tutta la validazione che si attiva quando l'utente intende inviare il messaggio

}

});

E fin qui siamo praticamente pari pari il tutorial di Net Tuts+.

La validazione che viene ora, prevede di controllare ciascun campo input, e se esso risulta vuoto, visualizzare il corrispondente messaggio d'errore.
Net Tuts+ ha solo tre campi, e quindi scrive la funzione di validazione tre volte, una per ciascun campo: siccome io ho un sacco di campi, ho velocizzato il tutto con un bel ciclo.

Per prima cosa, si inizializza una variabile, dataString, che andrà a contenere tutti i dati da passare allo script mail php, e sarà nella forma:
dataString = [nome_campo_1=] + [valore_campo_1] + [&nome_campo_2=] + [valore_campo_2] + ... + [&nome_campo_n=] + [valore_campo_n];


Poi, per analizzare ogni elemento <input> iterando, si usa il metodo each(callback): per ogni tag <input> (che non sia il pulsante d'invio o uno dei due radio all'inizio, e che non sia uno dei campi disabilitati, come vedremo al punto 4, sotto) e per la textarea lo script deve salvare due informazioni: il suo valore (col dato inserito dall'utente), che si ottiene con il metodo val(), e il suo ID. Con quest'ultimo andiamo subito a creare l'ID del corrispondente messaggio d'errore, aggiungendo il suffisso "_err".

Ecco lo script per queste prime operazioni:

$("#submit").click(function() {

$('.error').hide();

var dataString = "";

$('input[id!=submit][type!=radio], textarea, select').not('input[disabled]').each(function (i){
 var val = $(this).val();
 var id = $(this).attr("id");
 var err = id + "_err";
});

}

Ho inserito l'istruzione che nasconde i messaggi d'errore anche qui per resettare la validazione ogni volta che si preme il pulsante d'invio.

Ora, la validazione in sé: se il campo è vuoto (val == "") e il campo è obbligatorio (cioè detiene la classe obb), è necessario fare due azioni:
  1. Assegnare la classe d'errore "errorinput" (vista prima) al tag <input> per colorarlo di rosso,
  2. Visualizzare il corrispondente messaggio d'errore, tramite l'ID manipolato poc'anzi.

Se la validazione passa, si memorizza il dato inserito dall'utente accodandolo alla variabile dataString.

Inserendo queste operazioni, il ciclo completo diventa come segue:

$("#submit").click(function() {

var dataString = "";

$('input[id!=submit][type!=radio], textarea, select').not('input[disabled]').each(function (i){
 var val = $(this).val();
 var id = $(this).attr("id");
 var err = id + "_err";

// notifica dell'errore per i campi obbligatori
 if ((val == "") && ($(this).is('.obb'))) {
  $(this).addClass('errorinput').focus();
  $("label[id=" + err + "]").slideDown();
     return true;
 } else {
  $(this).removeClass('errorinput'); // rimozione cautelare della classe errore

// memorizzazione dato valutato
  if (i==0) {
   dataString = id + '=' + val;
  } else {  
   dataString = dataString + '&' + id + '=' + val;
  }
 }
});

}

});

Note su quanto inserito:

  • Sono necessarie due dichiarazioni per la variabile dataString, perché alla prima iterazione (i==0) non si deve anteporre il simbolo '&' al nome del dato che si memorizza.
  • Il messaggio d'errore, nel mio form, è individuale per ciascun campo. Se avessimo usato un unico tipo di messaggio per tutti i campi, il classico "questo campo è obbligatorio" (come fa Net Tuts+), si sarebbe potuto generarlo in questo punto dello script, risparmiando markup nel corpo della pagina.
  • Inserire un return true in questo punto significa dire al calcolatore di proseguire con le altre iterazioni del ciclo, senza fermarsi qui (cosa che si sarebbe ottenuta dichiarando return false): a livello pratico, questo vuol dire che tutti gli errori verranno visualizzati contemporaneamente (tutti i campi incompleti diventano rossi e si visualizzano i messaggi corrispondenti). Con return false si evidenzia un errore alla volta, come nel caso del tutorial di Net Tuts+.

Quanto visto finora è sufficiente a validare un form con un qualsiasi numero di campi, riuscendo a distinguere tra valori obbligatori o meno.

2.1 Radio buttons

Adesso si deve implementare la funzione dei pulsanti "radio" che consentono di scegliere tra "privato" e "azienda".
Il concetto è che l'uno esclude l'altro, e quindi se si riempie il campo di "privato" non si riempiono quelli di "azienda", e viceversa. Farlo capire al validatore è importante, perché sono tutti campi obbligatori!

In questo caso lo script deve solo ricordare all'utente di effettuare la scelta, la validazione dei campi risultanti è già stata implementata sopra.

Pertanto aggiungiamo un paio di righe all'inizio della funzione:

$("#submit").click(function() {

var dataString = "";

if ( (! $('#privato').is(':checked')) && (! $('#societa').is(':checked')) ) {
 $('.radioin').css('color' , 'red').append('&nbsp;&larr;');
 $('ol li:first').after('scegli se rappresenti un privato o un\'azienda');
 $('#radioerror').hide().slideDown('normal');
 return false;
}

//segue ciclo di validazione come visto sopra


Lo script non fa che controllare se nessuno dei radio è cliccato, allorché colora di rosso i titoli (e ci appende pure una freccia per meglio evidenziarli) e visualizza un messaggio.
Il return false fa sì che lo script si interrompa e nessun'altra validazione venga effettuata se l'utente prima non compie la sua scelta: i campi da validare infatti cambiano, come abbiamo detto.

3: Invio email
3.0 Preliminari
La parte di invio dell'email tramite un file php esterno è esattamente uguale a quella esposta nel tutorial di Net Tuts+, che fa utilizzo del metodo $.ajax.

Prima però di passare i dati al php, si deve inserire un'istruzione che blocchi lo script se esistono ancora errori nella compilazione del form:

if ($('.error').is(':visible')) {
 return false;
}

Poi, in fase di sviluppo, è utilissimo il suggerimento di Net Tuts+ di inserire un alert che stampi il contenuto di dataString, per controllare che siano stati memorizzati correttamente tutti i dati necessari:

alert (dataString); return false;

Questa riga verrà ovviamente commentata nella versione finale dello script!

3.1 Posting dei dati con jQuery
Adesso possiamo finalmente vedere come passare i dati all'invio email e visualizzare un messaggio di "successo" per l'utente:

$.ajax({
 type: "POST",  
 url: "email.php",  
 data: dataString,  
 success: function() {
  $('#contact_form').html('
'); $('#successo').html('

messaggio inoltrato correttamente

') .append('

sarai contattato appena possibile.

').hide().fadeIn('slow'); } }); return false;

L'opzione type corrisponde in questo caso all'attributo method del tag <form>, e viene settata a POST.
L'indirizzo del file php è relativo alla pagina html dove viene eseguito lo script.

L'opzione success ci consente di scrivere una semplice funzione che genera un messaggio di "inoltro corretto", e si fa semplicemente svuotando il <div> che racchiude il form, sostituendo quest'ultimo con un paio di righe: il metodo html(val) sostituisce tutto il contenuto dell'elemento selezionato con dell'HTML specificato. Nel nostro caso ci mettiamo un blocco che riempiamo con il messaggio scelto. L'ID assegnata al blocco ci consente di stilarlo con del CSS.

Il return false che chiude il nostro script di validazione serve per impedire al pulsante submit di eseguire la sua azione: tanto abbiamo già fatto tutto noi!

3.2 Script php
Lo script php è davvero semplice:

<?php

if ($_POST['azienda']) {
 $body = "Tipologia: Azienda\n\nRagione sociale: ".$_POST['azienda']."\nReferente: ".$_POST['referente'];
} else {
 $body = "Tipologia: privato\n\nNominativo: ".$_POST['nome'];
}

$body .= "\n\nIndirizzo: ".$_POST['indirizzo']."\n".$_POST['cap']." ".$_POST['citta']." (".$_POST['provincia'].")\n\nTelefono: ".$_POST['telefono']."\n\nArea d'interesse: ".$_POST['area']."\n\nMessaggio: ".$_POST['messaggio'];

$header = "From: ".$_POST['email'];

mail("indirizzo@email.dominio", "modulo contatti", $body, $header);
?>

La condizione serve soltanto per formattare l'email in modo diverso a seconda che sia stato scelto "privato" oppure "azienda"; poi si aggiungono tutti gli altri campi, che non cambiano.

Quello che è elencato in body va a finire nel corpo dell'email, mentre lo header ci consente di specificare l'indirizzo email dell'utente, che il client email userà ad esempio per "rispondere" al messaggio.

Con la funzione mail si invia l'email all'indirizzo specificato. Il secondo parametro è il subject dell'email, mentre il terzo ed il quarto passano i dati arrangiati prima.

4: Tocchi finali
L'unica cosa indispensabile da fare ancora è disabilitare i campi cui l'utente rinuncia effettuando la scelta tra "privato" e "azienda", perché usiamo questo attributo per escluderli dalla validazione.
E poi, visto che ci siamo, aggiungere qualche funzionalità in più a quei radio buttons non guasta.

Poiché questo deve avvenire prima della validazione, queste istruzioni saranno inserite nello script prima dell'evento click sviluppato nei punti precedenti.

L'evento ideale da intercettare è il cambiamento di stato dei pulsanti, e il metodo change() è proprio quello che fa al caso nostro; vediamo ad esempio cosa succede quando l'utente clicca sul pulsante per scegliere la categoria "privato":

$('#societa').change(function () {
 $('#nome').removeClass('obb').removeClass('errorinput').attr('disabled' , 'disabled');
 $('label[for=nome] i').empty();
 $('label[for=nome]').css('color' , '#999');
 $('#azienda, #referente').addClass('obb').removeAttr("disabled");
 $('label[for=azienda] i, label[for=referente] i').html('*');
 $('label[for=azienda], label[for=referente]').css('color' , '#000'); 
 $('.radioin').css('color' , '#000');
 $('.radioin span').remove();
 $('fieldset li:eq(0) .error, #radioerror').slideUp();
})

Le righe 2, 3 e 4 agiscono sui campi cui si rinuncia: essi diventano facoltativi e disabilitati (non ci si può scrivere); per maggiore chiarezza cancello anche l'asterisco e rendo la scritta più "sbiadita" con del grigio, e tolgo eventuali errori di validazione.
Le righe 4, 5 e 6 ripristinano lo stato obbligatorio dei campi prescelti (che potevano essere diventati facoltativi per un'altra scelta privato/azienda).
Le righe 7, 8 e 9 infine resettano eventuali notifiche di errore da parte della validazione.
In questo modo l'utente può cambiare idea quando vuole, e la validazione si adegua.

La stessa funzione, invertendo i riferimenti, si ricopia per l'altro pulsante, e il gioco è fatto.

Un extra interessante da inserire è la disabilitazione del pulsante d'invio mentre lo script sta processando i dati, così, nel tempo tra la pressione del pulsante e la visualizzazione del messaggio di successo, nessun utente particolarmente impaziente potrà inviare il form più di una volta.
Questo è proprio un voler essere perfezionisti, perché con script del genere il tempo di attesa è praticamente nullo! Confesso che questo me lo ha fatto venire in mente un commento al tutorial di Net Tuts+...

Basta disabilitare il pulsante prima della chiamata di $.ajax():

$('#submit').attr('disabled' , 'disabled');

È bene però riabilitarlo se la pagina viene ricaricata, per consentire all'utente di inviare un nuovo messaggio: questo si fa inserendo l'istruzione come prima cosa nel codice jQuery:

$(document).ready(function(){

$('#submit').removeAttr("disabled");

// e segue tutto il resto...

5: script completo
Ed ecco infine lo script completo, ottenuto mettendo insieme tutti i segmenti esaminati.

$(document).ready(function(){

/* form validation */

$('#submit').removeAttr("disabled");
$('.error').hide();

$('#privato').change(function () {
 $('#azienda, #referente').removeClass('obb').removeClass('errorinput').attr('disabled' , 'disabled');
 $('label[for=azienda] i, label[for=referente] i').empty();
 $('label[for=azienda], label[for=referente]').css('color' , '#999');
 $('#nome').addClass('obb').removeAttr("disabled"); 
 $('label[for=nome] i').html('*');
 $('label[for=nome]').css('color' , '#000');
 $('.radioin').css('color' , '#000');
 $('.radioin span').remove();
 $('fieldset li:eq(1) .error, #radioerror').slideUp();
})
$('#societa').change(function () {
 $('#nome').removeClass('obb').removeClass('errorinput').attr('disabled' , 'disabled');
 $('label[for=nome] i').empty();
 $('label[for=nome]').css('color' , '#999');
 $('#azienda, #referente').addClass('obb').removeAttr("disabled");
 $('label[for=azienda] i, label[for=referente] i').html('*');
 $('label[for=azienda], label[for=referente]').css('color' , '#000'); 
 $('.radioin').css('color' , '#000');
 $('.radioin span').remove();
 $('fieldset li:eq(0) .error, #radioerror').slideUp();
})


$("#submit").click(function() {

$('.error').hide();

var dataString = "";

if ( (! $('#privato').is(':checked')) && (! $('#societa').is(':checked')) ) {
 $('.radioin').css('color' , 'red').append('&nbsp;&larr;');
 $('ol li:first').after('scegli se rappresenti un privato o un\'azienda');
 $('#radioerror').hide().slideDown('normal');
 return false;
}

$('input[id!=submit][type!=radio], textarea, select').not('input[disabled]').each(function (i){
 var val = $(this).val();
 var id = $(this).attr("id");
 var err = id + "_err";
 if ((val == "") && ($(this).is('.obb'))) {
  $(this).addClass('errorinput').focus();
  $("label[id=" + err + "]").slideDown();
     return true;
 } else {
  $(this).removeClass('errorinput');
  if (i==0) {
   dataString = id + '=' + val;
  } else {  
   dataString = dataString + '&' + id + '=' + val;
  }
 }
});
if ($('.error').is(':visible')) {
 return false;
}

//alert (dataString); return false;

$('#submit').attr('disabled' , 'disabled');

$.ajax({
 type: "POST",  
 url: "email.php",  
 data: dataString,  
 success: function() {
  $('#contactform').html('
'); $('#successo').html('

messaggio inoltrato correttamente

') .append('

una risposta sarà inviata prima possibile.

') .append('') .hide().fadeIn('slow'); } }); return false; }); });

Ultima raccomandazione
Utilizzare esclusivamente l'ultima versione di jQuery, perché ad esempio nella versione 1.3.2 c'è un bug con IE8 (e chi se non lui?) che interpreta alla rovescia il check sulla visibilità degli errori, rendendo lo script inutile.

Ulteriori sviluppi
Per andare avanti da qui, si può passare alla validazione avanzata, ad esempio com'è implementata dal plugin Validation, che distingue campi numerici, email, url, ecc. e avverte l'utente quando i dati inseriti non sono conformi al formato atteso.
Il bello di questo è che avviene mentre l'utente sta digitando, e non c'è bisogno di aspettare la pressione del pulsante d'invio.
Questi sono ulteriori passi avanti nell'ottica del web 2.0, ma una volta inquadrati i concetti di base di cui sopra, non è difficile crearsi un prodotto su misura per le proprie necessità.