L'API HTML5 de contrainte de validation

La validation native des formulaires côté client

TJ VanToll

Traducteur

Didier Mouronval

Cet article est la traduction de Constraint Validation: Native Client Side Validation for Web Forms publié sur le site HTML5 Rocks.

Commentez

- Titre : L'API HTML5 de contrainte de validation

- Auteur : TJ VanToll

- Traducteur : Didier Mouronval

- Parution : 16 décembre 2012

- Licence : Le contenu de cet article est rédigé par TJ VanToll et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- II. Qu'est-ce que la contrainte de validation ?

- III. L'API DOM

- III-A. willValidate

- III-B. validity

- III-C. validationMessage

- III-D. checkValidity()

- III-E. setCustomValidity()

- IV. Attributs HTML

- IV-A. novalidate

- IV-B. formnovalidate

- V. Règles CSS

- V-A. :invalid et :valid

- V-B. Redéfinir les styles par défaut

- V-C. Les bulles d'information

- V-D. Supprimer l'affichage par défaut

- VI. Problèmes et limitations des implémentations

- VI-A. setCustomValidity

- VI-A-1. Gérer des erreurs multiples sur un champ

- VI-A-2. Savoir quand vérifier la validité d'un champ

- VI-A-3. Savoir quand l'utilisateur tente de valider un formulaire

- VI-B. Safari

- VI-C. Messages d'erreur définis

- VI-D. L'attribut title

- VI-E. :invalid et :valid

- VI-F. Navigateurs non compatibles

- VII. Gérer les navigateurs non compatibles

- VII-A. Option 1 : se baser uniquement sur la validation côté serveur

- VII-B. Option 2 : solutions de remplacement

- VIII. Solutions de remplacement

- VIII-A. Webshims

- VIII-B. H5F

- IX. Conclusion et remerciements

I - Introduction

La validation des formulaires a longtemps été une expérience pénible de développement. Implémenter une validation côté client qui soit à la fois conviviale pour l'utilisateur, souple pour le développeur et accessible est compliqué. Avant HTML5, il n'existait aucune façon d'obtenir nativement une validation et les développeurs devaient passer par différentes solutions basées sur JavaScript.

Afin de soulager le développeur de ce fardeau, HTML5 a introduit le concept connu sous le nom de contrainte de validation. Une solution native pour implémenter la validation des formulaires Web côté client.

Actuellement, bien que disponible sur les dernières versions de tous les navigateurs, l'API de contrainte de validation est reléguée aux présentations et démonstrations. Mon but dans cet article est d'éclairer les développeurs sur les possibilités de cette nouvelle API et leur permettre de créer de meilleurs formulaires pour tout le monde.

Dans cet article, nous allons :

- présenter une vue d'ensemble détaillée de ce qu'est la contrainte de validation ;

- faire le point sur les limites actuelles au niveau de la spécification et des différentes implémentations ;

- voir comment utiliser la contrainte de validation HTML5 dans vos formulaires actuellement.

II - Qu'est-ce que la contrainte de validation ?

Le cœur de l'API de validation est un algorithme lancé par le navigateur lorsqu'un formulaire est soumis et permettant de déterminer si les valeurs qu'il contient sont valides. Pour effectuer cette vérification, l'algorithme utilise les nouveaux attributs HTML5 min, max, step, pattern et required ainsi que les attributs existants maxlength et type.

À titre d'exemple, prenez ce formulaire avec un champ texte et un attribut required :

<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Si vous essayez de soumettre ce formulaire tel quel, les navigateurs compatibles vont empêcher la soumission et afficher un des messages suivants (NdT les messages ne sont pas traduits) :

Selon la spécification, la façon dont le message est présenté à l'utilisateur est laissée à l'initiative du navigateur. Par ailleurs, la spécification prévoit une API DOM complète, de nouveaux attributs HTML et des règles CSS que les auteurs peuvent utiliser pour personnaliser l'interface.

III - L'API DOM

L'API de contrainte de validation ajoute les propriétés et méthodes suivantes aux éléments du DOM.

III-A - willValidate

La propriété willValidate indique si un élément sera concerné ou non par la validation. Pour les éléments pouvant être soumis, sa valeur sera true à moins que, pour une raison quelconque, l'élément ne soit pas éligible pour la validation, par exemple s'il possède l'attribut disabled.

<div id="one"></div>
<input type="text" id="two" />
<input type="text" id="three" disabled />
<script>
document.getElementById('one').willValidate; //undefined
document.getElementById('two').willValidate; //true
document.getElementById('three').willValidate; //false
</script>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

III-B - validity

La propriété validity d'un élément retourne un objet ValidityState contenant des propriétés booléennes relatives à la validité de la valeur de l'élément.

- customError: true si un message personnalisé a été défini avec setCustomValidity().

<input id="foo" />
<input id="bar" />
<script>
document.getElementById('foo').validity.customError; //false
document.getElementById('bar').setCustomValidity('Invalid');
document.getElementById('bar').validity.customError; //true
</script>

- patternMismatch: true si la propriété value n'est pas conforme à l'attribut pattern.

<input id="foo" pattern="[0-9]{4}" value="1234" />
<input id="bar" pattern="[0-9]{4}" value="ABCD" />
<script>
document.getElementById('foo').validity.patternMismatch; //false
document.getElementById('bar').validity.patternMismatch; //true
</script>

- rangeOverflow: true si la propriété value est supérieure à l'attribut max.

<input id="foo" type="number" max="2" value="1" />
<input id="bar" type="number" max="2" value="3" />
<script>
document.getElementById('foo').validity.rangeOverflow; //false
document.getElementById('bar').validity.rangeOverflow; //true
</script>

- rangeUnderflow: true si la propriété value est inférieure à l'attribut min.

<input id="foo" type="number" min="2" value="3" />
<input id="bar" type="number" min="2" value="1" />
<script>
document.getElementById('foo').validity.rangeUnderflow; //false
document.getElementById('bar').validity.rangeUnderflow; //true
</script>

- stepMismatch: true si la propriété value ne correspond pas à l'intervalle précisé par l'attribut step.

<input id="foo" type="number" step="2" value="4" />
<input id="bar" type="number" step="2" value="3" />
<script>
document.getElementById('foo').validity.stepMismatch; //false
document.getElementById('bar').validity.stepMismatch; //true
</script>

- tooLong: true si la propriété value est plus longue que l'attribut maxlength.

Tous les navigateurs empêchent ce cas de se produire normalement en interdisant la saisie si elle excède la valeur de maxlength. Cependant, dans de rares cas, cette propriété peut valoir true dans certains navigateurs. J'ai écrit un billet blog à ce sujet.

- typeMismatch: true si la propriété value ne correspond pas à l'attribut type.

<input id="foo" type="url" value="http://foo.com" />
<input id="bar" type="url" value="foo" />

<input id="foo2" type="email" value="foo@foo.com" />
<input id="bar2" type="email" value="bar" />

<script>
document.getElementById('foo').validity.typeMismatch; //false
document.getElementById('bar').validity.typeMismatch; //true

document.getElementById('foo2').validity.typeMismatch; //false
document.getElementById('bar2').validity.typeMismatch; //true
</script>

- valueMissing: true si l'élément possède l'attribut required et que sa propriété value est vide.

<input id="foo" type="text" required value="foo" />
<input id="bar" type="text" required value="" />
<script>
document.getElementById('foo').validity.valueMissing; //false
document.getElementById('bar').validity.valueMissing; //true
</script>

- valid: true si toutes les conditions listées ci-dessus valent false.

<input id="valid-1" type="text" required value="foo" />
<input id="valid-2" type="text" required value="" />
<script>
document.getElementById('valid-1').validity.valid; //true
document.getElementById('valid-2').validity.valid; //false
</script>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

III-C - validationMessage

La propriété validationMessage d'un élément contient le message que le navigateur doit afficher à l'utilisateur si la validation du champ échoue.

Chaque navigateur possède un message par défaut pour la langue de l'utilisateur. Si l'élément n'est pas éligible pour la validation ou s'il contient une valeur correcte, validationMessage contiendra une chaine vide.

Au moment d'écrire cet article (NdT 17 octobre 2012), Opera ne remplit pas correctement cette propriété. Le message est bien affiché, mais la propriété n'est pas remplie correctement.

<input type="text" id="foo" required />
<script>
document.getElementById('foo').validationMessage;
//Chrome  --> 'Please fill out this field.'
//Firefox --> 'Please fill out this field.'
//Safari  --> 'value missing'
//IE10    --> 'This is a required field.'
//Opera   --> ''
</script>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

III-D - checkValidity()

La méthode checkValidity s'applique à un élément de formulaire (input, select, textarea…) et renvoie true si la valeur de cet élément est valide.

Sur les éléments form, elle renvoie true si l'ensemble des données à valider qu'il contient est valide.

<form id="form-1">
<input id="input-1" type="text" required />
</form>
<form id="form-2">
<input id="input-2" type="text" />
</form>
<script>
document.getElementById('form-1').checkValidity();  //false
document.getElementById('input-1').checkValidity(); //false

document.getElementById('form-2').checkValidity();  //true
document.getElementById('input-2').checkValidity(); //true
</script>

De plus, à chaque fois que la validité d'un champ est vérifiée avec checkValidity et que le test échoue, un événement invalid est déclenché pour cet élément. Avec l'exemple ci-dessus, si vous vouliez lancer une action lorsque l'élément input-1 est vérifié et est invalide, vous pourriez faire :

document.getElementById('input-1').addEventListener('invalid', function() {
//...
}, false);

Il n'existe pas d'événement valid, mais vous pouvez utiliser l'événement change pour afficher vos notifications sur un élément :

document.getElementById('input-1').addEventListener('change', function(event) {
if (event.target.validity.valid) {
//Field contains valid data.
} else {
//Field contains invalid data.
}
}, false);

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

III-E - setCustomValidity()

La méthode setCustomValidity modifie la propriété validationMessage et vous permet de modifier les règles de validation.

Puisqu'elle permet de modifier validationMessage, si vous lui affectez une chaine vide rend le champ valide alors que lui affecter une chaine non vide rend le champ invalide. Malheureusement, il n'est pas possible de modifier validationMessage sans modifier la validité du champ.

Par exemple, si vous avez deux champs mot de passe qui doivent être égaux, vous pouvez faire :

if (document.getElementById('password1').value != document.getElementById('password2').value) {
document.getElementById('password1').setCustomValidity('Passwords must match.');
} else {
document.getElementById('password1').setCustomValidity('');
}

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

IV - Attributs HTML

Nous avons déjà vu que les attributs maxLength, min, max, step, pattern, et type sont utilisés par le navigateur pour vérifier la validité du formulaire. Pour la contrainte de validation, deux autres attributs peuvent être utilisés : novalidate et formnovalidate.

IV-A - novalidate

L'attribut booléen novalidate est appliqué à un formulaire. Lorsqu'il est présent, il permet d'indiquer que ce champ n'aura pas besoin d'être vérifié lors de la soumission du formulaire.

<form novalidate>
<input type="text" required />
<input type="submit" value="Submit" />
</form>

Puisque le formulaire possède l'attribut novalidate, il sera soumis malgré la présence de l'attribut required sur le champ texte.

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

IV-B - formnovalidate

L'attribut booléen formnovalidate s'applique à un élément submit de formulaire (button ou input).

<form>
<input type="text" required />
<input type="submit" value="Validate" />
<input type="submit" value="Do NOT Validate" formnovalidate />
</form>

Lorsque le bouton « Validate » est cliqué, le formulaire n'est pas soumis si le champ texte est vide. En revanche, lors du clic sur le bouton « Do NOT Validate », le formulaire est bien soumis du fait de son attribut formnovalidate.

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

V - Règles CSS

Créer une validation de formulaire efficace ne se limite pas à gérer les erreurs, cela consiste aussi à afficher ces erreurs de façon utile. Les navigateurs compatibles permettent l'utilisation de CSS pour améliorer cet affichage.

V-A - :invalid et :valid

Pour les navigateurs compatibles, la pseudoclasse :valid va cibler les éléments respectant leur règle de validation alors que la pseudoclasse :invalid ciblera ceux pour lesquels ce n'est pas le cas.

<form>
<input type="text" id="foo" required />
<input type="text" id="bar" />
</form>
<script>
document.querySelectorAll('input[type="text"]:invalid'); //Matches input#foo
document.querySelectorAll('input[type="text"]:valid');   //Matches input#bar
</script>

V-B - Redéfinir les styles par défaut

Par défaut, Firefox applique une propriété box-shadow et IE10 une propriété outline rouge aux champs :invalid.

Par défaut, les navigateurs basés sur Webkit et Opera n'appliquent aucun style à ces éléments.
Si vous voulez un affichage similaire quel que soit le navigateur, vous pouvez supprimer les styles par défaut.

:invalid {
box-shadow: none; /* FF */
outline: 0;       /* IE 10 */
}

J'ai proposé une demande de pull pour débattre de savoir si cette unification était du ressort de normalize.css.

V-C - Les bulles d'information

Une contradiction majeure d'affichage est le style de la bulle d'erreur affichée par les navigateurs. Webkit est le seul moteur de rendu permettant leur personnalisation. Pour Webkit, vous pouvez utiliser ces quatre pseudoclasses :

::-webkit-validation-bubble {}
::-webkit-validation-bubble-message {}
::-webkit-validation-bubble-arrow {}
::-webkit-validation-bubble-arrow-clipper {}

V-D - Supprimer l'affichage par défaut

Puisque seul Webkit permet de personnaliser le style d'affichage des erreurs, si vous souhaitez un affichage identique sur tous les navigateurs, la seule solution est de supprimer l'affichage par défaut et de créer le vôtre. Le code suivant permet de désactiver l'affichage par défaut des erreurs.

var forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('invalid', function(e) {
e.preventDefault();
// Créez votre affichage d'erreur ici.
}, true);
}

Si vous désactivez les messages par défaut, faites bien attention de mettre en place votre propre affichage pour avertir les utilisateurs en cas d'erreur. Actuellement, les bulles d'information sont les seuls éléments que mettent en place les navigateurs pour prévenir les utilisateurs qu'ils se sont trompés.

VI - Problèmes et limitations des implémentations

Bien que cette nouvelle API apporte beaucoup pour la validation des formulaires côté client, vous pourrez être confrontés à certaines limitations.

VI-A - setCustomValidity

S'il s'agit juste de définir validationMessage sur un champ, setCustomValidity fonctionne correctement. Mais si le formulaire devient plus complexe, setCustomValidity montre des limites.

VI-A-1 - Gérer des erreurs multiples sur un champ

Affecter la propriété validationMessage sur un champ se contente de redéfinir validationMessage. Ainsi, si vous affectez setCustomValidity une seconde fois, vous réaffecterez juste la valeur de la propriété. Il n'y a pas de mécanisme permettant de gérer une pile d'erreurs ou de moyen d'afficher plusieurs messages.

Une solution possible pour afficher des messages d'erreurs distincts à l'utilisateur est la suivante :

var foo = document.getElementById('foo');
foo.setCustomValidity(foo.validationMessage + ' An error occurred');

Vous ne pouvez pas affecter de HTML ou de caractères de formatage, donc le message ressemblera à ça :

VI-A-2 - Savoir quand vérifier la validité d'un champ

Pour illustrer ce problème, prenons le code suivant, avec un formulaire contenant deux champs password qui doivent être identiques :

<form>
<fieldset>
<legend>Change Your Password</legend>
<ul>
<li>
<label for="password1">Password 1:</label>
<input type="password" required id="password1" />
</li>
<li>
<label for="password2">Password 2:</label>
<input type="password" required id="password2" />
</li>
</ul>
<input type="submit" />
</fieldset>
</form>

Ma précédente proposition était d'utiliser l'événement change pour effectuer la vérification, ce qui peut se coder comme suit :

var password1 = document.getElementById('password1');
var password2 = document.getElementById('password2');

var checkPasswordValidity = function() {
if (password1.value != password2.value) {
password1.setCustomValidity('Passwords must match.');
} else {
password1.setCustomValidity('');
}
};

password1.addEventListener('change', checkPasswordValidity, false);
password2.addEventListener('change', checkPasswordValidity, false);

Avec ce code, lorsque l'un des champs est modifié, sa validité est vérifiée. Pourtant, envisageons un script qui remplit automatiquement ces champs ou encore un script qui modifie des attributs de validation comme pattern, required, min, max ou step. De tels scripts affecteraient la validité des champs mais aucun événement ne permet de les détecter.

Nous aurions besoin d'un événement permettant de savoir quand une règle de validité a été modifiée.

VI-A-3 - Savoir quand l'utilisateur tente de valider un formulaire

Pourquoi ne pas utiliser l'événement submit pour le savoir ? L'événement submit n'est pas déclenché tant que le navigateur n'a pas déterminé que tous les champs à valider sont corrects. Ainsi, il n'est pas possible de savoir si l'utilisateur a tenté de valider son formulaire, mais que le navigateur l'en a empêché.

Il serait utile de savoir quand l'utilisateur tente de soumettre un formulaire. Vous pourriez vouloir montrer à l'utilisateur une liste de messages d'erreur, changer le focus ou afficher des messages d'aide. Malheureusement, vous aurez besoin de code supplémentaire pour faire cela.

L'une des façons d'y arriver est d'ajouter au formulaire l'attribut novalidate et d'utiliser l'événement submit. Du fait de l'attribut novalidate, la soumission du formulaire ne sera pas empêchée par un champ non validé. Ensuite, ce sera au script de vérifier la validité du formulaire lors de l'événement submit et d'empêcher, si besoin, la soumission.
Voici comment pourrait s'écrire un tel script dans le cas précédent de deux champs password devant être identiques :

<form id="passwordForm" novalidate>
<fieldset>
<legend>Changer votre mot de passe</legend>
<ul>
<li>
<label for="password1">Mot de passe :</label>
<input type="password" required id="password1" />
</li>
<li>
<label for="password2">Confirmez :</label>
<input type="password" required id="password2" />
</li>
</ul>
<input type="submit" />
</fieldset>
</form>
<script>
var password1 = document.getElementById('password1');
var password2 = document.getElementById('password2');

var checkPasswordValidity = function() {
if (password1.value != password2.value) {
password1.setCustomValidity('Les mots de passe doivent être identiques.');
} else {
password1.setCustomValidity('');
}
};

password1.addEventListener('change', checkPasswordValidity, false);
password2.addEventListener('change', checkPasswordValidity, false);

var form = document.getElementById('passwordForm');
form.addEventListener('submit', function() {
checkPasswordValidity();
if (!this.checkValidity()) {
event.preventDefault();
// Ajoutez ici la gestion de vos messages d'erreur.
password1.focus();
}
}, false);
</script>

L'inconvénient ici est que l'utilisation de l'attribut novalidate empêche l'affichage des messages par le navigateur. De ce fait, si vous utilisez cette technique, vous devez prévoir votre propre gestion d'affichage des erreurs.
Voici un exemple pour faire cela.

Nous aurions besoin d'un événement forminvalid qui serait déclenché lors de l'annulation d'une soumission de formulaire si les données sont invalides.

VI-B - Safari

Même si Safari supporte l'API de validation, au moment de l'écriture de cet article (version 6), Safari n'empêche pas l'envoi du formulaire si les données sont invalides. Pour l'utilisateur, Safari se comporte comme un navigateur ne supportant pas l'API.

La parade la plus simple est de faire comme dans le cas précédent et d'ajouter l'attribut novalidate à tous les formulaires et de vérifier soi-même les données en empêchant la soumission avec preventDefault.
Le code suivant montre comment mettre cela en place :

var forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
forms[i].noValidate = true;

forms[i].addEventListener('submit', function(event) {
//Prevent submission if checkValidity on the form returns false.
if (!event.target.checkValidity()) {
event.preventDefault();
// Ajoutez ici la gestion de vos messages d'erreur.
}
}, false);
}

Il existe plusieurs bogues connus où ChackValidity renvoie des faux positifs (voir ici ou ici par exemple). Les faux positifs sont problématiques parce que l'utilisateur sera bloqué alors que ses données sont justes, il faut y penser !

VI-C - Messages d'erreur définis

Même si vous avez la possibilité de modifier un message d'erreur avec validationMessage et setCustomValidity, cela peut être gênant de devoir obligatoirement utiliser JavaScript pour définir ces messages, surtout sur de grands formulaires.

Pour vous aider, Firefox a introduit un attribut x-moz-errormessage qui permet d'affecter directement la propriété validationMessage d'un champ.

<form>
<input type="text" required x-moz-errormessage="Fill this out." />
<input type="submit" value="Submit" />
</form>

Lorsque le formulaire ci-dessus est soumis, le message d'erreur affiché par Firefox sera personnalisé :

Cette fonctionnalité a été proposée au W3C mais a été refusée. Pour l'instant, Firefox est le seul navigateur permettant de personnaliser de la sorte les messages d'erreur.

VI-D - L'attribut title

Bien que l'attribut title ne modifie pas le message de validationMessage, les navigateurs affichent sa valeur dans la bulle d'information (s'il est présent) en cas de patternMismatch. Notons que Chrome affiche la valeur de l'attribut title quelle que soit l'erreur de validation, pas uniquement pour patternMismatch.

Par exemple, si vous essayez de soumettre le formulaire suivant :

<form>
<label for="price">Price: $</label>
<input type="text" pattern="[0-9].[0-9][0-9]"
title="Please enter the price in x.xx format (e.g. 3.99)"
id="price" value="3" />
<input type="submit" value="Submit" />
</form>

Voici ce que vous pourrez obtenir dans les différents navigateurs :

VI-E - :invalid et :valid

Comme nous l'avons déjà évoqué, la pseudoclasse :valid va cibler tous les éléments qui satisfont leurs contraintes de validation et :invalid va cibler ceux qui ne la satisfont pas. Malheureusement, ces pseudoclasses sont affectées immédiatement, c'est-à-dire avant une tentative de soumission. Prenons l'exemple suivant :

<style>
:invalid {
border: 1px solid red;
}
:valid {
border: 1px solid green;
}
</style>
<form>
<input type="text" required />
<input type="text" />
</form>

Le but ici est d'ajouter une bordure rouge sur les champs invalides et verte sur les champs valides. Mais la bordure apparaît dès l'affichage du formulaire, or des tests sur la facilité d'utilisation de formulaires ont montré que le meilleur moment pour donner des informations à l'utilisateur est immédiatement après qu'ils ont rempli un champ, pas avant.

Une solution pour faire cela sur l'exemple précédent est d'ajouter une classe CSS aux champs après qu'ils ont été remplis et de n'appliquer les styles qu'aux éléments ayant cette classe.

<style>
.interacted:invalid {
border: 1px solid red;
}
.interacted:valid {
border: 1px solid green;
}
</style>
<form>
<input type="text" required />
<input type="text" />
<input type="submit" />
</form>
<script>
var inputs = document.querySelectorAll('input[type=text]');
for (var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('blur', function(event) {
event.target.classList.add('interacted');
}, false);
}
</script>

Firefox a compris cette problématique et implémenté deux pseudoclasses additionnelles : :-moz-ui-invalid et :-moz-ui-valid. À la différence de :invalid et :valid, ces pseudoclasses ne cibleront pas d'éléments tant que ceux-ci n'ont pas été modifiés ou que l'utilisateur tente de valider le formulaire.

Cette évolution a été intégrée dans la spécification des sélecteurs CSS niveau 4 avec le sélecteur :user-error qui fonctionne quasiment comme :-moz-ui-invalid. Il ne reste plus qu'à attendre l'implémentation par les navigateurs.

Pour terminer, faites attention que :invalid et :valid sont supposés cibler les balises <form> aussi bien que les champs. Actuellement, seul Firefox le fait, mais pensez-y lorsque vous définissez des règles globales pour ces pseudoclasses.

VI-F - Navigateurs non compatibles

L'API de validation est plutôt bien supportée par les navigateurs, mais il en existe malgré tout qui ne sont pas compatibles. Parmi eux, les versions d'Internet Explorer inférieures à 8, Safari de iOS et le navigateur Android par défaut.

VII - Gérer les navigateurs non compatibles

Si vous souhaitez utiliser l'API de contrainte de validation sur un formulaire d'un site en production, vous devez obligatoirement gérer les navigateurs non compatibles. D'après mon expérience, deux solutions sont possibles.

VII-A - Option 1 : se baser uniquement sur la validation côté serveur

Il est important de se souvenir que même avec l'API de validation, il est nécessaire d'effectuer des vérifications côté serveur. Les utilisateurs mal intentionnés peuvent facilement contourner toutes les validations côté client et le protocole HTTP n'impose pas qu'une requête vienne d'un navigateur.

De ce fait, la validation côté client ne peut être considérée que comme une amélioration progressive pour l'utilisateur. Tout formulaire doit être utilisable même en l'absence de validation par le navigateur.

Puisque vous devez obligatoirement valider les valeurs reçues côté serveur, cette validation peut renvoyer des messages d'erreur qui seront affichés à l'utilisateur et cela peut être considéré comme une solution valable pour les navigateurs n'implémentant pas l'API de validation.

VII-B - Option 2 : solutions de remplacement

Si se contenter de la validation côté serveur est suffisant pour certaines applications, pour beaucoup d'autres, se passer de validation côté client pour les navigateurs non conformes n'est pas envisageable.

Si vous souhaitez utiliser cette API et que les navigateurs non compatibles se comportent de façon identique, le meilleur moyen est d'utiliser un polyfill. En plus de permettre de simuler les fonctionnalités de l'API pour les navigateurs non conformes, la plupart de ces solutions de remplacement vont plus loin et corrigent certains défauts des implémentations natives.

VIII - Solutions de remplacement

Il existe de nombreux scripts de remplacement vous permettant d'utiliser l'API de validation quel que soit le navigateur. Je vais vous présenter deux des plus populaires.

VIII-A - Webshims

Webshims est une collection de scripts de remplacement dont une partie correspond aux formulaires HTML5 et à l'API de validation.

Pour montrer comment fonctionne Webshims, reprenons le premier exemple avec un simple champ texte ayant l'attribut required.

<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>

Pour permettre de le faire fonctionner sur tous les navigateurs, nous devons inclure Webshims et les scripts dont il dépend dans la page, puis appeler $.webshims.polyfill('forms').

<script src="js/jquery-1.8.2.js"></script>
<script src="js/modernizr-yepnope-custom.js"></script>

<script src="js-webshim/minified/polyfiller.js"></script>

<script>jQuery.webshims.polyfill('forms');</script>

<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>

Pour les navigateurs compatibles, il n'y aura aucun changement dans le résultat. En revanche, les navigateurs non compatibles vont maintenant pouvoir empêcher la soumission du formulaire et afficher un message d'erreur. Voici ce que cela rendra dans certains navigateurs :

En plus de rendre compatibles tous les navigateurs, Webshims propose des solutions à beaucoup de limitations dont nous avons parlé :

- spécifier des messages d'erreur via l'attribut data-errormessage ;

- mise en œuvre de classes form-ui-valid et form-ui-invalid qui se comportent comme :-moz-ui-valid et :-moz-ui-invalid ;

- mise en œuvre d'événements firstinvalid, lastinvalid, changedvalid, et changedinvalid ;

- gestion correcte des faux positifs de Webkit lors de la soumission.

Pour plus d'informations sur les possibilités offertes par Webshims, regarder la documentation sur les formulaires HTML5.

VIII-B - H5F

H5F est une solution de remplacement légère et ne dépendant d'aucun autre script. Elle implémente l'API de validation complète ainsi qu'un bon nombre de nouveaux attributs.

Voici comment intégrer H5F dans votre page et gérer un formulaire simple :

<script src="H5F.js"></script>

<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>
<script>
H5F.setup(document.getElementsByTagName('form'));
</script>

H5F va empêcher la soumission du formulaire s'il contient des données non valides, mais les utilisateurs ne verront la bulle d'information que pour les navigateurs qui supportent nativement cette fonctionnalité. Comme H5F reproduit l'API de validation complète, vous pouvez gérer l'interface utilisateur comme bon vous semble.

Vous pouvez consulter la page d'exemples de H5F pour voir comment ajouter une bulle d'information à droite des champs concernés. J'ai aussi créé un exemple montrant comment afficher une liste de messages d'erreur en haut du formulaire.

En plus d'intégrer une solution de remplacement pour l'API de validation, H5F propose des classes reproduisant le comportement de :-moz-ui-valid et :-moz-ui-invalid. Par défaut, ces classes sont valid et error, mais vous pouvez les personnaliser dans le second paramètre de H5F.setup.

H5F.setup(document.getElementById('foo'), {
validClass: 'valid',
invalidClass: 'invalid'
});

Pour plus d'informations sur H5F, consulter la page Github du projet.

IX - Conclusion et remerciements

L'API HTML5 de contrainte de validation permet d'ajouter une validation des champs de formulaires côté client tout en mettant à disposition une API JavaScript et des règles CSS pour la personnalisation.

Bien qu'il existe quelques problèmes concernant l'implémentation et la gestion des navigateurs anciens, avec une bonne solution de remplacement ou une prise en charge alternative côté serveur, vous pouvez d'ores et déjà utiliser cette API dans vos formulaires.

Cet article est la traduction de Constraint Validation: Native Client Side Validation for Web Forms publié sur le site HTML5 Rocks.

Nous tenons à remercier ClaudeLELOUP et zoom61 pour leur relecture attentive de cet article.