Correction

Parent Previous Next



Correction



Bien, vous avez probablement terminé si vous lisez cette phrase. Ou bien vous n'avez pas réussi à aller jusqu'au bout, ce qui peut arriver !


Le corrigé au grand complet : HTML, CSS et Javascript


Nous pouvons maintenant passer à la correction. Pour ce TP, il vous fallait créer la structure HTML de votre page en plus du code Javascript ; voici le code que nous avons réalisé pour ce TP :

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8" />

    <title>TP : Un formulaire interactif</title>

  </head>

   

  <body>

     

    <form id="myForm">

 

      <span class="form_col">Sexe :</span>

      <label><input name="sex" type="radio" value="H" />Homme</label>

      <label><input name="sex" type="radio" value="F" />Femme</label>

      <span class="tooltip">Vous devez sélectionnez votre sexe</span>

      <br /><br />

 

      <label class="form_col" for="lastName">Nom :</label>

      <input name="lastName" id="lastName" type="text" />

      <span class="tooltip">Un nom ne peut pas faire moins de 2 caractères</span>

      <br /><br />

 

      <label class="form_col" for="firstName">Prénom :</label>

      <input name="firstName" id="firstName" type="text" />

 

      <span class="tooltip">Un prénom ne peut pas faire moins de 2 caractères</span>

      <br /><br />

 

      <label class="form_col" for="age">Âge :</label>

      <input name="age" id="age" type="text" />

      <span class="tooltip">L'âge doit être compris entre 5 et 140</span>

      <br /><br />

 

      <label class="form_col" for="login">Pseudo :</label>

      <input name="login" id="login" type="text" />

      <span class="tooltip">Le pseudo ne peut pas faire moins de 4 caractères</span>

      <br /><br />

 

      <label class="form_col" for="pwd1">Mot de passe :</label>

      <input name="pwd1" id="pwd1" type="password" />

      <span class="tooltip">Le mot de passe ne doit pas faire moins de 6 caractères</span>

 

      <br /><br />

 

      <label class="form_col" for="pwd2">Mot de passe (confirmation) :</label>

      <input name="pwd2" id="pwd2" type="password" />

      <span class="tooltip">Le mot de passe de confirmation doit être identique à celui d'origine</span>

      <br /><br />

 

      <label class="form_col" for="country">Pays :</label>

 

      <select name="country" id="country">

        <option value="none">Sélectionnez votre pays de résidence</option>

        <option value="en">Angleterre</option>

        <option value="us">États-Unis</option>

        <option value="fr">France</option>

      </select>

      <span class="tooltip">Vous devez sélectionner votre pays de résidence</span>

 

      <br /><br />

 

      <span class="form_col"></span>

      <label><input name="news" type="checkbox" /> Je désire recevoir la newsletter chaque mois.</label>

      <br /><br />

 

      <span class="form_col"></span>

      <input type="submit" value="M'inscrire" /> <input type="reset" value="Réinitialiser le formulaire" />

 

    </form>

     

  </body>

</html>


Vous remarquerez que de nombreuses balises <span> possèdent une classe nommée .tooltip. Elles contiennent le texte à afficher lorsque le contenu du champ les concernant ne correspond pas à ce qui est souhaité.


Nous allons maintenant passer au CSS. D'habitude nous ne vous le fournissons pas directement, mais cette fois il fait partie intégrante de ce TP, donc le voici :

body {

 padding-top: 50px;

}

 

.form_col {

  display: inline-block;

  margin-right: 15px;

  padding: 3px 0px;

  width: 200px;

  min-height: 1px;

  text-align: right;

}

 

input {

  padding: 2px;

  border: 1px solid #CCC;

  -moz-border-radius: 2px;

  -webkit-border-radius: 2px;

  border-radius: 2px;

  outline: none; /* Retire la bordure orange appliquée par certains navigateurs (Chrome notamment) lors du focus des éléments <input> */

}

 

input:focus {

  border-color: rgba(82, 168, 236, 0.75);

  -moz-box-shadow: 0 0 8px rgba(82, 168, 236, 0.5);

  -webkit-box-shadow: 0 0 8px rgba(82, 168, 236, 0.5);

  box-shadow: 0 0 8px rgba(82, 168, 236, 0.5);

}

 

.correct {

  border-color: rgba(68, 191, 68, 0.75);

}

 

.correct:focus {

  border-color: rgba(68, 191, 68, 0.75);

  -moz-box-shadow: 0 0 8px rgba(68, 191, 68, 0.5);

  -webkit-box-shadow: 0 0 8px rgba(68, 191, 68, 0.5);

  box-shadow: 0 0 8px rgba(68, 191, 68, 0.5);

}

 

.incorrect {

  border-color: rgba(191, 68, 68, 0.75);

}

 

.incorrect:focus {

  border-color: rgba(191, 68, 68, 0.75);

  -moz-box-shadow: 0 0 8px rgba(191, 68, 68, 0.5);

  -webkit-box-shadow: 0 0 8px rgba(191, 68, 68, 0.5);

  box-shadow: 0 0 8px rgba(191, 68, 68, 0.5);

}

 

.tooltip {

  display: inline-block;

  margin-left: 20px;

  padding: 2px 4px;

  border: 1px solid #555;

  background-color: #CCC;

  -moz-border-radius: 4px;

  -webkit-border-radius: 4px;

  border-radius: 4px;

}


Notez bien les deux classes .correct et .incorrect : elles seront appliquées aux <input> de type text et  password afin de bien montrer si un champ est correctement rempli ou non.


Nous pouvons maintenant passer au plus compliqué, le code Javascript :

(function() { // On utilise une IEF pour ne pas polluer l'espace global

     

    // Fonction de désactivation de l'affichage des « tooltips »

     

    function deactivateTooltips() {

     

        var spans = document.getElementsByTagName('span'),

        spansLength = spans.length;

         

        for (var i = 0 ; i < spansLength ; i++) {

            if (spans[i].className == 'tooltip') {

                spans[i].style.display = 'none';

            }

        }

     

    }

     

     

    // La fonction ci-dessous permet de récupérer la « tooltip » qui correspond à notre input

     

    function getTooltip(element) {

     

        while (element = element.nextSibling) {

            if (element.className === 'tooltip') {

                return element;

            }

        }

         

        return false;

     

    }

     

     

    // Fonctions de vérification du formulaire, elles renvoient « true » si tout est OK

     

    var check = {}; // On met toutes nos fonctions dans un objet littéral

     

    check['sex'] = function() {

     

        var sex = document.getElementsByName('sex'),

            tooltipStyle = getTooltip(sex[1].parentNode).style;

         

        if (sex[0].checked || sex[1].checked) {

            tooltipStyle.display = 'none';

            return true;

        } else {

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

    check['lastName'] = function(id) {

     

        var name = document.getElementById(id),

            tooltipStyle = getTooltip(name).style;

     

        if (name.value.length >= 2) {

            name.className = 'correct';

            tooltipStyle.display = 'none';

            return true;

        } else {

            name.className = 'incorrect';

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

    check['firstName'] = check['lastName']; // La fonction pour le prénom est la même que celle du nom

     

    check['age'] = function() {

     

        var age = document.getElementById('age'),

            tooltipStyle = getTooltip(age).style,

            ageValue = parseInt(age.value);

         

        if (!isNaN(ageValue) && ageValue >= 5 && ageValue <= 140) {

            age.className = 'correct';

            tooltipStyle.display = 'none';

            return true;

        } else {

            age.className = 'incorrect';

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

    check['login'] = function() {

     

        var login = document.getElementById('login'),

            tooltipStyle = getTooltip(login).style;

         

        if (login.value.length >= 4) {

            login.className = 'correct';

            tooltipStyle.display = 'none';

            return true;

        } else {

            login.className = 'incorrect';

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

    check['pwd1'] = function() {

     

        var pwd1 = document.getElementById('pwd1'),

            tooltipStyle = getTooltip(pwd1).style;

         

        if (pwd1.value.length >= 6) {

            pwd1.className = 'correct';

            tooltipStyle.display = 'none';

            return true;

        } else {

            pwd1.className = 'incorrect';

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

    check['pwd2'] = function() {

     

        var pwd1 = document.getElementById('pwd1'),

            pwd2 = document.getElementById('pwd2'),

            tooltipStyle = getTooltip(pwd2).style;

         

        if (pwd1.value == pwd2.value && pwd2.value != '') {

            pwd2.className = 'correct';

            tooltipStyle.display = 'none';

            return true;

        } else {

            pwd2.className = 'incorrect';

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

    check['country'] = function() {

     

        var country = document.getElementById('country'),

            tooltipStyle = getTooltip(country).style;

         

        if (country.options[country.selectedIndex].value != 'none') {

            tooltipStyle.display = 'none';

            return true;

        } else {

            tooltipStyle.display = 'inline-block';

            return false;

        }

     

    };

     

     

    // Mise en place des événements

     

    (function() { // Utilisation d'une fonction anonyme pour éviter les variables globales.

     

        var myForm = document.getElementById('myForm'),

            inputs = document.getElementsByTagName('input'),

            inputsLength = inputs.length;

     

        for (var i = 0 ; i < inputsLength ; i++) {

            if (inputs[i].type == 'text' || inputs[i].type == 'password') {

     

                inputs[i].onkeyup = function() {

                    check[this.id](this.id); // « this » représente l'input actuellement modifié

                };

     

            }

        }

     

        myForm.onsubmit = function() {

     

            var result = true;

     

            for (var i in check) {

                result = check[i](i) && result;

            }

     

            if (result) {

                alert('Le formulaire est bien rempli.');

            }

     

            return false;

     

        };

     

        myForm.onreset = function() {

     

            for (var i = 0 ; i < inputsLength ; i++) {

                if (inputs[i].type == 'text' || inputs[i].type == 'password') {

                    inputs[i].className = '';

                }

            }

     

            deactivateTooltips();

     

        };

     

    })();

     

     

    // Maintenant que tout est initialisé, on peut désactiver les « tooltips »

     

    deactivateTooltips();

 

})();



Les explications


Les explications vont essentiellement porter sur le code Javascript qui est, mine de rien, plutôt long (plus de deux cents lignes de code, ça commence à faire pas mal).


La désactivation des bulles d'aide


Dans notre code HTML nous avons créé des balises <span> avec la classe .tooltip. Ce sont des balises qui vont nous permettre d'afficher des bulles d'aide, pour que l'utilisateur sache quoi entrer comme contenu. Seulement, elles sont affichées par défaut et il nous faut donc les cacher par le biais du Javascript.


Et pourquoi ne pas les cacher par défaut puis les afficher grâce au Javascript ?


Si vous faites cela, vous prenez le risque qu'un utilisateur ayant désactivé le Javascript ne puisse pas voir les bulles d'aide, ce qui serait plutôt fâcheux, non ? Après tout, afficher les bulles d'aide par défaut et les cacher avec le Javascript ne coûte pas grand-chose, autant le faire… De plus, nous allons avoir besoin de cette fonction plus tard quand l'utilisateur voudra réinitialiser son formulaire.


Venons-en donc au code :

function deactivateTooltips() {

 

    var spans = document.getElementsByTagName('span'),

        spansLength = spans.length;

 

    for (var i = 0 ; i < spansLength ; i++) {

        if (spans[i].className == 'tooltip') {

            spans[i].style.display = 'none';

        }

    }

 

}

Est-il vraiment nécessaire de vous expliquer ce code en détail ? Il ne s'agit que d'un simple parcours de balises comme vous en avez déjà vu. Il est juste important de préciser qu'il y a une condition à la ligne 7 qui permet de ne sélectionner que les balises <span> qui ont une classe .tooltip.


Récupérer la bulle d'aide correspondant à un <input>


Il est facile de parcourir toutes les bulles d'aide, mais il est un peu plus délicat de récupérer celle correspondant à un  <input> que l'on est actuellement en train de traiter. Si nous regardons bien la structure de notre document HTML, nous constatons que les bulles d'aide sont toujours placées après l''<input> auquel elles correspondent, nous allons donc partir du principe qu'il suffit de chercher la bulle d'aide la plus « proche » après l'<input> que nous sommes actuellement en train de traiter. Voici le code :

function getTooltip(element) {

 

    while (element = element.nextSibling) {

        if (element.className === 'tooltip') {

            return element;

        }

    }

     

    return false;

 

}

Notre fonction prend en argument l'<input> actuellement en cours de traitement. Notre boucle while se charge alors de vérifier tous les éléments suivants notre <input> (d'où l'utilisation du  nextSibling). Une fois qu'un élément avec la classe .tooltip a été trouvé, il ne reste plus qu'à le retourner.


Analyser chaque valeur entrée par l'utilisateur


Nous allons enfin entrer dans le vif du sujet : l'analyse des valeurs et la modification du style du formulaire en conséquence. Tout d'abord, quelles valeurs faut-il analyser ? Toutes, sauf la case à cocher pour l'inscription à la newsletter. Maintenant que cela est clair, passons à la toute première ligne de code :

var check = {};


Alors oui, au premier abord, cette ligne de code ne sert vraiment pas à grand-chose, mais en vérité elle a une très grande utilité : l'objet créé va nous permettre d'y stocker toutes les fonctions permettant de « checker » (d'où le nom de l'objet) chaque valeur entrée par l'utilisateur. L'intérêt de cet objet est triple :


Nous n'allons pas étudier toutes les fonctions d'analyse, elles se ressemblent beaucoup, nous allons donc uniquement étudier deux fonctions afin de mettre les choses au clair :

check['login'] = function() {

 

    var login = document.getElementById('login'),

        tooltipStyle = getTooltip(login).style;

     

    if (login.value.length >= 4) {

        login.className = 'correct';

        tooltipStyle.display = 'none';

        return true;

    } else {

        login.className = 'incorrect';

        tooltipStyle.display = 'inline-block';

        return false;

    }

 

}


Il est très important que vous constatiez que notre fonction est contenue dans l'index login de l'objet check, l'index n'est rien d'autre que l'identifiant du champ de texte auquel la fonction appartient. Le code n'est pas bien compliqué : on récupère l'<input> et la propriété  style de la bulle d'aide qui correspondent à notre fonction et on passe à l'analyse du contenu.


Si le contenu remplit bien la condition, alors on attribue à notre <input> la classe .correct, on désactive l'affichage de la bulle d'aide et on retourne true.


Si le contenu ne remplit pas la condition, notre <input> se voit alors attribuer la classe .incorrect et la bulle d'aide est affichée. En plus de cela, on renvoie la valeur false.


Passons maintenant à une deuxième fonction que nous tenions à aborder :

check['lastName'] = function(id) {

 

    var name = document.getElementById(id),

        tooltipStyle = getTooltip(name).style;

 

    if (name.value.length >= 2) {

        name.className = 'correct';

        tooltipStyle.display = 'none';

        return true;

    } else {

        name.className = 'incorrect';

        tooltipStyle.display = 'inline-block';

        return false;

    }

 

};


Cette fonction diffère de la précédente sur un seul point : elle possède un argument id ! Cet argument sert à récupérer l'identifiant de l'<input> à analyser. Pourquoi ? Tout simplement parce qu'elle va nous servir à analyser deux champs de texte différents : celui du nom et celui du prénom. Puisqu'ils ont tous les deux la même condition, il aurait été stupide de créer deux fois la même fonction.


Donc, au lieu de faire appel à cette fonction sans aucun argument, il faut lui passer l'identifiant du champ de texte à analyser, ce qui donne deux possibilités :

check['lastName']('lastName');

check['lastName']('firstName');


Cependant, ce fonctionnement pose un problème, car nous étions partis du principe que nous allions faire appel à nos fonctions d'analyse selon le principe suivant :

check['id_du_champ']();


Or, si nous faisons ça, cela veut dire que nous ferons aussi appel à la fonction check['firstName']() qui n'existe pas… Nous n'allons pas créer cette fonction sinon nous perdrons l'intérêt de notre système d'argument sur la fonction check['lastName'](). La solution est donc de faire une référence de la manière suivante :

check['firstName'] = check['lastName'];


Ainsi, lorsque nous appellerons la fonction check['firstName'](), implicitement ce sera la fonction check['lastName']() qui sera appelée. Si vous n'avez pas encore tout à fait compris l'utilité de ce système, vous allez voir que tout cela va se montrer redoutablement efficace dans la suite du code.


La mise en place des événements : partie 1


La mise en place des événements se décompose en deux parties : les événements à appliquer aux champs de texte et les événements à appliquer aux deux boutons en bas de page pour envoyer ou réinitialiser le formulaire.

Nous allons commencer par les champs de texte. Tout d'abord, voici le code :

var inputs = document.getElementsByTagName('input'),

    inputsLength = inputs.length;

 

for (var i = 0 ; i < inputsLength ; i++) {

    if (inputs[i].type == 'text' || inputs[i].type == 'password') {

 

        inputs[i].onkeyup = function() {

            check[this.id](this.id); // « this » représente l'input actuellement modifié

        };

 

    }

}


Comme nous l'avons déjà fait plus haut, nous n'allons pas prendre la peine de vous expliquer le fonctionnement de cette boucle et de la condition qu'elle contient, il ne s'agit que de parcourir les  <input> et d'agir seulement sur ceux qui sont de type text ou  password.


En revanche, il va falloir des explications sur les lignes 7 à 9. Les lignes 7 et 9 permettent d'assigner une fonction anonyme à l'événement  keyup de l'<input> actuellement traité. Quant à la ligne 8, elle fait appel à la fonction d'analyse qui correspond à l'<input> qui a exécuté l'événement. Ainsi, si l'<input> #login déclenche son événement, il appellera alors la fonction  check['login']().


Cependant, un argument est passé à chaque fonction d'analyse que l'on exécute. Pourquoi ? Eh bien il s'agit de l'argument nécessaire à la fonction check['lastName'](), ainsi lorsque les <input>#lastNameet #firstName déclencheront leur événement, ils exécuteront alors respectivement les lignes de codes suivantes :

check['lastName']('lastName');

et

check['firstName']('firstName');


Mais là on passe l'argument à toutes les fonctions d'analyse, cela ne pose pas de problème normalement ?


Pourquoi cela en poserait-il un ? Imaginons que l'<input> #login déclenche son événement, il exécutera alors la ligne de code suivante :

check['login']('login');


Cela fera passer un argument inutile dont la fonction ne tiendra pas compte, c'est tout.


La mise en place des événements : partie 2


Nous pouvons maintenant aborder l'attribution des événements sur les boutons en bas de page :

(function() { // Utilisation d'une fonction anonyme pour éviter les variables globales.

 

var myForm = document.getElementById('myForm'),

    inputs = document.getElementsByTagName('input'),

    inputsLength = inputs.length;

 

for (var i = 0 ; i < inputsLength ; i++) {

    if (inputs[i].type == 'text' || inputs[i].type == 'password') {

 

        inputs[i].onkeyup = function() {

            check[this.id](this.id); // « this » représente l'input actuellement modifié

        };

 

    }

}

 

myForm.onsubmit = function() {

 

    var result = true;

 

    for (var i in check) {

        result = check[i](i) && result;

    }

 

    if (result) {

        alert('Le formulaire est bien rempli.');

    }

 

    return false;

 

};

 

myForm.onreset = function() {

 

    for (var i = 0 ; i < inputsLength ; i++) {

        if (inputs[i].type == 'text' || inputs[i].type == 'password') {

            inputs[i].className = '';

        }

    }

 

    deactivateTooltips();

 

};

 

})();

Comme vous pouvez le constater, nous n'avons pas appliqué d'événements click sur les boutons mais avons directement appliqué submit et reset sur le formulaire, ce qui est bien plus pratique dans notre cas.


Alors concernant notre événement submit, celui-ci va parcourir notre tableau check et exécuter toutes les fonctions qu'il contient (y compris celles qui ne sont pas associées à un champ de texte comme check['sex']() et check['country']()). Chaque valeur retournée par ces fonctions est « ajoutée » à la variable result, ce qui fait que si une des fonctions a renvoyé false alors result sera aussi à false et l'exécution de la fonction alert() ne se fera pas.


Concernant notre événement reset, c'est très simple : on parcourt les champs de texte, on retire leur classe et ensuite on désactive toutes les bulles d'aide grâce à notre fonction deactivateTooltips().


Voilà, ce TP est maintenant terminé.


Créé avec HelpNDoc Personal Edition: Créer des sites web d'aide facilement

Site à deux balles