Validazione lato client delle form

Questa traduzione è incompleta. Aiutaci a tradurre questo articolo dall’inglese

Prima di inviare dati al server, è importante assicurarsi che tutti i campi richiesti della form siano stati inseriti e siano nel formato richiesto. Questa viene chiamata validazione delle form lato client, ed aiuta a fare in modo che i dati inviati siano consistenti con quanto richiesto. Questo articolo illustra anche con esempi i concetti base della validazione.
Prerequisiti: Conoscenze informatiche, ed una ragionevole comprensione di HTML, CSS, e JavaScript.
Obiettivo: Capire che cos'è la validazione delle form lato client, perché è importante e come applicare le varie tecniche per implementarla.

La validazione lato client è un controllo che va fatto subito ed è una caratteristica importante per una buona esperienza untente; trovando i dati non corretti nel lato client, l'utente li può correggere immediatamente. Se i dati non corretti venissero inviati al server e rifiutati, si assisterebbe ad un considerevole e fastidioso ritardo operativo dovuto al traffico tra client e server per le richieste di correzione.

Comunque, la validazione lato client non dovrebbe essere considerata una misura di sicurezza sufficiente! La tua app dovrebbe sempre eseguire controlli di sicurezza anche nel lato server, perché le validazioni lato client sono troppo facili da bypassare, quindi gli utenti malevoli potrebbero comunque spedire dati non corretti al server. Leggi l'articolo Website security per farti un idea di quello che potrebbe succedere; in questo articolo non trattiamo la validazione lato server, ma tu dovresti tenerla presente.

Che cos'è la validazione delle form?

Vai su qualche diffuso sito con registrazione delle form e ti accorgerai che ti  fornirà informazioni immediate se non inserisci i dati nel formato che loro si aspettano. Otterrai messaggi simili a questi:

  • "Questo è un camo richiesto" (Non lo puoi lasciare in bianco).
  • "Per favore inserisci il numero di telefono nel formato xxx-xxxx" (Viene richiesto uno specifico formato di dato per essere considerato valido).
  • "Per favore inserisci un indirizzo email valido" (la mail inserita non è nel formato corretto).
  • "La password deve essere compresa tra 8 e 30 caratteri e deve contenere lettere maiuscole, minuscole, simboli e numeri." (Viene richiesto un formato molto particolare).

Questa viene chiamata validazione della form. Quando inserisci i dati il browser e/o il web server controllano se i dati sono corretti secondo le regole stabilite dalla applicazione. La validazione fatta dal browser viene chiamata validazione lato client e quella fatta dal server viene chiamata validazione lato server. Questo articolo si occupa della validazione lato client.

Se le informazioni hanno il formato corretto, l'applicazione consente che vengano spedite al server il quale di solito le salva in un database. Se invece non sono corrette, viene mandato un messaggo all'utente spiegando l'errore e consentendo di riprovare.

Noi desideriamo che l'inserimento delle form sia il più facile possibile. Quindi per quale motivo insistiamo per la validazione? I motivi principali sono tre:

  • Vogliamo che ci arrivino i dati giusti nel formato corretto. La nostra applicazione potrebbe non funzionare in modo corretto se i dati che ci arrivano sono scorretti o incompleti.
  • Vogliamo proteggere i dati degli utenti. Costringendo gli utenti ad inserire password robuste facilitiamo la protezione delle loro informazioni anagrafiche.
  • Vogliamo proteggere noi stessi. Ci sono molti modi che gli utenti malevoli possono provare per danneggiare le applicazioni se la form non è protetta (vedi Website security).

    Attenzione: Mai fidarsi dei dati inviati al server dal client. Anche se la form è stata validata correttamente, gli utenti malevoli potrebbero alterare la richiesta di rete.

Differenti tipi di validazione lato client

Ci sono due tipi di validazione che si possono incontrare nel web:

  • Validazione built-in della form usando le funzionalità di validazione delle form di HTML5, di cui parleremo spesso in questo articolo. Questo tipo di validazione di solito non richiedono molto JavaScript. La validazione built-in ha prestazioni migliori di JavaScript, ma non è altrettanto configurabile.
  • Validazione JavaScript che viene eseguita scrivendo codice JavaScript. Questa validazione si può configurare come si desidera ma deve essere scritta interamente (oppure si usa una delle molte librerie disponibili).

Usare la validazione built-in delle form

Una delle funzionalità più significative dei HTML5 form controls è la capacità di effettuare molte validazioni senza richiedere l'uso di JavaScript. Questo viene fatto usando attributi di validazione negli elementi della form. Lo abbiamo visto anche in altre occasioni, ma ricapitolando:

  • required: Specifica che è un campo che deve essere riempito prima di poter invare i dati.
  • minlength e maxlength: Specifica la dimensione minima e massima dei campi di testo
  • min e max: Specifica i valori minimi e massimi dei campi di tipo numerico
  • type: Specifica se il dato deve essere un numero, un indirizzo email o qualche altro tipo predefinito. 
  • pattern: Specifica una regular expression che definisce le regole che i dati inseriti devono seguire.

Se i dati inseriti nella form seguono tutte le regole specificate dagli attributi è considerata valida. Altrimenti viene considerata non valida.

Quando un elemento è valido, le seguenti cose sono vere:

  • L'elemento è conforme alla pseudo-classe :valid CSS, che ti consente di applicare uno stile specifico agli elementi validi.
  • Se l'utente prova ad inviare i dati il browser spedirà la form se non c'è nient altro le la blocca (ad esempio ulteriori validazioni JavaScript).

Quando un elemento non è valido, le seguenti cose sono vere:

  • L'elemento è conforme alla pseudo-classe :ivalid CSS, e qualche altra pseudo-classe (es. :out-of-range) in base al tipo di errore che ti consente di applicare uno stile specifico agli elementi non validi.
  • Se l'utente prova ad inviare i dati il browser blocca la form e visualizza un adeguanto messaggio di errore.

Nota: Esistiono alcuni errori che impediscono la spedizione della form, compreso badInput, patternMismatch, rangeOverflow o rangeUnderflow, stepMismatch, tooLong or tooShort, typeMismatch, valueMissing, o un customError.

Esempi di validazione built-in delle form

In questa sezione proveremo alcuni degli attributi di cui abbiamo parlato sopra.

Semplice esempio iniziale

Incominciamo con un esempio semplice: Un campo di testo che ti permette di scegliere se preferisci le banane o le ciliege (banana or cherry). Questo esempio richiede un semplice testo <input> con associata una <label> e un pulsante di invio <button>. Il codice sorgente si trova su GitHub su fruit-start.html con un esempio live.

<form>
  <label for="choose">Would you prefer a banana or cherry?</label>
  <input id="choose" name="i_like">
  <button>Submit</button>
</form>
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

Per incominciare fai una copia di  fruit-start.html in una directory vuota del tuo disco fisso.

L'attributo required

La validazione HTML5 più semplice è l'attributo required. Per rendere obbligatorio un campo input aggiungi questo attributo. Se è presente, l'elemento esegue il controllo con la pseudo-classe :required e la form non viene inviata e visualizza un messaggio d'errore se il campo input è vuoto. Quando è vuoto il campo è considerato non valido anche dalla pseudo-classe :invalid.

Aggiungi l'attributo required come si vede qui sotto.

<form>
  <label for="choose">Would you prefer a banana or cherry? (required)</label>
  <input id="choose" name="i_like" required>
  <button>Submit</button>
</form>

Considera il CSS che è incluso nel file d'esempio:

input:invalid {
  border: 2px dashed red;
}

input:invalid:required {
  background-image: linear-gradient(to right, pink, lightgreen);
}

input:valid {
  border: 2px solid black;
}

Questo CSS assegna all'input un bordo rosso a trattini quando il valore non è valido ed una linea nera più sottile quando è valido. Abbiamo anche aggiunto uno sfondo a gradiente quando è richiesto e non valido. Prova il nuovo comportamento nell'esempio sotto:

Nota: Puoi trovare questo esempio live su GitHub come fruit-validation.html (vedi anche il codice sorgente.)

Prova ad inviare la form senza un valore. Nota che i campo non valido acquista il focus ed appare il messaggio predefinito ("Per favore inserisci questo campo"), e la form non viene inviata.

La presenza dell'attributo required in un elemento che lo supporta comporta che l'elemento controlla la pseudoclasse :required per controllare se contiene un valore oppure no. Se il campo <input> non ha un valore, attiva la pseudoclasse :invalid.

Nota: Per una buona esperienza utente, indica all'utente quando un campo è richiesto. Non è solo una buona pratica, ma è anche richiesto dalle linee guida accessibility del WCAG. È anche bene rendere obbligatori i soli campi che ti servono. è inutile rendere obbligatori i campi di cui non hai reale necessità.

Validazione con una regular expression

Un'altra caratteristica di validazione molto usata è l'attributo pattern, che si aspetta una Regular Expression come valore. Una regular expression (regex) è un modello che può essere usato per confrontare una combinazione di caratteri in una stringa, questo li rende ottimi per la validazione delle form e vengono usati anche per un certo numero di altri usi in JavaScript.

I regex sono abbastanza complessi, e non riusciamo a spiegarli in modo completo in questo articolo. Di seguito riportiamo alcuni esempi per darti un'idea di base su come funzionano.

  • a — Accetta un carattere che deve essere a (non b, nemmeno aa, e così via).
  • abc — Accetta a, seguito da b, seguito da c.
  • ab?c — Accetta a, opzionalmente seguito b, seguito c. ( ac oppure abc)
  • ab*c — Accetta a, opzionalmente seguito da qualsiasi numero di b, seguito da c. ( ac , abc, abbbbbc, e via di seguito).
  • a|b — Accetta un carattere che può essere a o b.
  • abc|xyz — Accetta esattamente abc o esattamente xyz (ma non abcxyz o a o y, e così via).

Ci sono moltissime altre possibilità che non trattiamo. Per la lista completa e molti esempi consulta la nostra documentazione delle Regular expressions.

Proviamo ad implementare un esemio. Modifica il tuo HTML per aggiungere un attributo pattern come il seguente:

<form>
  <label for="choose">Would you prefer a banana or a cherry?</label>
  <input id="choose" name="i_like" required pattern="[Bb]anana|[Cc]herry">
  <button>Submit</button>
</form>

Che ci da il seguente aggiornamento — prova:

Nota: Puoi trovare questo esempio live su GitHub su fruit-pattern.html (guarda anche il codice sorgente.)

In questo esempio, l'elemento <input> accetta una di quattro possibili valori: "banana", "Banana", "cherry", o "Cherry". Le regular expressions sono sono sensibili a maiuscole e minuscole, ma noi abbiamo supportato sia le parole minuscole che quelle con la prima maiuscola usando il modello "Aa" racchiuso tra parentesi quadre.

Prova ora a cambiare il valore dell attributo pattern con gli esempi visti in precedenza e vedi in che modo cambiano i valori che puoi inserire. Prova anche a scrivere qualche regola per conto tuo e cerca di dare un senso alle regole rispetto al esempio della frutta!

Se un valore di <input> non soddisfa il modello della regular expression il campo input  applicherà la pseudoclasse  :invalid.

Nota: Alcuni tipi di elemento <input> non necessitano di un attributo pattern per essere validati secondo una regular expression. Specificando il tipo email, ad esempio, l'input viene validato con il consolidato modello per la validazione delle email o con il modello per una lista di email separate da virgole se ha anche l'attributo multiple.

Nota: L'elemento <textarea> non supporta l'attributo pattern.

Limitare la dimensione dei campi

Puoi limitare la dimensione dei campi testo creati con  <input> o <textarea> usando gli attributi minlength e maxlength. Un campo non è valido se ha meno caratteri del valore di minlength o maggiore del valore di maxlength.

I browsers spesso non consentono all'utente di inserire più caratteri di quelli consentiti dal campo. Per migliorare l'esperienza utente invece di usare solamente maxlength si può fornire l'indicazione del numero di caratteri residui per dare modo all'utente di regolarsi. Un esempio di questo si trova in Twitter. Con JavaScript esiste una soluzione che usa maxlength, che si può utilizzare.

Limitare i valori dei campi

Per i campi numerici (es. <input type="number">), gli attirbuti min e max possono essere utilizzati per fornire i limiti di valori validi. Se il campo contiene valori fuori dai limiti non è valido.

Vediamo un altro esempio. Creiamo una copia del file fruit-start.html.

Ora sostituiamo i contenuto dell'elemento <body> con il seguente:

<form>
  <div>
    <label for="choose">Would you prefer a banana or a cherry?</label>
    <input type="text" id="choose" name="i_like" required minlength="6" maxlength="6">
  </div>
  <div>
    <label for="number">How many would you like?</label>
    <input type="number" id="number" name="amount" value="1" min="1" max="10">
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>
  • Si può vedere che abbiamo messo 6 negli attrinuti minlength e maxlength del campo text che corrisponde alla lunghezza delle parole banana e cherry.
  • Abbiamo anche aggiunto un campo number  con un min di uno ed unmax di dieci. I numeri fuori dai limiti vengono visualizzati come non validi; gli utenti non possono usare le frecce di incremento e decremento per andare oltre i limiti. Se l'utente inserisce manualmente un dato esterno ai limiti il valore non è valido. Il numero non è obbligatorio quindi se si rimuove il valore, resta comunque valido.

Ecco un esempio live:

Nota: Puoi trovare questo esempio live su GitHub su fruit-length.html (guarda anche il codice sorgente.)

Nota: <input type="number"> (ed anche altri tipi come range e date) possono anche avere l'attributo step, che specifica il valore minimo di incremento e decremento quando viene usato il campo (ad esempio premendo i pulsanti su e giu dei campi numerici). Nel esempio precedente non abbiamo inserito l'attributo step quindi il valore parte da 1. Questo significa che i numeri con la virgola come 3.2, sono anch'essi non validi.

Esempio completo

Ecco un esempio completo che dimostra l'uso delle funzionalità di validazione built-in di HTML:

<form>
  <p>
    <fieldset>
      <legend>Do you have a driver's license?<abbr title="This field is mandatory" aria-label="required">*</abbr></legend>
      <!-- While only one radio button in a same-named group can be selected at a time,
           and therefore only one radio button in a same-named group having the "required"
           attribute suffices in making a selection a requirement --> 
      <input type="radio" required name="driver" id="r1" value="yes"><label for="r1">Yes</label>
      <input type="radio" required name="driver" id="r2" value="no"><label for="r2">No</label>
    </fieldset>
  </p>
  <p>
    <label for="n1">How old are you?</label>
    <!-- The pattern attribute can act as a fallback for browsers which
         don't implement the number input type but support the pattern attribute.
         Please note that browsers that support the pattern attribute will make it
         fail silently when used with a number field.
         Its usage here acts only as a fallback -->
    <input type="number" min="12" max="120" step="1" id="n1" name="age"
           pattern="\d+">
  </p>
  <p>
    <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory" aria-label="required">*</abbr></label>
    <input type="text" id="t1" name="fruit" list="l1" required
           pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range">
    <datalist id="l1">
      <option>Banana</option>
      <option>Cherry</option>
      <option>Apple</option>
      <option>Strawberry</option>
      <option>Lemon</option>
      <option>Orange</option>
    </datalist>
  </p>
  <p>
    <label for="t2">What's your e-mail address?</label>
    <input type="email" id="t2" name="email">
  </p>
  <p>
    <label for="t3">Leave a short message</label>
    <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
  </p>
  <p>
    <button>Submit</button>
  </p>
</form>

Ed ora un po' di CSS per dare stile al HTML:

form {
  font: 1em sans-serif;
  max-width: 320px;
}

p > label {
  display: block;
}

input[type="text"],
input[type="email"],
input[type="number"],
textarea,
fieldset {
  width : 100%;
  border: 1px solid #333;
  box-sizing: border-box;
}

input:invalid {
  box-shadow: 0 0 5px 1px red;
}

input:focus:invalid {
  box-shadow: none;
}

Questo viene presentato nel modo seguente:

Vedi Validation-related attributes per la lista completa degli attributi che possono essere utilizzati per limitare i valori ed i tipi di input che li supportano.

Nota: Puoi trovare questo esempio live su GitHub su  full-example.html (guarda anche il codice sorgente.)

Validazione delle forms con JavaScript

Sei costretto ad usare JavaScript se desideri controllare l'aspetto dei messaggi nativi d'errore o per conformarti ai browsers più vecchi che non supportano la validazione built-in di HTML. In questa sezione daremo un occhiata a diversi modi per farlo.

Le API di limitazione della validazione

Molti browsers supportano le Constraint Validation API, che consistono in un gruppo di metodi e proprietà disponibili nelle seguienti interfaccie degli elementi delle form del DOM:

Le API di validazione rendono le seguenti proprietà disponibili per li elementi di cui sopra.

  • validationMessage: Restituisce un messaggio che descrive il limite di validazione che il campo non soddisfa nella lingua del browser. Se il campo non ha limiti di validazione  (willValidate è false) o se il valore è valido restituisce una stringa vuota.
  • validity: Restituisce un oggetto ValidityState che contiene alcune proprietà che descrivono lo stato di validità dell'elemento. Puoi trovare tutti i dettagli delle proprietà disponibili nella pagina di riferimento ValidityState; sotto elenchiamo alcuni dei più comuni:
    • patternMismatch: Restituisce true se il valore non è conforme al modello specificato in pattern, e false se è conforme. Se è true, l'elemento attiva la pseudoclasse CSS :invalid.
    • tooLong: Restituisce true se il valore supera la lunghezza massima specificata in maxlength, o false se è più corta o ugauale al massimo.Se è true, l'elemento attiva la pseudoclasse CSS :invalid.
    • tooShort: Restituisce true se il valore è più corto della dimensione minima specificata da minlength, o false se è uguale o maggiore del minimo. Se è true, l'elemento attiva la pseudoclasse CSS :invalid.
    • rangeOverflow: Restituisce true se il valore supera il massimo specificato da max, o false se è inferiore o uguale al massimo. Se è true, l'elemento attiva le pseudoclassi :invalid e :out-of-range.
    • rangeUnderflow: Restituisce true se il valore è inferiore al minimo specificato da min, o false se è maggiore o ugualeal minimo. Se è true, l'elemento attiva le pseudoclassi :invalid e :out-of-range.
    • typeMismatch: Restituisce true non rispetta la sintassi (quando type è email o url), o false se la sintassi è corretta. Se è true, l'elemento attiva la pseudoclasse CSS :invalid.
    • valid: Restituisce true se l'elemento rispetta tutti i parametri di validazione ed è quindi considerato valido, o false se non ne rispetta almeno uno. Se è true, l'elemento attiva la pseudoclasse CSS :valid; altimenti attiva la pseudocasse :invalid.
    • valueMissing: Restituisce true se l'elemento possiede l'attributo required, ma non è stato inserito un valore o false altirmenti. Se è true, l'elemento attiva la pseudoclasse CSS :invalid.
  • willValidate: Restituisce true se l'elemento verrà validato quando la form verrà spedita; altrimenti false.

Le API di validazione rendono anche disponibili i seguenti metodi per gli elementi di cui sopra.

  • checkValidity(): Restituisce true se il valore dell'elemento non ha problemi di validazione; altrimenti false. Se l'elemento non è valido viene anche lanciato un invalid event sul elemento.
  • setCustomValidity(message): Aggiunge un messaggio di errore personalizzato all'elemento; se si imposta il messaggio di errore personalizzato l'elemento viene considerato non valido, e viene visualizzato l'errore specificato. Questo consente di usare codice JavaScript per stabilire condizioni di errore di validazione diversi da quelli messi a disposizione dallo standard HTML5. Il messaggio viene esposto all'utente quando si presenta il problema.

Implementare un messaggio di errore personalizzato

Come abbiamo visto negli esempi di limiti di validazione HTML5 in precedenza, ogni volta che l'utente tenta di inviare una form non valida, il browser visualizza un messaggio d'errore. Il modo in cui viene visualizzato dipende dal browser.

Questi messaggi automatizzati hanno due controindicazioni:

  • Non c'è modo di cambiare il loro aspetto con CSS.
  • Essi dipendono dalla lingua del browser, che signfica che potresiti avere una pagina scritta in una lingua ed i messaggi di errore in un'altra come si vede in questa immagine presa da Firefox.

Example of an error message with Firefox in French on an English page

Personalizzare questi messaggi di errore è uno dei casi più comuni di utilizzo delle constraint validation API. Vediamo un piccolo esempio di come fare questo.

Incominciamo con un po' di HTML semplice (prova ad inserirlo in un file HTML vuoto o usa come base una copia di of fruit-start.html come preferisci):

<form>
  <label for="mail">I would like you to provide me with an e-mail address:</label>
  <input type="email" id="mail" name="mail">
  <button>Submit</button>
</form>

Ed aggiungi il seguente JavaScript alla pagina:

const email = document.getElementById("mail");

email.addEventListener("input", function (event) {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("I am expecting an e-mail address!");
  } else {
    email.setCustomValidity("");
  }
});

Qui aggiungiamo un riferimento al campo email ed aggiungiamo un event listener che viene eseguito ogni volta che il valore cambia.

Nel codici controlliamo se la proprietà validity.typeMismatch del campo emali diventa true, significa che il valore contenuto non corrisponde al modello degli indirizzi email. Se è così viene chiamato il metodo setCustomValidity() con un messaggio appropriato. Questo rende il campo non valido, in modo che quando viene inviata la form, l'invio fallisce e viene visualizzato il messaggio di errore.

Se la proprietà validity.typeMismatch restituisce false, chiamiamo il metodo setCustomValidity() con una stringa vuota che rende valido il campo in modo che possa essere spedito.

Lo puoi provare con:

Nota: Puoi trovare questo esempio live su GitHub su custom-error-message.html (vedi anche il codice sorgente.)

Un esempio più dettagliato

Ora che abbiamo visto un esempio molto semplice, vediamo come possiamo usare le API per costurire un sistema di validazione un po' più complesso.

Prima di tutto il codice HTML:

<form novalidate>
  <p>
    <label for="mail">
      <span>Please enter an email address:</span>
      <input type="email" id="mail" name="mail" required minlength="8">
      <span class="error" aria-live="polite"></span>
    </label>
  </p>
  <button>Submit</button>
</form>

Questa semplice form usa l'attributo novalidate per disattivare la validazine automatica del browser; questo consente al nostro script di prendere il controllo della validazione. Questo comunque non disabilita il supporto per le API di validazione e l'applicazione delle pseudoclassi CSS come :valid, ecc. Questo significa che se anche il browser con controlla la validità della form prima di spedire i dati, tu puoi comunque farlo dal solo ed applicare lo stile appropriato.

Il nostro input da validare è un <input type="email">, che è required, ed ha un minlength di 8 caratteri. Proviamo a controllare la validità usando il nostro codice e visualizziamo un messaggio appropriato per ciascun attributo.

Ci proponiamo di visualizzare il messaggio all'intermo di un elemento <span>. L'attributo aria-live per assicurarci che il nostro messaggio di errore verrà reso disponibile a tutti compresi coloro che usano i lettori di schermo.

Nota: Il punto chiave qui è l'uso dell'attributo novalidate per la form che è ciò che impedisce alla form di vidualizzare il proprio messaggio di errore e ci consente invece di visualizzare il nostro messaggio presonalizzato nel DOM in qualche modo scelto da noi.

Ora un po' di CSS oer migliorare leggermente il look della form, e fornire qualche tipo di informazione quando il valore non è valido:

body {
  font: 1em sans-serif;
  width: 200px;
  padding: 0;
  margin : 0 auto;
}

p * {
  display: block;
}

input[type=email]{
  -webkit-appearance: none;
  appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  box-sizing: border-box;
}

/* This is our style for the invalid fields */
input:invalid{
  border-color: #900;
  background-color: #FDD;
}

input:focus:invalid {
  outline: none;
}

/* This is the style of our error messages */
.error {
  width  : 100%;
  padding: 0;

  font-size: 80%;
  color: white;
  background-color: #900;
  border-radius: 0 0 5px 5px;

  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}

Ora vediamo il codice JavaScript che implementa l'errore personalizzato di validazione.

// There are many ways to pick a DOM node; here we get the form itself and the email
// input box, as well as the span element into which we will place the error message.
const form  = document.getElementsByTagName('form')[0];

const email = document.getElementById('mail');
const emailError = document.querySelector('#mail + span.error');

email.addEventListener('input', function (event) {
  // Each time the user types something, we check if the
  // form fields are valid.

  if (email.validity.valid) {
    // In case there is an error message visible, if the field
    // is valid, we remove the error message.
    emailError.innerHTML = ''; // Reset the content of the message
    emailError.className = 'error'; // Reset the visual state of the message
  } else {
    // If there is still an error, show the correct error
    showError();
  }
});

form.addEventListener('submit', function (event) {
  // if the email field is valid, we let the form submit

  if(!email.validity.valid) {
    // If it isn't, we display an appropriate error message
    showError();
    // Then we prevent the form from being sent by canceling the event
    event.preventDefault();
  }
});

function showError() {
  if(email.validity.valueMissing) {
    // If the field is empty
    // display the following error message.
    emailError.textContent = 'You need to enter an e-mail address.';
  } else if(email.validity.typeMismatch) {
    // If the field doesn't contain an email address
    // display the following error message.
    emailError.textContent = 'Entered value needs to be an e-mail address.';
  } else if(email.validity.tooShort) {
    // If the data is too short
    // display the following error message.
    emailError.textContent = `Email should be at least ${ email.minLength } characters; you entered ${ email.value.length }.`;
  }

  // Set the styling appropriately
  emailError.className = 'error active';
}

I commenti spiegano le cose per bene, ma brevemente:

  • Ogni volta che cambia il valore dell'input controlliamo per vedere se contiene dati validi. Se lo sono rimuoviamo eventuali messaggi di errore. Se invece ci sono errori eseguiamo showError() per inviare il messaggio appropriato.
  • Ogni volta che proviamo ad inviare la form controlliamo nuovamente se i dati sono validi. Se è così consentiamo la spedizione. Altrimenti eseguiamo showError() per visaulizzare il messaggio appropriato ed impedire la spedizione della form con preventDefault().
  • La funzione showError() utilizza varie proprietà della validity dell'input per determinare il tipo di errore e quindi visualizzare il messaggio appropriato.

Ecco il risultato live:

Nota: Puoi trovare questo esempio live su GitHub su detailed-custom-validation.html (vedi anche il codice sorgente.)

Le API di validazione ti forniscono uno stumento potente per gestire la validaizone delle form, fornendoti un grande controllo sulla interfaccia utente sopra e sotto quello che puoi fare con i soli HTML e CSS.

Nota: Per ulteriori ingormazioni vedi il nostro Constraint validation guide, ed il riferimento Constraint Validation API.

Validare le forms senza le built-in API

In alcuni casi come ad esempio per i browsers più vecchi o per il supporto ai custom controls, non sarà possibile usare le API di validazione. Potrai ancora utilizzare JavaScript per validare la tua form, ma devi scrivere tutto da solo.

Per validare la form fatti alcune domande:

Che tipo di validazione devo eseguire?
Devi determinare come validare i tuoi dati: operazioni sulle stringhe, conversioni di tipo, regular expressions, e via discorrendo. Sta tutto a te.
Cosa devo fare se la form non è valida?
Questo chiaramente è materia di UI. Devi decidere come si deve comportare la form. Deve la form spedire i dati ugualmente? Devi illuminare i campi che sono in errore? Devi visualizzare messaggi di errore?
Come posso aiutare l'utente a correggere i dati non validi?
Per ridurre la frustrazione dell'utente, è molto importante fornire il maggior numero di informazioni possibili per guidarlo a correggere gli errori. Dovresti fornire suggerimenti sui dati attesi ed anche messaggi di errore chiari e comprensibili. Se vuoi approfondire come approntare la UI adeguata per la validazione, ecco alcuni articoli utili che dovresti leggere:

Un esempio che non usa le API di validazione

Per illustrare questo, quello che segue è una versione semplificata dell'esempio precedente che funziona anche con i browsers più vecchi.

Il HTML è quasi uguale; abbiamo solo rimosso alcune funzionalità di validazione.

<form>
  <p>
    <label for="mail">
        <span>Please enter an email address:</span>
        <input type="text" class="mail" id="mail" name="mail">
        <span class="error" aria-live="polite"></span>
    </label>
  </p>
  <!-- Some legacy browsers need to have the `type` attribute
       explicitly set to `submit` on the `button`element -->
  <button type="submit">Submit</button>
</form>

Allo stesso modo, anche il CSS non necessita di grandi modifiche; abbiamo solo trasformato la pseudoclasse :invalid in una vera classe ed evitato di usare il selettore di attiributo che non funziona con Internet Explorer 6.

body {
  font: 1em sans-serif;
  width: 200px;
  padding: 0;
  margin : 0 auto;
}

form {
  max-width: 200px;
}

p * {
  display: block;
}

input.mail {
  -webkit-appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  box-sizing: border-box;
}

/* This is our style for the invalid fields */
input.invalid{
  border-color: #900;
  background-color: #FDD;
}

input:focus.invalid {
  outline: none;
}

/* This is the style of our error messages */
.error {
  width  : 100%;
  padding: 0;
 
  font-size: 80%;
  color: white;
  background-color: #900;
  border-radius: 0 0 5px 5px;
  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}

Le modifiche maggiori sono nel codice JavaScript, che richiede una revisione molto più pesante.

// There are fewer ways to pick a DOM node with legacy browsers
const form  = document.getElementsByTagName('form')[0];
const email = document.getElementById('mail');

// The following is a trick to reach the next sibling Element node in the DOM
// This is dangerous because you can easily build an infinite loop.
// In modern browsers, you should prefer using element.nextElementSibling
let error = email;
while ((error = error.nextSibling).nodeType != 1);

// As per the HTML5 Specification
const emailRegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

// Many legacy browsers do not support the addEventListener method.
// Here is a simple way to handle this; it's far from the only one.
function addEvent(element, event, callback) {
  let previousEventCallBack = element["on"+event];
  element["on"+event] = function (e) {
    const output = callback(e);

    // A callback that returns `false` stops the callback chain
    // and interrupts the execution of the event callback.
    if (output === false) return false;

    if (typeof previousEventCallBack === 'function') {
      output = previousEventCallBack(e);
      if(output === false) return false;
    }
  }
};

// Now we can rebuild our validation constraint
// Because we do not rely on CSS pseudo-class, we have to 
// explicitly set the valid/invalid class on our email field
addEvent(window, "load", function () {
  // Here, we test if the field is empty (remember, the field is not required)
  // If it is not, we check if its content is a well-formed e-mail address.
  const test = email.value.length === 0 || emailRegExp.test(email.value);

  email.className = test ? "valid" : "invalid";
});

// This defines what happens when the user types in the field
addEvent(email, "input", function () {
  const test = email.value.length === 0 || emailRegExp.test(email.value);
  if (test) {
    email.className = "valid";
    error.innerHTML = "";
    error.className = "error";
  } else {
    email.className = "invalid";
  }
});

// This defines what happens when the user tries to submit the data
addEvent(form, "submit", function () {
  const test = email.value.length === 0 || emailRegExp.test(email.value);

  if (!test) {
    email.className = "invalid";
    error.innerHTML = "I expect an e-mail, darling!";
    error.className = "error active";

    // Some legacy browsers do not support the event.preventDefault() method
    return false;
  } else {
    email.className = "valid";
    error.innerHTML = "";
    error.className = "error";
  }
});

Il risultato assomiglia a questo:

Come puoi vedere, non è proprio così difficile costruire un tuo sistema di validazione. La parte difficile è di renderlo abbastanza generico da essere usato su tutte le piattaforme e con ogni form che andarai a creare. Ci sono anche molte librerie pronte che ti aiuntano nella validazione come ad esempio Validate.js.

Metti alla prova le tue capacità!

Sei arrivato alla fine di questo articolo, ma riesci a ricordare le informazioni più importanti? Puoi trovare alcuni ulteriori test per verificare che tu abbia recepito questi informazioni prima di proseguire — vedi Test your skills: Form validation.

Sommario

La validazione delle form lato client alle volte richiede JavaScript se desideri configurare lo stile ed i messaggi di errore, ma richiede sempre che tu pensi attentamente all'utente. Ricordati sempre di guidare l'utente ad inserire dati corretti. Quindi assicurati di:

  • Visualizzare messaggi di errore espliciti.
  • Sii permissivo per i formati di input non essenziali.
  • Segnala in modo esatto il punto in cui si verifica l'errore soprattutto se la form è molto grande.

Quando hai controllato che la form è stata compilata correttamente, la puoi inviare. In seguito spieghiamo come spedire i dati delle form.

In questo modulo

Argomenti avanzati