Débuter avec jQuery

La bibliothèque qui simplifie l'interaction


précédentsommairesuivant

9. Un exemple récapitulatif

Dans cette dernière partie, nous allons reprendre notre exemple du chapitre 5 et le compléter en y ajoutant de nouvelles fonctionnalités.

Cet exemple comportant des traitements AJAX appelant des scripts PHP, vous devrez, contrairement aux autres exemples de ce livre, disposer d'un serveur pour le tester. Un serveur local de type WAMP (pour Windows, Apache, MySQL, PHP) sera amplement suffisant. Une version en ligne est toutefois également disponible à l'adresse : http://dmouronval.developpez.com/digitbooks/jquery/.

Notez que le but de ces exemples est de montrer le fonctionnement de jQuery, de ce fait, la partie serveur est limitée au strict minimum : le code PHP se contente de renvoyer des données prédéfinies en fonc- tion des paramètres reçus. Il ne fait pas appel à une base de données et n'effectue que des vérifications minimalistes des entrées utilisateur. Les scripts PHP ne sont donc là que pour montrer des exemples de ce qu'est sensé renvoyer comme résultat un traitement côté serveur dans une requête AJAX.

L'exemple du chapitre 5 a donc été modifié en remplaçant le contenu des deux premiers onglets par des formulaires du type de ceux que l'on peut trouver dans un formulaire d'identification / inscription à un site.

Concernant la navigation des onglets, seul un effet a été conservé (celui de glissement latéral).

Le premier onglet est constitué d'un onglet de type identification. Si l'on est déjà inscrit, il suffit d'y entrer son identifiant et son mot de passe. Si ceux-ci sont corrects, alors un message de bienvenue apparaît, ainsi qu'une option de déconnexion. Si l'on n'est pas encore inscrit, un lien permet d'accéder directement à la partie inscription (en complément du clic sur les onglets).

Le deuxième onglet est donc un formulaire d'inscription. Les informations demandées sont succinctes : un identifiant, un mot de passe, le nom et le prénom, ainsi qu'une adresse mail valide.

Une fois tous les champs remplis, on envoie les informations au serveur qui renvoie une portion de page affichant ces informations.

Le troisième onglet, qui correspondrait à une partie informative (mentions légales sur la conservation des données par exemple), est resté inchangé avec du texte de type « Lorem ipsum ».

Le code HTML et CSS correspondant peut être consulté dans le fichier jquery-8-1.html joint.

9-1. Information sur les formats de données

En règle générale, dans un formulaire, il est essentiel de bien définir le format des valeurs que l'on attend pour chaque champ où cela est possible. Il est donc important, si l'on fixe des règles, de les faire connaître.

L'un des soucis majeurs en termes de sécurité sur un site Internet est de filtrer les données reçues de l'utilisateur. En effet, si une personne malveillante arrive sur votre formulaire d'inscription, elle se doutera que les informations seront stockées dans une base de données, elle tentera donc d'envoyer vers cette base de données des informations sans rapport avec celles attendues, mais susceptibles de lui donner le contrôle sur votre site (voir les attaques par injection SQL). Toutes les informations reçues doivent donc être considérées par défaut comme non sûres et vérifiées. Pour se faciliter la tâche, imposer un certain format aux informations demandées est souvent une bonne technique. La subtilité résidant en un équilibre entre suffisamment de liberté pour l'utilisateur couplée à une vérification aisée.

Dans notre exemple, nous avons donc ajouté des bulles d'information qui apparaîtront au survol des textes associés aux champs (balises <label>) indiquant les caractères autorisés pour chaque champ.

Au niveau du code HTML, les informations à afficher sont regroupées dans des balises <span>, ajoutées à la fin de la page.

 
Sélectionnez
<span class="tip" id="tip-login">Entrez le login que vous avez
renseigné à l'inscription</span>
<span class="tip" id="tip-mdp">Entrez le mot de passe que vous avez
renseigné à l'inscription</span>
<span class="tip" id="tip-pseudo">Votre login doit contenir des lettres
              sans accent ou des chiffres et comporter au  
              moins trois caractères</span>
<span class="tip" id="tip-pass">Votre mot de passe doit contenir
            des lettres sans accent ou des chiffres et  
            comporter au moins six caractères</span>
<span class="tip" id="tip-nom">Votre nom doit comporter des lettres,
espaces et traits d'union</span>
<span class="tip" id="tip-prenom">Votre prénom doit comporter des
              lettres, espaces et traits d'union</span>
<span class="tip" id="tip-mail">Merci de préciser une adresse e-mail
              valide, par exemple : nom@domaine.com</span>

Chacune de ces balises contient une classe CSS (tip) permettant de leur associer un style (en particulier de les masquer par défaut). D'autre part, elles possèdent aussi un identifiant rappelant le champ auquel elles sont associées.

Elles ont été placées à part dans le code HTML pour faciliter leur positionnement par rapport à la page. En effet, pour pouvoir les afficher à un endroit précis, il est nécessaire de leur affecter la propriété CSS position à absolute. Or ce type de positionnement affiche l'élément concerné en fonction de la position de son plus proche élément parent ayant lui-même une propriété CSS position autre que static (valeur par défaut). Comme le contenu de nos onglets est déjà positionné (balise #contenu en relative), il serait nécessaire d'effectuer des calculs supplémentaires pour afficher les infobulles si elles étaient situées dans cet élément. D'autre part, pour d'autres raisons, la propriété CSS overflow de la balise contenu est fixée à hidden (pour les besoins de certains effets), ce qui fait que les bulles d'informations seraient susceptibles d'être partiellement masquées si elles étaient contenues dans cette balise.

Image non disponible
Figure 8.1. Formulaire avec infobulle

Le code permettant d'afficher ou de masquer les infobulles est le suivant :

 
Sélectionnez
// Gestion des bulles d'information des labels
var tip
$('#contenu').delegate('label', 'hover', function(e){
  if(e.type == 'mouseenter'){
    tip = $('#tip-' + $(this).attr('for'));
    tip.css({
      'top': (e.pageY + 20) + 'px',
      'left': (e.pageX + 10) + 'px'
    });
    tip.fadeTo('fast', 0.9);
    $(this).mousemove(function(e){
      tip.css({
        'top': (e.pageY + 20) + 'px',
        'left': (e.pageX + 10) + 'px'
      });
    });
  }
  else{
    tip.hide();
    tip.unbind('mousemove');
  }
});

Nous commençons par créer une variable tip qui contiendra la bulle affichée.

Nous affectons ensuite la gestion de différents événements aux balises <label>. Cependant, une fois l'utilisateur identifié, les formulaires disparaissent (ils seront stockés et retirés du DOM). Donc, si l'on affectait les événements directement aux balises <label>, ces événements disparaîtraient en cas de déconnexion ; il faudrait donc les réaffecter. Nous préférons ainsi utiliser l'événement delegate() sur le premier ancêtre qui ne sera pas modifié, à savoir l'élément #contenu.

Nous commençons par affecter la gestion du survol (hover). Comme nous sommes dans le cadre de delegate(), il n'est pas possible d'indiquer deux fonctions de rappel comme c'était le cas avec la méthode .hover().

Nous allons donc différencier deux cas, selon la valeur de la propriété type de l'objet event associé. Cette propriété indique le nom de événement qui a été déclenché et permet de distinguer l'affichage ou le masquage de la bulle.

Si l'on entre sur la balise associée, alors on stocke dans la variable tip l'information affichée. On lui affecte ensuite des propriétés CSS de positionnement pour l'afficher au niveau de la souris, là encore, nous utilisons les propriétés de l'objet event (top et left). Notez le décalage de 20 et 10 pixels pour éviter le chevauchement sur le pointeur de souris et pour permettre d'afficher la bulle en-dessous du champ de formulaire associé.

Nous souhaitons ensuite que la bulle se déplace en même temps que la souris, nous affectons donc un traitement sur l'événement mousemove pour repositionner la bulle.

Il ne reste plus qu'à faire apparaître la bulle.

Lorsque l'on sort de la balise <label>, on se contente de masquer la bulle puis d'annuler l'action sur l'événement mousemove. Si nous ne faisions pas cette annulation, à chaque survol, un nouvel événement serait ajouté ; cela ne nuirait pas, à priori, au fonctionnement en lui-même, mais serait dommageable en termes de performances puisque le même calcul serait fait inutilement autant de fois que de survol sur la balise.

9-2. Vérifier les valeurs des champs

La vérification des champs de formulaire avant l'envoi de celui-ci est une technique très courante. Il s'agit de vérifier, avant l'envoi des données, si les valeurs renseignées correspondent au format attendu.

Important : Les vérifications faites en JavaScript ont pour seul objectif d'éviter de faire un aller-retour inutile vers le serveur et d'informer un utilisateur bien intentionné que les valeurs renseignées sont incomplètes ou erronées.

En aucun cas ces vérifications ne peuvent être considérées comme fiables : un utilisateur mal intentionné aura toujours la possibilité de passer outre ces vérifications.

La seule validation rigoureuse est celle qui sera faite au niveau du serveur ! Gardez en mémoire cette maxime valable quel que soit le type d'information reçue : « ne jamais croire les entrées utilisateur ! (Never Trust User Input) ".

Habituellement, la vérification des champs de formulaire se fait en listant les champs à contrôler et en associant à chacun un type de format dont on vérifiera la correspondance avec un modèle donné. Ce contrôle se fait habituellement à l'aide des expressions régulières.

La portion de script effectuant la vérification est la suivante :

 
Sélectionnez
// Empêche la validation des formulaires : tout est géré en AJAX
$('#contenu').delegate('form', 'submit', function(){
  return false;
});
// Vérification du format des données avant envoi
$('#contenu').delegate('#connect', 'click', function(){
  var erreurs = '';
  if(!$('#login').val()){
    erreurs += '<li>Vous devez indiquer un identifiant.</li>';
  }
  else if(!/^[a-z0-9]{3,}$/i.test($('#login').val())){
    erreurs += '<li>Le format de votre identifiant n\'est pas
              correct.</li>';
  }
  if(!$('#mdp').val()){
    erreurs += '<li>Vous devez indiquer un mot de passe.</li>';
  }
  else if(!/^[a-z0-9]{6,}$/i.test($('#mdp').val())){
    erreurs += '<li>Le format de votre mot de passe n\'est pas
            correct.</li>';
  }
  if(erreurs){
    $('#errIdentif').html('<ul>'+erreurs+'</ul>');
    $('#errIdentif').find('ul').delay(5000).fadeOut('slow', function(){
      $(this).remove();
    });
    return false;
  }
  var params = $('#connexion').serialize();
  sendAjax('ajax/identif.php', params, '#errIdentif');
});
$('#contenu').delegate('#inscript', 'click', function(){
  var erreurs = '';
  if(!$('#pseudo').val()){
    erreurs += '<li>Vous devez indiquer un identifiant.</li>';
  }
  else if(!/^[a-z0-9]{3,}$/i.test($('#pseudo').val())){
    erreurs += '<li>Le format de votre identifiant n\'est pas
              correct.</li>';
  }
  if(!$('#pass').val()){
    erreurs += '<li>Vous devez indiquer un mot de passe.</li>';
  }
  else if(!/^[a-z0-9]{6,}$/i.test($('#pass').val())){
    erreurs += '<li>Le format de votre mot de passe n\'est pas
              correct.</li>';
  }
  if(!$('#nom').val()){
    erreurs += '<li>Vous devez indiquer votre nom.</li>';
  }
  else if(!/^[a-zàâäçéèêëîïôöûü\-\s]+$/i.test($('#nom').val())){
    erreurs += '<li>Le format de votre nom n\'est pas correct.</li>';
  }
  if(!$('#prenom').val()){
    erreurs += '<li>Vous devez indiquer votre prénom.</li>';
  }
  else if(!/^[a-zàâäçéèêëîïôöûü\-\s]+$/i.test($('#prenom').val())){
    erreurs += '<li>Le format de votre prénom n\'est pas correct.</li>';
  }
  if(!$('#mail').val()){
    erreurs += '<li>Vous devez indiquer votre adresse mail.</li>';
  }
  else if(!/^[-+.\w]{1,64}@[-.\w]{1,64}\.[-.\w]{2,6}$/i.test($('#mail').val())){
    erreurs += '<li>Le format de votre adresse mail n\'est pas
              correct.</li>';
  }
  if(erreurs){
    $('#errInscr').html('<ul>'+erreurs+'</ul>');
    $('#errInscr').find('ul').delay(10000).fadeOut('slow', function(){
      $(this).remove();
    });
    return false;
  }
  var params = $('#inscription').serialize();
  sendAjax('ajax/inscription.php', params, '#errInscr');
});

Tout d'abord, puisque les traitements des formulaires seront faits à l'aide d'AJAX, on commence par inhiber les actions submit des formulaires contenus dans la balise #contenu.

Nous affectons un gestionnaire sur chacun des boutons de soumission. Ici encore, les formulaires disparaissant une fois l'identification (ou l'inscription) effectuée, nous affectons les événements à la balise #contenu via la méthode delegate() afin que la vérification soit toujours effective après déconnexion.

Il est possible de se créer des méthodes complètes de validation, en fonction du type de champ (numérique, alphanumérique, e-mail, etc.) puis de l'utiliser en distinguant pour chaque champ à quel type il correspond (via une classe CSS par exemple). Différents plugins existant pour faire cela et comme nos champs ont tous un format attendu différent, nous nous contenterons, pour chacun, de vérifier qu'il correspond au masque prédéfini.

Le champ identifiant attend au moins trois lettres et / ou chiffres. L'expression régulière correspondante est :

 
Sélectionnez
/^[a-z0-9]{3,}$/i

L'expression régulière se compose de deux parties : le masque de recherche, entre les deux slashs puis éventuellement un ou plusieurs modificateurs.

À l'intérieur de l'expression, l'accent circonflexe (^) signifie « le début de la chaîne », le dollar à la fin ($) signifiant lui « la fin de la chaîne ». Ici, les deux signes étant présents, la recherche doit se faire sur l'intégralité de la chaîne à tester, s'ils n'étaient pas présents, une occurrence du masque de recherche sur une partie de la chaîne à tester serait valide.

La partie entre crochets correspond à une classe de caractères, c'est-à-dire que l'on va rechercher la présence de l'un des caractères s'y trouvant, ici, les notations a-z et 0-9 correspondent à des plages de valeurs, soit entre le caractère a et le caractère z (selon le code ASCII) et entre 0 et 9. On va donc vérifier si la chaîne à tester contient soit une lettre soit un chiffre.

La partie entre accolades correspond à un quantificateur. Deux valeurs, séparées par une virgule peuvent y être précisées : le nombre minimum d'occurrences et le nombre maximum. Ici, la seconde valeur n'est pas précisée, on recherche donc au moins trois caractères correspondant à la classe précédente. Notez qu'il existe des quantificateurs prédéfinis : «  ? » correspond à 0 ou 1 occurrence (soit {0,1}) ; « * » correspond à 0 ou plusieurs occurrences (soit {0,}) ; « + » correspond à une fois ou plus (soit {1,}).

Enfin, le modificateur i signifie que la recherche se fait sans tenir compte de la casse.

Le format du mot de passe est identique, sauf qu'il doit contenir au moins six caractères.

Attention, sur un vrai site, un mot de passe ne contenant que des chiffres ou des lettres est considéré comme peu sécurisé !

Pour les nom et prénom, le masque sera légèrement différent. En effet, les chiffres ne sont plus acceptés, en revanche, on peut accepter les caractères accentués, des espaces et des traits d'union pour les noms ou prénoms composés. L'expression régulière correspondante est :

 
Sélectionnez
/^[a-zàâäçéèêëîïôöûü\-\s]+$/i

Notez que les caractères accentués doivent être précisés un par un : il n'existe pas de plage correspondante ni de classe prédéfinie. D'autre part, nous aurions pu mettre un espace littéral dans la classe, mais nous avons préféré mettre une classe prédéfinie : \s qui correspond à tout type de caractère d'espacement. D'autres classes prédéfinies existent, par exemple \d pour les chiffres (équivalent de [0-9]) ou \w pour les caractères alphanumériques (attention, cette classe n'inclue pas les caractères accentués, mais inclut le souligné (underscore), équivalent :

 
Sélectionnez
[a-zA-Z0-9_]).

Enfin, le format de l'adresse mail est plus complexe. La norme concernant les adresses e-mail est très complexe à mettre sous forme d'expression régulière. La plupart des expressions vérifiant les formats des adresses e-mails sont en réalité des restrictions (souvent acceptables) de la norme. Dans notre exemple, nous avons créé le masque suivant :

 
Sélectionnez
/^[-+.\w]{1,64}@[-.\w]{1,64}\.[-.\w]{2,6}$/i

Ce qui correspond à un premier bloc de une à 64 fois les caractères alphanumériques, le souligné, le tiret, le plus ou le point, suivi du caractère arobase, puis le nom de domaine, acceptant la même classe que la précédente sans le plus, suivi d'un dernier point et de l'extension, de deux à six caractères alphanumériques incluant éventuellement un point ou un tiret. Le tout quelle que soit la casse.

À chaque vérification, si le format ne correspond pas, on affecte à un booléen la valeur false pour indiquer qu'une erreur a été trouvée et on rempli une chaîne correspond à un message d'erreur.

Si, à la fin des vérifications, le booléen erreurs vaut false, on se contente d'afficher le message d'erreur créé (voir figure 8.2)

Image non disponible
Figure 8.2. Messages d'erreur

Si aucune erreur n'a été détectée, alors on utilise la méthode serialize() sur le formulaire traité et on envoie les valeurs au serveur via AJAX.

9-3. Liens de navigation entre onglets

Pour les deux formulaires, nous avons ajouté des liens permettant de passer d'un formulaire à l'autre (ces liens sont en fait des doublons de la barre d'onglets).

 
Sélectionnez
// Liens pour passer d'un onglet à l'autre
$('#contenu').delegate('.identification', 'click', function(){
  $('#onglets li:eq(0)').mouseenter();
  $('#onglets li:eq(0)').click();
});
$('#contenu').delegate('.inscription', 'click', function(){
  $('#onglets li:eq(1)').mouseenter();
  $('#onglets li:eq(1)').click();
});
// Effet de survol des liens de navigation dans le cadre 
// (couleur rouge du texte)
$('#contenu').delegate('.identification,.inscription', 'hover', function(e)
{
      $(this).css('color', e.type == 'mouseleave' ? '' : 'red');
    }
);

Au clic des liens, on simule d'abord le survol de l'onglet correspondant avec la méthode mouseenter() afin de provoquer l'effet sur celui-ci (sinon, le nouvel onglet affiché ne semblerait pas actif dans la barre) puis on simule le clic sur cet onglet.

Enfin, on ajoute un effet de survol des liens en modifiant la couleur d'affichage du texte.

Notez qu'encore une fois, nous devons affecter ces événements avec la méthode delegate() afin de préserver le comportement en cas de connexion puis de déconnexion.

9-4. Traitement sur le serveur

La page d'identification (identif.php) vérifie, pour les champs login et mdp, s'ils sont présents puis s'ils contiennent les bonnes valeurs. Les seules valeurs reconnues sont « jquery » pour l'identifiant et « createur » pour le mot de passe, correspondant à l'utilisateur John Resig (le créateur de jQuery). Si les valeurs ne sont pas présentes ou différentes de celles attendues, des messages d'erreur sont concaténés.

À la fin des vérifications, on renvoie un code d'erreur (ici 0), puis un séparateurs (les caractères « <> »), puis le message d'erreur.

Si aucune erreur n'est rencontrée, on renvoie un code de réussite (1), le séparateur, puis le contenu du premier onglet contenant le message de bienvenue personnalisé pour l'utilisateur, de nouveau le séparateur et enfin le contenu du deuxième onglet contenant les informations liées à l'utilisateur identifié.

La page d'inscription fonctionne de façon similaire : on vérifie que chaque valeur attendue est présente et correspond au format imposé.

Si des erreurs sont détectées, on remplit un message d'erreur que l'on renvoie précédé du code d'erreur et du séparateur.

Si aucune erreur n'est trouvée, on renvoie le code de réussite, le séparateur, puis on construit les deux blocs à afficher avec les valeurs reçues, en insérant le séparateur entre les deux blocs.

9-5. Traitement de la réponse

Il ne reste plus qu'à traiter la réponse de la requête AJAX :

 
Sélectionnez
// Envoi des données au serveur et traitement de la réponse
function sendAjax(url, params, divErreur){
  $.post(
    url,
    params,
    function(data){
      var donnees = data.split('<>');
      if(donnees[0] == '0'){
        $(divErreur).html(donnees[1]);
        $(divErreur).find('ul').delay(5000).fadeOut('slow',  
function(){
          $(this).remove();
        });
      }
      else{
        if(!$('#contenu').data('item1')){
          $('#contenu').data('item1', $('#item1').html());
        }
        $('#item1').html(donnees[1]);
        if(!$('#contenu').data('item2')){
          $('#contenu').data('item2', $('#item2').html());
        }
        $('#item2').html(donnees[2]);
      }
    },
    'text'
  );
}
$('#contenu').delegate('#deconnect', 'click', function(){
  $('#item1').html($('#contenu').data('item1'));
  $('#item2').html($('#contenu').data('item2'));
});

La réponse est constituée de plusieurs éléments séparés par les caractères « <> ».

On commence donc par couper la réponse selon ce séparateur à l'aide de la méthode JavaScript split(). Cette méthode retourne un tableau.

On commence par tester le premier élément de ce tableau, qui correspond au code d'erreur ou de réussite.

S'il s'agit d'une erreur, on affiche celle-ci (second élément du tableau) à l'emplacement prévu.

Si la requête renvoie un code de réussite, alors on commence par stocker, si cela n'a pas déjà été fait, dans l'objet jQuery data de la balise #contenu le code des deux formulaires. Puis, on remplace ceux-ci par le code HTML reçu dans la réponse du serveur.

Dans ce code, un lien de déconnexion est présent, il ne reste donc plus, au clic de celui-ci, qu'à récupérer le contenu de l'objet data pour remplacer les informations affichées par les formulaires initiaux.

9-6. En conclusion

Cet exemple termine notre survol de jQuery. Tout comme dans ce récapitulatif, nous avons essayé non pas d'être exhaustif, mais surtout de faire comprendre comment peut fonctionner jQuery ou une page l'utilisant.

Si vous comprenez les principes de fonctionnement de jQuery, vous n'aurez pas de mal à parcourir la documentation officielle pour en découvrir tous les recoins et les secrets cachés. Surtout, vous serez en mesure de vous adapter aux évolutions futures de la bibliothèque au fil de ses mises à jour.

Bien entendu, l'étude de jQuery ne s'arrête pas là, ce n'est même potentiellement qu'un début car jQuery, grâce à sa communauté particulièrement active, offre d'autres aspects, plus concrets, que ce soit au niveau de l'extension de la bibliothèque axée sur l'Interface utilisateur ( jQuery UI), que ce soit celle spécifique pour appareils portables ( jQuery Mobile) ou encore au travers de la découverte des plugins, que ce soit dans leur utilisation, puis, pourquoi pas, dans la création des vôtres, que vous pourrez alors proposer au public.

Toujours est-il que, quelle que soit votre utilisation de jQuery, vous vous rendrez rapidement compte que son slogan n'est pas usurpé : « faites plus en codant moins ! ».


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Licence Creative Commons
Le contenu de cet article est rédigé par Didier Mouronval et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 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.