Validation de formulaire

Question fréquente
Maintenant que vous vous êtes bien reposé avec la page précédente, et que vous êtes toujours impatient d'écrire des programmes, nous allons continuer avec un peu de travaux pratiques, mais comme je suis à cours d'idées géniales, que les exemples de scripts sur le web sont minables et sans intérêt, et que les quelques questions que j'ai reçu à propos du javascript concernent presque toutes ce sujet, voici la technique miracle du: contrôle-de-formulaire-avant-de-l'envoyer-bêtement-vide-ou-mal-rempli.

Le côté HTML
Voici donc un bête formulaire et la façon classique de le construire, des leds (totalement inutiles) montrant la validation des champs uniquement pour le plaisir des yeux et en caractères rouges (toujours sans supplément de prix), les petits ajouts HTML pour lancer les scripts java:

<FORM METHOD="POST" ACTION="mailto:xxx@yyy.zzz"
onSubmit="return validation(this)">

<IMG SRC="LEDVoff.gif" NAME="Led_Nom" BORDER="0">
Nom: <INPUT TYPE="TEXT" NAME="Nom" SIZE="20"
onChange="teste_vide(this,'Le nom manque','Led_Nom')"><BR>
Nom:
<IMG SRC="LEDVoff.gif" NAME="Led_Age" BORDER="0">
Age: <INPUT TYPE="TEXT" NAME="Age" SIZE="4"
onChange="teste_valeur(this,'L\'âge manque ou est incorrect
(7 à 77 ans)',7,77,'Led_Age')"
><BR>
Age:
<IMG SRC="LEDVoff.gif" NAME="Led_Email" BORDER="0">
Adresse E-Mail: <INPUT TYPE="TEXT" NAME="Email" SIZE="20"
onChange="teste_email(this,'L\'adresse E-Mail manque
ou est incorrecte,'Led_Email')"
><BR>
Adresse E-Mail:
<IMG SRC="LEDMon.gif" NAME="Led_Reset" BORDER="0">
<INPUT TYPE="RESET" VALUE="Effacement" onClick="raz(this)"><BR>
<IMG SRC="LEDRon.gif" NAME="Led_Envoi" BORDER="0">
<INPUT TYPE="SUBMIT" VALUE="Envoi"><BR>
</FORM>

Comment ça fonctionne ?
Maintenant que vous avez bien joué avec le formulaire (du moins je l'espère), des explications s'imposent.
  • <FORM METHOD="POST" ACTION="mailto:xxx@yyy.zzz">, du vulgaire HTML, rappel: METHOD peut être soit GET, soit POST en fonction du serveur utilisé.
    Mais, onSubmit="return validation(this)" fait qu'en cas de tentative de soumission du formulaire on appelle la routine "validation" (qu'il va falloir écrire) et on lui passe le paramètre "this".
    A noter que l'on pourrait aussi profiter de la routine de validation pour afficher un panneau de confirmation ou de remerciements.
    Le paramètre this est un mot clef très particulier de javascript, puisque qu'il désigne toujours l'objet courant, ce qui est bien pratique pour éviter d'avoir à écrire la tartine "window.document.form[0]" dans ce cas précis pour désigner cet objet.
    Le mot clef "return" devant la routine "validation" indique que cette routine retourne une valeur (en l'occurrence "true" ou "false", soit vrai ou faux) ce qui permet d'envoyer ou pas le formulaire en fonction du résultat.
  • <IMG SRC="LEDVoff.gif" NAME="Led_Nom" BORDER="0"> Nom: <INPUT TYPE="TEXT" NAME="Nom" SIZE="20"><BR>, là non plus pas de surprise, on affiche une image de led éteinte et on place un champ texte de nom "Nom".
    Mais, onChange="teste_vide(this,'Le nom manque','Led_Nom')", fait qu'en cas de changement du contenu du champ on appelle la routine "teste_vide" (qu'il faut encore écrire) et on lui passe les paramètres: "this", du texte pour le cas ou le champ est vide et le nom de la led à changer d'état.
  • <IMG SRC="LEDVoff.gif" NAME="Led_Age" BORDER="0"> Age: <INPUT TYPE="TEXT" NAME="Age" SIZE="4"><BR>, on affiche une image de led éteinte et on place un champ texte plus petit, de nom "Age".
    Mais, onChange="teste_valeur(this,'L\'âge manque ou est incorrect (7 à 77 ans)',7,77,'Led_Age')", fait qu'en cas de changement du contenu du champ on appelle la routine "teste_valeur" (qu'il faut encore écrire) et on lui passe les paramètres: "this", du texte pour le cas ou le champ est vide ou hors limites, la valeur mini, la valeur maxi et le nom de la led à changer d'état.
    Le backslash ("\") entre le "L" et l'apostrophe de "âge" n'est pas une coquille, il sert à afficher l'apostrophe elle même et indique également au programme de ne pas l'utiliser comme délimitation. Cette astuce est à utiliser partout où une chaîne de caractère contient un ou plusieurs guillemets.
  • <IMG SRC="LEDVoff.gif" NAME="Led_Email" BORDER="0"> Adresse E-Mail: <INPUT TYPE="TEXT" NAME="Email" SIZE="20"><BR>, on affiche une image de led éteinte et on place un champ texte dans lequel on espère recevoir une adresse E-Mail valide.
    Mais, onChange="teste_email(this,'L\'adresse E-Mail manque ou est incorrecte','Led_Email')", fait qu'en cas de changement du contenu du champ on appelle la routine "teste_email" (qu'il faut encore écrire) et on lui passe les paramètres: "this", du texte pour le cas où le contenu du champ n'est pas correct et le nom de la led à changer d'état.
    Il n'est pas vraiment possible de tester si une adresse donnée existe en javascript, mais on peut contrôler au moins la présence du célèbre caractère "@" (arobas, ou en provençal: arobasseu, peuchêre) et d'au moins un point dans la partie droite de l'adresse, de manière à valider les choses comme "xxx@yyy.zzz", de toutes façons on pourait toujours donner une adresse valide de quelqu'un d'autre pour tricher, mais n'en profitez pas pour abonner votre pire ennemi à vie chez France-loisir...
  • <IMG SRC="LEDMon.gif" NAME="Led_Reset" BORDER="0"> <INPUT TYPE="RESET" VALUE="Effacement"><BR> , permet simplement de retourner à la case départ et serait bien plus utile si le formulaire en question contenait des valeurs par défaut pénibles à retaper "à la main". Et je ne me donne même pas la peine de changer la couleur de la led reset, d'où l'absence de script à ce sujet.
    Mais, onClick="raz(this)", éteint les autres leds et remet la mémoire de leur état à zéro en appelant la routine "raz" (qu'il faudra encore se taper) en lui envoyant le paramètre "this" qui ne lui sert strictement à rien et dont elle n'a rien à foutre, mais je me suis fait avoir par la force de l'habitude...
  • <IMG SRC="LEDRon.gif" NAME="Led_Envoi" BORDER="0"> <INPUT TYPE="SUBMIT" VALUE="Envoi"><BR>, la cerise sur le gâteau, ici aussi point de script, puisque ce sont les autres objets qui déterminent l'état de la led, on pourrait aussi invalider le bouton "envoi" lui-même (seulement avec msie) tant que les champs ne sont pas remplis correctement puisque une des propriétés des objets "bouton" le permet, Je vous laisse chercher laquelle à titre d'exercice, il n'y a pas de raison que je me tape tout le boulot.
  • </FORM> Ouf, c'est fini.

Les scripts
Et maintenant fini de rire ! Le vrai cerveau ce sont les scripts, et ce n'est pas avec les quelques petits machins en rouge que vous avez rajouté dans les lignes HTML de votre mesquin formulaire que le travail va se faire.

Je rassure tout de suite les flemmards, pas besoin de tout récrire, un simple copier/coller fera l'affaire, mais si vous voulez aussi comprendre ce que vous faites, il va falloir dépenser un peu de phosphore.

  • Premier script: teste_vide(objet,texte,image led)
    
    function teste_vide(objet,texte,led)
    {
      with (objet)                                // Pour l'objet en paramètre
      {
        if (value==null || value=='')             // Si valeur numérique est nulle
        {                                         // ou si pas de caractère, alors
          document.images[led].src="LEDVoff.gif"; // Éteint la led verte
          document.images['Led_Envoi'].src="LEDRon.gif"; //Allume la led rouge
          flag[led]=false;                        // Mémorise faux pour ce champ
          if (texte!="") {alert(texte);}          // Si il y a du texte, l'affiche
          objet.select();                         // Resélectionne le champ
          objet.focus();                          // Resélectionne le contenu
          return false;                           // Retourne faux
        }
        else                                      // Sinon
        {
          flag[led]=true;                         // Mémorise OK pour ce champ
          document.images[led].src="LEDVon.gif";  // Allume la led
          if (flag['Led_Nom']==true && flag['Led_Age']==true // Si autres champs OK
            && flag['Led_Email']==true)
            {                                     // Passe led Envoi en vert
              document.images['Led_Envoi'].src="LEDVon.gif";
            }
          return true;                            // Retourne vrai
        }
      }
    }
    

    Quelques explications supplémentaires ne feront pas de mal.
    • Le "//" indique à javascript que tout ce qui suit est un commentaire et ne doit pas être exécuté, commenter un programme permet de s'y retrouver plus facilement.
    • Le "==" indique à javascript qu'il doit tester la variable et non pas lui affecter la valeur qui suit (ce que l'on fait avec juste "=").
    • Le "||" signifie "OU", si un test OU l'autre est vrai, ALORS la suite (entre les "{}") est exécutée, sinon le programme continue après le "}".
    • Si la valeur du champ est nulle, on éteint la led verte du champ concerné (au cas ou elle aurait été allumée précédemment) et on allume la led rouge du bouton d'envoi, puisqu'au moins un champ n'est plus valide.
    • On mémorise ensuite l'état de la led dans le tableau de nom "flag" (drapeau en français) avec flag[led]=false;
    • Si il y a du texte à afficher, on l'affiche dans une requête de type "alert".
    • On resélectionne et active ensuite le champ incriminé.
    • Et on retourne l'état du champ (non valide) avec return false; nous verrons pourquoi plus loin.
    • Si la valeur du champ n'est pas nulle, on mémorise l'état de la led dans le tableau de nom "flag" avec flag[led]=true;
    • On allume la led verte du champ en question.
    • On teste si tous les champs sont valides, le "&&" signifie "ET", si oui on passe la led envoi en vert.
    • Et on retourne l'état du champ (valide) avec return true;

  • Second script: teste_valeur(objet,texte,min,max,led)
    
    function teste_valeur(objet,texte,min,max,led)
    {
      with (objet)
      {
        valeur=parseInt(value);    // Lit la valeur et compare avec les limites
        if (parseInt(min)!=min || valeur<min || parseInt(max)!=max
          || valeur>max || value!=valeur)
        {
          document.images[led].src="LEDVoff.gif"; // Si la valeur est hors limites
          document.images['Led_Envoi'].src="LEDRon.gif"; // Comme script 1
          flag[led]=false;
          if (texte!="") {alert(texte);}
          objet.select();
          objet.focus();
          return false;
        }
        else // Si elle est dans les limites on fait comme dans le script 1
        {
          flag[led]=true;
          document.images[led].src="LEDVon.gif";
          if (flag['Led_Nom']==true && flag['Led_Age']==true
            && flag['Led_Email']==true)
            {
              document.images['Led_Envoi'].src="LEDVon.gif";
            }
          return true;
        }
      }
    }
    

    Il y a très peu de différences par rapport au script précédent, ici on lit la valeur numérique du champ grâce à la fonction parseInt(), "value" étant tout simplement la propriété du contenu du champ (objet document.forms[0].texts[1].value), celle du minimum et celle du maximum, puis on contrôle simplement si la valeur du champ se trouve entre ces limites et si ce n'est pas du texte grâce à l'astuce diabolique: "|| value!=valeur" (la valeur numérique d'un texte est "0", et 0 est forcément différent du texte).

  • Test adresse E-Mail: teste_email(objet,texte,image led)
    
    function teste_email(objet,texte,led)
    {
      with (objet)
      {
        apos=value.indexOf("@");        // Cherche position de "@"
        dotpos=value.lastIndexOf(".");  // Cherche dernière position de "."
        lastpos=value.length-1;         // Contrôle si l'adresse est vraisemblable
        if (apos<1 || dotpos-apos<2 || lastpos-dotpos>3 || lastpos-dotpos<2)
        {
          document.images[led].src="LEDVoff.gif"; // Comme scripts 1 et 2
          document.images['Led_Envoi'].src="LEDRon.gif";
          flag[led]=false;
          if (texte) {alert(texte);}
          objet.select();
          objet.focus();
          return false;
        }
        else // Comme script 1 et 2
        {
          flag[led]=true;
          document.images[led].src="LEDVon.gif";
          if (flag['Led_Nom']==true && flag['Led_Age']==true
            && flag['Led_Email']==true)
            {
              document.images['Led_Envoi'].src="LEDVon.gif";
            }
          return true;
        }
      }
    }
    

    Même chose ici que ci-dessus, sauf que l'on s'intéresse uniquement à la présence et à la position respective des caractère "@" et "." et au nombre de caractères du dernier groupe qui doit être compris entre deux et trois.
    Je n'entrerai pas plus loin dans le détail pour le moment pour ne pas devenir trop rasoir ou vous larguer.

  • Test de validation: validation(objet)
    
    function validation(objet)
    {
      with (objet)
      {
        if (teste_vide(Nom,'Indiquez votre nom s\'il vous plaît.','Led_Nom')==false)
        {
          Nom.select(); Nom.focus(); return false;
        }
        if (teste_valeur(Age,'L\'indication de l\'âge est obligatoire.',7,77,'Led_Age')==false)
        {
          Age.select(); Age.focus(); return false;
        }
        if (teste_email(Email,'Vous oubliez d\'indiquer votre adresse E-Mail.','Led_Email')==false)
        {
          Email.select(); Email.focus(); return false;
        }
        return true;
      }
    }
    

    Ici on teste simplement tour à tour chaque champ à valider du formulaire (chaque fonction retournant true ou false) et si tous sont corrects on retourne true (vrai) et cela permet l'exécution complète du bouton submit, sinon le premier champ incorrect trouvé on sélectionne le champ incriminé et on retourne false (faux) pour arrêter l'envoi du message par submit.

  • Remize à zéro des leds et mémoires: raz(objet)
    
    function raz(objet)
    {
      with (objet)
      {
        document.images['Led_Nom'].src="LEDVoff.gif";
        flag['Led_Nom']=false;
        document.images['Led_Age'].src="LEDVoff.gif";
        flag['Led_Age']=false;
        document.images['Led_Email'].src="LEDVoff.gif";
        flag['Led_Email']=false;
        document.images['Led_Envoi'].src="LEDRon.gif";
      }
    }
    

    On éteint chaque led et on remet la mémoire de celles-ci à zéro (en fait à vide, avec =''), ces mémoires servent uniquement à allumer la led envoi en vert si tous les champs sont bien remplis, les champs eux sont automatiquement vidés (ou remis à la valeur de défaut) grâce au HTML par le navigateur.

  • Vous croyez peut-être que c'est enfin terminé ?
    Et bien non, mais ne jetez pas l'éponge, vous y êtes presque, il reste une dernière bricole à faire, quand on utilise un tableau, c'est comme à la douane, il faut le déclarer, de plus vous l'avez peut-être déjà oublié, mais les routines ci dessus sont à placer entre des marqueurs <SCRIPT> et </SCRIPT> entre <HEAD> et </HEAD>.
    Ajoutez-y donc la routine suivante: init()
    
    function init()
    {
      flag=new Array(3);
      flag['Led_Nom']=false;
      flag['Led_Age']=false;
      flag['Led_Email']=false;
      document.forms[0].Nom.select();
      document.forms[0].Nom.focus();
    }
    

    Qui sert uniquement à déclarer un tableau de nom "flag" et de nombre de cases égal à 4 ! (et oui le tableau part de zéro, faites le compte) et à sélectionner et mettre en évidence le premier champ du formulaire à remplir.
    S'il y a des gens qui ne dorment pas encore, ils se seront rendu compte qu'il n'y a encore rien qui exécute cette fonction, bravo !

    Dernier bout de programme (et c'est vraiment le dernier): <BODY onLoad="init();"> </BODY>, ouf !!!

    Voici en exclusivité galactique, voire universelle (sauf si quelqu'un me l'a piqué) le squelette complet de ce projet, vous pourrez le copier/coller mais vous devrez supprimer les images ou changer les noms de fichier, ainsi que l'adresse de destination du formulaire.


Pas très compliqué
Tous ces tests de validation sont malgré leur apparence terriblement simplistes, pour ne pas dire simplets, mais même si vous voulez effectuer des calculs plus complexe, le principe reste le même, il vous faudra juste vous enfoncer encore plus profondément dans les fonctions mathématiques et les opérateurs de comparaisons.

La page suivante vous donne enfin la barre de navigation en haut de la page, mais n'en profitez pas pour sauter des leçons.


RetourInfo Suite

Dernière mise à jour de cette page: 04/09/2001, visiteurs: depuis le 6 avril 2002