Vous êtes sur la page 1sur 428

Rfrence

jQuery
Simpliez et enrichissez vos dveloppements JavaScript
J. Chaffer et K. Swedberg Prfac par John Resing, crateur de jQuery

Rseaux et tlcom

Programmation Dveloppement web Scurit Systme dexploitation

jQuery
Simplifiez et enrichissez vos dveloppements JavaScript
Jonathan Chaffer Karl Swedberg

Traduit par Herv Soulard avec la contribution technique de Didier Mouronval

Pearson Education France a apport le plus grand soin la ralisation de ce livre afin de vous fournir une information complte et fiable. Cependant, Pearson Education France nassume de responsabilits, ni pour son utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui pourraient rsulter de cette utilisation. Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou professionnelle. Pearson Education France ne pourra en aucun cas tre tenu pour responsable des prjudices ou dommages de quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes. Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs propritaires respectifs.

Publi par Pearson Education France 47 bis, rue des Vinaigriers 75010 PARIS Tl. : 01 72 74 90 00 www.pearson.fr

Titre original : Learning jQuery 1.3 Traduit de lamricain par Herv Soulard Contribution technique : Didier Mouronval

ISBN original : 978-1-847196-70-5 ISBN : 978-2-7440-4142-6 Copyright 2009 Packt Publishing Copyright 2009 Pearson Education France All rights reserved Tous droits rservs dition originale publie par Packt www.packtpub.com
Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues larticle L. 122-5 2 et 3 a) du Code de la proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le respect des modalits prvues larticle L. 122-10 dudit code. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.

Table des matires


Avant-propos ..................................................................................................................... Prface ldition franaise ............................................................................................ propos des auteurs ........................................................................................................ VII IX XI

Prface ............................................................................................................................... XIII Organisation du livre ..................................................................................................... XIII Prrequis ........................................................................................................................ XV Public du livre................................................................................................................ XVI Conventions typographiques ......................................................................................... XVI Votre avis ....................................................................................................................... XVII Tlcharger le code........................................................................................................ XVII 1 Premiers pas ................................................................................................................ 1.1 Intrt de jQuery................................................................................................. 1.2 Efficacit de jQuery............................................................................................ 1.3 Historique du projet jQuery................................................................................ 1.4 Premire page web avec jQuery......................................................................... 1.5 En rsum ........................................................................................................... 2 Slecteurs ..................................................................................................................... 2.1 Le DOM ............................................................................................................. 2.2 La fonction $().................................................................................................... 2.3 Slecteurs CSS ................................................................................................... 2.4 Slecteurs dattribut............................................................................................ 2.5 Slecteurs personnaliss ..................................................................................... 2.6 Mthodes de parcours du DOM ......................................................................... 2.7 Accder aux lments du DOM ......................................................................... 2.8 En rsum ........................................................................................................... 3 vnements .................................................................................................................. 3.1 Effectuer des tches au chargement de la page .................................................. 3.2 vnements simples ........................................................................................... 3.3 vnements composs........................................................................................ 3.4 Le priple dun vnement ................................................................................. 3.5 Modifier le priple : lobjet event....................................................................... 1 2 3 4 5 12 13 13 14 15 18 20 24 27 27 29 29 33 42 45 47

IV

Table des matires

3.6 3.7 3.8 4 Effets 4.1 4.2 4.3 4.4 4.5 4.6 4.7

Retirer un gestionnaire dvnements ................................................................ Simuler une action de lutilisateur...................................................................... En rsum ........................................................................................................... ............................................................................................................................ Modifier les styles CSS ...................................................................................... Bases du masquage/affichage............................................................................. Effets et vitesse................................................................................................... Effets composs.................................................................................................. Crer des animations personnalises.................................................................. Effets simultans ou squentiels......................................................................... En rsum ...........................................................................................................

52 55 59 61 61 66 68 69 71 75 82 83 83 87 89 97 98 105 107 109 110 125 126 132 136 137 139 144 145 146 173 197 199 199 222 236 257

5 Manipulation du DOM ............................................................................................... 5.1 Manipuler des attributs....................................................................................... 5.2 Insrer de nouveaux lments ............................................................................ 5.3 Dplacer des lments ........................................................................................ 5.4 Envelopper des lments .................................................................................... 5.5 Copier des lments............................................................................................ 5.6 Les mthodes du DOM en bref .......................................................................... 5.7 En rsum ........................................................................................................... 6 AJAX ............................................................................................................................ 6.1 Charger des donnes la demande..................................................................... 6.2 Choisir le format des donnes ............................................................................ 6.3 Passer des donnes au serveur............................................................................ 6.4 Surveiller la requte............................................................................................ 6.5 AJAX et les vnements..................................................................................... 6.6 Questions de scurit.......................................................................................... 6.7 Autres solutions.................................................................................................. 6.8 En rsum ........................................................................................................... 7 Manipulation des tables ............................................................................................. 7.1 Tri et pagination ................................................................................................. 7.2 Modifier laspect de la table............................................................................... 7.3 En rsum ........................................................................................................... 8 Manipulation des formulaires .................................................................................... 8.1 Amliorer un formulaire de base........................................................................ 8.2 Formulaires compacts......................................................................................... 8.3 Manipuler des donnes numriques ................................................................... 8.4 En rsum ...........................................................................................................

Table des matires

9 Carrousels et prompteurs .......................................................................................... 9.1 Prompteur de nouvelles ...................................................................................... 9.2 Carrousel dimages............................................................................................. 9.3 En rsum ........................................................................................................... 10 Utilisation des plugins ................................................................................................. 10.1 Rechercher des plugins et de laide.................................................................... 10.2 Utiliser un plugin................................................................................................ 10.3 Le plugin Form................................................................................................... 10.4 La bibliothque jQuery UI ................................................................................. 10.5 Autres plugins recommands ............................................................................. 10.6 En rsum ........................................................................................................... 11 Dveloppement de plugins ......................................................................................... 11.1 Ajouter de nouvelles fonctions globales ............................................................ 11.2 Ajouter des mthodes lobjet jQuery............................................................... 11.3 Mthodes de parcours du DOM ......................................................................... 11.4 Ajouter des mthodes abrges .......................................................................... 11.5 Paramtres dune mthode ................................................................................. 11.6 Ajouter une expression de slection................................................................... 11.7 Distribuer un plugin............................................................................................ 11.8 En rsum ........................................................................................................... Annexe A Ressources en ligne ....................................................................................... A.1 Documentation sur jQuery ................................................................................. A.2 Rfrences JavaScript......................................................................................... A.3 Compacteurs de code JavaScript........................................................................ A.4 Rfrence (X)HTML .......................................................................................... A.5 Rfrences CSS .................................................................................................. A.6 Blogs................................................................................................................... A.7 Frameworks de dveloppement web utilisant jQuery ........................................ Annexe B Outils de dveloppement .............................................................................. B.1 Outils pour Firefox ............................................................................................. B.2 Outils pour Internet Explorer ............................................................................. B.3 Outils pour Safari ............................................................................................... B.4 Outils pour Opera ............................................................................................... B.5 Autres outils ....................................................................................................... Annexe C Fermetures en JavaScript ............................................................................ C.1 Fonctions internes............................................................................................... C.2 Interactions entre fermetures ..............................................................................

259 260 276 301 303 303 304 305 307 317 328 329 329 333 337 340 344 353 355 357 359 359 360 361 361 362 362 365 367 367 368 369 370 370 373 373 377

VI

Table des matires

C.3 C.4 C.5

Fermetures dans jQuery...................................................................................... Dangers lis aux fuites de mmoire ................................................................... En rsum ...........................................................................................................

378 381 384 385 385 387 389 391 392 395 396 399

Annexe D Rfrence rapide ........................................................................................... D.1 Expressions de slection..................................................................................... D.2 Mthodes de parcours du DOM ......................................................................... D.3 Mthodes dvnement....................................................................................... D.4 Mthodes deffet................................................................................................. D.5 Mthodes de manipulation du DOM .................................................................. D.6 Mthodes AJAX ................................................................................................. D.7 Autres mthodes ................................................................................................. Index ..................................................................................................................................

Avant-propos
Jai t trs heureux dapprendre que Karl Swedberg et Jonathan Chaffer entreprenaient lcriture de jQuery. En tant que premier ouvrage traitant de jQuery, ce livre a fix un niveau de qualit que les autres sur le mme thme, ou sur JavaScript en gnral, se sont efforcs datteindre. Depuis sa sortie, il a toujours fait partie des titres JavaScript en tte des ventes, principalement en raison de sa qualit et de sa prise en compte des dtails. Jtais particulirement content que Karl et Jonathan sattellent cette tche car je les connais bien et je savais quils seraient dexcellents auteurs. Travaillant au cur de lquipe jQuery, jai eu loccasion de connatre Karl au cours des deux dernires annes, notamment pour la rdaction de cet ouvrage. Lorsque je regarde le rsultat, ses comptences de dveloppeur et denseignant de la langue anglaise taient manifestement bien adaptes ce projet. Jai galement eu lopportunit de rencontrer les auteurs en personne, un fait plutt rare dans le monde des projets open-source distribus. Ils sont toujours des membres fidles de la communaut jQuery. La bibliothque jQuery est utilise par des personnes dhorizons diffrents, notamment des concepteurs, des dveloppeurs et des programmeurs plus ou moins expriments. Il en est de mme au sein de lquipe jQuery. Ses membres aux connaissances varies apportent leurs avis sur la direction donner au projet. Il existe cependant un point commun entre tous les utilisateurs de jQuery : ils constituent une communaut de dveloppeurs et de concepteurs dont lobjectif est de simplifier le dveloppement en JavaScript. Dire quun projet open-source est de type communautaire ou que son but est daider les nouveaux utilisateurs tient du clich. Toutefois, dans le cas de jQuery, cela na rien dun vu pieux. Au sein de lquipe jQuery, les personnes qui soccupent de la communaut jQuery, qui rdigent la documentation ou qui dveloppent des plugins sont plus nombreuses que celles en charge du code de base. Bien que les responsables du dveloppement de la bibliothque fassent preuve dun dynamisme lev, cest la communaut qui sest tablie autour de jQuery qui fait toute la diffrence entre un projet lthargique et un projet qui rpond chaque besoin, voire les anticipe.

VIII

jQuery

Notre faon de mener le projet et votre manire dutiliser le code sont fondamentalement trs diffrentes de celles que lon rencontre dans la plupart des projets open-source ou avec la plupart des bibliothques JavaScript. Les membres du projet et de la communaut jQuery sont de vritables experts. Ils savent pourquoi jQuery est diffrent et font de leur mieux pour transmettre ces connaissances aux utilisateurs. Pour comprendre ce quest la communaut jQuery, la lecture dun article ne vous suffira pas. Vous devez vous y impliquer pour prendre pleinement conscience de ce qui sy passe. Jespre que vous trouverez loccasion de participer. Rejoignez-nous sur les forums, les listes de diffusion et les blogs, et laissez-nous vous guider lors de votre apprentissage de jQuery. La bibliothque jQuery nest pas quun morceau de code. Elle reprsente la somme dexpriences qui ont conduit son existence, avec les nombreux hauts et bas, les difficults de son dveloppement et lexcitation de la voir grandir et russir. Son volution sest faite en phase avec les utilisateurs et les membres de lquipe, avec un souci constant de comprhension et dadaptation. Lorsque jai constat que cet ouvrage considrait jQuery comme un outil unifi, non comme une compilation des expriences quil cache, jtais la fois dcontenanc et enthousiaste. Voir comment les autres apprennent, comprennent et faonnent jQuery, voil ce qui rend ce projet si stimulant. Je ne suis pas le seul entretenir avec jQuery une relation diffrente dune relation normale entre un utilisateur et un outil. Je ne suis pas certain de pouvoir expliquer pourquoi cest ainsi, mais jai pu le constater de nombreuses fois cet instant unique o le visage de lutilisateur sillumine lorsquil ralise quel point jQuery va laider. un moment prcis, le dclic se passe. Lutilisateur de jQuery comprend que loutil dont il se sert est bien plus quun simple outil. Lcriture dapplications web dynamiques prend alors pour lui un tout autre sens. Cest une chose incroyable et rellement ma prfre dans le projet jQuery. Je souhaite que vous ayez lopportunit dprouver galement cette sensation. John Resig Crateur de jQuery

Prface ldition franaise


Lapparition, il y a quelques annes, de ce que lon appelle le "Web 2.0" a suscit un regain dintrt pour le langage JavaScript. Jappelle Web 2.0 la capacit dune page Internet interagir avec son utilisateur, au point parfois de devenir une vritable application. Ainsi, JavaScript, autrefois cantonn des actions minimes et souvent msestim, est petit petit devenu quasiment incontournable dans la conception de sites se voulant modernes. La principale difficult lorsque lon aborde un dveloppement JavaScript est de tenir compte des diffrences dinterprtation et de syntaxe entre les divers navigateurs. Si Internet Explorer est celui qui donne le plus de fil retordre, il ne faut pas oublier que, mme pour les autres, des diffrences existent ! Ces deux facteurs conjugus, la forte demande en JavaScript pour obtenir des sites dynamiques et les diffrentes implmentations de ce langage, font que de nombreuses bibliothques sont apparues ces dernires annes afin de faciliter le travail des dveloppeurs. Le but de ces bibliothques (frameworks en anglais) est double : uniformiser la syntaxe de JavaScript pour la rendre compatible avec tous les navigateurs et ajouter de nouvelles fonctionnalits au langage. Parmi ces bibliothques, jQuery semble aujourdhui la plus populaire et la plus utilise de toutes. Son succs vient probablement de son concept modulaire. En plus des intentions gnrales cites prcdemment, jQuery a t pens selon trois ides majeures :
n n n

faciliter la slection densembles dlments d'un document HTML ; offrir une capacit de chanage des instructions sur les collections rcupres ; permettre aux dveloppeurs de crer eux-mmes et facilement des extensions personnalises.

Cest larticulation de ces trois principes, au-del de la simple syntaxe, qui vous est prsente dans ce livre. Vous apprendrez crer des fonctions concises, simples et rutilisables.

jQuery

Lensemble des explications sappuie sur des exemples prcis et fonctionnels, qui se mettent en place au fur et mesure des notions traites. Vous dcouvrirez ainsi immdiatement comment intgrer concrtement les fonctions abordes, mais aussi comment se construit un code jQuery. Ces exemples ont le mrite de bien insister sur lintrt dutiliser la bibliothque de faon globale. En effet, trop souvent, ce genre de bibliothque nest employ que pour lune de ses fonctionnalits (par exemple les fonctions AJAX), ce qui est une ineptie en termes de conception. Cest comme si vous utilisiez une photo haute dfinition comme vignette dans une page HTML sans loptimiser au pralable. Il est dommage de charger une bibliothque telle que jQuery sans tirer profit de lensemble de ses avantages et de toute sa puissance.

Didier Mouronval Responsable des rubriques JavaScript et Ajax de developpez.com

propos des auteurs


Jonathan Chaffer est le directeur technique de Structure Interactive, une agence web localise Grand Rapids, Michigan. Il y supervise des projets de dveloppement web fonds sur diverses technologies et participe galement aux tches de programmation. Au sein de la communaut open-source, Jonathan participe activement au projet Drupal, qui a adopt jQuery comme framework JavaScript. Il est lauteur du module CCK (Content Construction Kit), trs employ pour la gestion du contenu structur sur les sites Drupal. Il est charg des principales restructurations du systme de menus de Drupal et de la rfrence de lAPI dveloppeur. Jonathan vit Grand Rapids avec sa femme, Jennifer. Il souhaite remercier Jenny pour son enthousiasme et son soutien infatigables, Karl pour la motivation quil lui transmet lorsque lesprit est faible, et la communaut Ars Technica pour son inspiration constante vers lexcellence technique. Karl Swedberg est dveloppeur web chez Fusionary Media Grand Rapids, Michigan. Il passe le plus clair de son temps mettre en uvre des conceptions, en mettant laccent sur du code HTML conforme aux standards, des styles CSS propres et du JavaScript non intrusif. En tant que membre de lquipe du projet jQuery et contributeur actif la liste de discussion jQuery, Karl participe des ateliers et des confrences et propose des formations professionnelles en Europe et en Amrique du Nord. Avant son aventure dans le dveloppement web, Karl a travaill comme rviseur, comme professeur danglais au lyce et comme cafetier. Son intrt pour la technologie sest rvl au dbut des annes 1990 alors quil travaillait chez Microsoft Redmond, Washington, sans faiblir depuis. Karl prfrerait passer du temps avec sa femme, Sara, et ses deux enfants, Benjamin et Lucia. Il souhaite remercier Sara pour son amour loyal et son soutien, et ses deux charmants enfants. Il exprime son plus profond respect envers Jonathan Chaffer pour son expertise en programmation, ainsi que sa gratitude pour avoir voulu crire cet ouvrage avec lui. Il remercie galement John Resig pour avoir cr la meilleure bibliothque JavaScript au monde et avoir runi une communaut extraordinaire autour de ce projet. Ses remerciements vont au personnel de Packt Publishing, aux relecteurs techniques du livre, jQuery Cabal et tous ceux qui ont apport leur aide et leurs ides.

Prface
Lhistoire a commenc en 2005 par un travail passionn de John Resig, un petit gnie du JavaScript qui travaille prsent pour la Mozilla Corporation. Inspir par les pionniers du domaine, tels que Dean Edwards et Simon Willison, Resig a runi un ensemble de fonctions pour faciliter la recherche dlments dans une page web et leur attribuer des comportements. Avant quil nannonce publiquement son projet en janvier 2006, il avait ajout la modification du DOM et les animations de base. Il a nomm son projet jQuery afin de souligner le rle central de la recherche (query) des lments dans une page web et leur manipulation par du code JavaScript. Au cours des quelques annes qui ont suivi, le jeu des fonctionnalits de jQuery sest dvelopp, les performances se sont amliores et certains des sites les plus populaires dInternet lont adopt. Bien que Resig reste le dveloppeur en chef du projet, jQuery a pu vritablement spanouir dans le monde open-source, jusqu senorgueillir aujourdhui dune quipe de dveloppeurs JavaScript de premier plan et dune communaut dynamique de milliers de dveloppeurs. Grce la bibliothque JavaScript jQuery, vous pouvez amliorer vos sites web, quel que soit votre niveau dexprience. En un seul fichier de taille rduite, elle offre de nombreuses fonctionnalits, une syntaxe facile apprendre et une compatibilit robuste entre les navigateurs. Par ailleurs, les centaines de plugins dvelopps pour tendre les fonctionnalits de jQuery en font un outil pratiquement indispensable lors de nimporte quel dveloppement de scripts ct client. jQuery est une introduction en douceur aux concepts de jQuery. Grce cet ouvrage, vous pourrez ajouter des interactions et des animations vos pages, mme si vos prcdentes tentatives en JavaScript ont pu vous laisser perplexe. Il va vous aider franchir les obstacles dresss par AJAX, les vnements, les effets et les fonctionnalits avances du langage JavaScript. Il contient galement un court guide de rfrence de la bibliothque jQuery, vers lequel vous pourrez vous tourner en cas de besoin.

Organisation du livre
Au Chapitre 1, Premiers pas, vous ferez vos premires armes avec jQuery. Ce chapitre commence par une description de cette bibliothque JavaScript et de ce quelle peut vous apporter. Il explique comment obtenir et configurer jQuery, puis passe lcriture dun premier script.

XIV

jQuery

Le Chapitre 2, Slecteurs, dtaille lutilisation des expressions de slection de jQuery, ainsi que les mthodes de parcours du DOM afin de rechercher des lments dans la page, o quils se trouvent. Vous utiliserez jQuery pour appliquer des styles divers lments de la page, une opration quil est parfois impossible de raliser avec du CSS pur. Au Chapitre 3, vnements, vous utiliserez le mcanisme de gestion des vnements de jQuery pour dclencher des comportements suite des vnements gnrs par le navigateur. Vous verrez comment jQuery facilite linsertion non intrusive dvnements, avant mme que le chargement de la page ne soit termin. Ce chapitre abordera galement des sujets plus avancs, comme la propagation des vnements, la dlgation et les espaces de noms. Le Chapitre 4, Effets, prsente des techniques danimation avec jQuery. Il explique comment masquer, afficher et dplacer des lments de la page en employant des effets la fois utiles et agrables lil. Le Chapitre 5, Manipulation du DOM, montre comment modifier une page la demande. Il vous apprendra intervenir sur la structure dun document HTML, ainsi que sur son contenu. Au Chapitre 6, AJAX, vous dcouvrirez les nombreuses mthodes proposes par jQuery pour simplifier laccs aux fonctionnalits ct serveur, sans recourir aux actualisations pnibles de la page. Dans les trois chapitres suivants (7, 8 et 9), vous travaillerez sur plusieurs exemples rels, en runissant tout ce que vous avez appris aux chapitres prcdents pour crer des solutions jQuery robustes des problmes courants. Au Chapitre 7, Manipulation des tables, vous trierez, filtrerez et mettrez en style des informations de manire obtenir une mise en forme agrable et fonctionnelle des donnes. Le Chapitre 8, Manipulation des formulaires, vous permettra de matriser les dtails de la validation ct client, vous y concevrez une prsentation de formulaire adaptative et mettrez en uvre des fonctionnalits interactives ct client, telles que lauto-compltion dans les champs du formulaire. Le Chapitre 9, Carrousels et prompteurs, montre comment amliorer la prsentation et lintrt des lments dune page en les affichant sous forme de groupes plus faciles grer. Vous ferez en sorte que les informations apparaissent et disparaissent, que ce soit automatiquement ou sous le contrle de lutilisateur. Dans les deux chapitres suivants (10 et 11), vous dpasserez les mthodes standard de jQuery pour explorer les extensions tierces de la bibliothque et verrez diffrentes manires de ltendre vous-mme.

Prface

XV

Le Chapitre 10, Utilisation des plugins, sintresse au plugin Form et aux plugins officiels pour linterface utilisateur runis sous le nom jQuery UI. Vous y apprendrez galement rechercher dautres plugins jQuery populaires et connatre leurs fonctions. Au Chapitre 11, Dveloppement de plugins, vous verrez comment tirer profit des possibilits extraordinaires dextension de jQuery pour dvelopper vos propres plugins. Vous crerez vos propres fonctions utilitaires, ajouterez des mthodes lobjet jQuery, crirez des expressions de slection personnalises, etc. LAnnexe A, Ressources en ligne, recommande plusieurs sites web qui proposent des informations sur diffrents sujets en rapport avec jQuery, JavaScript et le dveloppement web en gnral. lAnnexe B, Outils de dveloppement, vous dcouvrirez quelques programmes et utilitaires tiers pour ldition et le dbogage du code jQuery depuis votre environnement de dveloppement personnel. LAnnexe C, Fermetures en JavaScript, prsente tout ce que vous devez savoir sur le concept de fermetures ce quelles sont et comment les utiliser pour votre propre bnfice. LAnnexe D, Rfrence rapide, donne une vue densemble de la bibliothque jQuery, y compris chacune de ses mthodes et ses expressions de slection. Sa prsentation facile parcourir vous sera prcieuse en ces moments o vous savez ce que vous voulez faire mais sans tre certain du nom de la mthode ou du slecteur dlments.

Prrequis
Pour crire et excuter le code illustr dans cet ouvrage, vous avez besoin des lments suivants :
n n

un diteur de texte ; un navigateur web moderne, comme Chrome de Google, Firefox de Mozilla, Safari dApple ou Internet Explorer de Microsoft ; la bibliothque jQuery, en version 1.3.2 ou ultrieure, tlchargeable depuis le site http://jquery.com/.

Par ailleurs, pour mettre en uvre les exemples AJAX du Chapitre 6, vous aurez besoin dun serveur compatible PHP.

XVI

jQuery

Public du livre
Ce livre est destin aux concepteurs web qui souhaitent ajouter des lments interactifs leurs crations et aux dveloppeurs qui veulent donner leurs applications web la meilleure interface utilisateur. Des connaissances minimales en programmation JavaScript sont indispensables ; vous devez tre laise avec la syntaxe de ce langage. Vous devez possder les bases du balisage HTML et des styles CSS. Aucune connaissance de jQuery nest requise, pas plus quune exprience avec dautres bibliothques JavaScript.

Conventions typographiques
Cet ouvrage emploie plusieurs styles pour le texte afin de mettre en exergue les diffrentes informations. Voici quelques exemples de ces styles, avec leur signification. Dans le texte, le code est prsent de la manire suivante : "Nous pouvons inclure dautres contextes laide de la directive include." Lorsquun lment de code correspond une valeur indique par lutilisateur, il est crit en italique de la manire suivante : .find(slecteur). Voici un exemple de bloc de code :
<html> <head> <title>Le titre</title> </head> <body> <div> <p>Un paragraphe.</p> <p>Un autre paragraphe.</p> <p>Encore un autre paragraphe.</p> </div> </body> </html>

Lorsque nous souhaitons attirer votre attention sur une partie dun bloc de code, les lignes ou les lments correspondants sont indiqus en gras :
$(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); $('a[href$=.pdf]').addClass('pdflink'); $('a[href^=http][href*=henry]') .addClass('henrylink'); });

Les entres et les sorties de la ligne de commande se prsentent de la manire suivante :


fonctExt(): Fonction externe Fonction interne

Prface

XVII

Les termes nouveaux et les mots importants sont crits dans une police italique. Les mots affichs lcran, par exemple dans les menus ou les botes de dialogue, apparaissent dans le texte sous la forme suivante : "Notez licne PDF droite du lien HAMLET, licne denveloppe ct du lien EMAIL, ainsi que le fond blanc et la bordure noire autour du lien HENRY V."
ATTENTION
Les avertissements signalent des actions viter.

ASTUCE
Ces encadrs prsentent des astuces.

INFO
Ces notes proposent des informations supplmentaires sur le sujet en cours.

Votre avis
Lavis de nos lecteurs tant toujours le bienvenu, nhsitez pas nous faire part de vos commentaires. Pour cela, rendez-vous sur la page ddie cet ouvrage sur le site web Pearson (http://www.pearson.fr) et cliquez sur le lien RAGIR. Si vous souhaitez proposer la publication dun titre dont vous avez besoin ou si vous souhaitez devenir auteur, ouvrez la page CONTACTS sur le site web Pearson, remplissez le formulaire prsent et envoyez-le au service EDITORIAL. Malgr tout le soin apport la rdaction du contenu de cet ouvrage, des erreurs sont toujours possibles. Si vous en rencontrez, que ce soit dans le texte ou dans le code, merci de nous les indiquer en allant sur la page CONTACTS du site web Pearson et en envoyant le formulaire rempli au service GNRAL.

Tlcharger le code
Les fichiers des exemples de code sont disponibles depuis le site web Pearson (http:// www.pearson.fr), en suivant le lien CODES SOURCES sur la page ddie ce livre. Ils contiennent les instructions permettant de les employer.

1
Premiers pas
Au sommaire de ce chapitre
U U U U U

Intrt de jQuery Efficacit de jQuery Historique du projet jQuery Premire page web avec jQuery En rsum

Le World Wide Web est aujourdhui un environnement dynamique et ses utilisateurs ont des exigences leves quant laspect et aux fonctions des sites. Pour construire des sites interactifs intressants, les dveloppeurs se tournent vers des bibliothques JavaScript, comme jQuery, qui leur permettent dautomatiser les tches courantes et de simplifier les plus complexes. La popularit de jQuery vient de sa capacit simplifier un grand nombre de tches. Les fonctionnalits de jQuery tant nombreuses, il peut sembler difficile de savoir par o commencer. Toutefois, sa conception fait preuve de cohrence et de symtrie. La plupart de ses concepts sont emprunts la structure de HTML et de CSS (Cascading Style Sheets). Les concepteurs ayant peu dexprience en programmation peuvent ainsi dmarrer rapidement, car la plupart des dveloppeurs web matrisent mieux ces deux technologies que JavaScript. Dans ce premier chapitre, nous allons crire un programme jQuery oprationnel constitu uniquement de trois lignes de code. Les programmeurs expriments bnficieront galement de cette cohrence conceptuelle, comme nous le verrons plus loin dans les chapitres traitant de sujets plus avancs. Voyons prsent ce que jQuery peut apporter.

jQuery

1.1

Intrt de jQuery

La bibliothque jQuery fournit une couche dabstraction gnrique pour les scripts web classiques. Elle est donc utile pour la plupart des dveloppements de scripts. En raison de sa nature extensible, il est impossible dtudier toutes ses utilisations dans un mme ouvrage, dautant que le dveloppement de plugins proposant de nouvelles possibilits est permanent. Toutefois, les fonctionnalits standard permettent de rpondre aux besoins suivants :
n

Accder aux lments dun document. Sans laide dune bibliothque JavaScript, il faut crire de nombreuses lignes de code pour parcourir larborescence du DOM (Document Object Model) et localiser des parties spcifiques de la structure dun document HTML. jQuery fournit un mcanisme de slection robuste et efficace qui permet de retrouver nimporte quels lments dun document afin de les examiner ou de les manipuler. Modifier laspect dune page web. CSS propose une solution puissante pour modifier le rendu des documents, mais elle montre ses limites lorsque les navigateurs web ne respectent pas les mmes standards. Avec jQuery, les dveloppeurs peuvent contourner ce problme en se fondant sur une prise en charge identique des standards quels que soient les navigateurs. Par ailleurs, la bibliothque permet de modifier les classes ou les proprits de style appliques une partie du document, mme aprs que la page a t affiche. Altrer le contenu dun document. jQuery ne se borne pas des changements cosmtiques mais permet de modifier le contenu du document. Du texte peut tre chang, des images peuvent tre insres ou interverties, des listes tre rordonnes, lintgralit de la structure du contenu HTML peut tre revue et tendue. Toutes ces possibilits sont permises par une API (Application Programming Interface) simple demploi. Rpondre aux actions de lutilisateur. Mme les comportements les plus labors et les plus puissants ne sont daucune utilit si leur excution nest pas contrle. La bibliothque jQuery propose une solution lgante pour intercepter une grande varit dvnements, comme un clic sur un lien, sans avoir mlanger des gestionnaires dvnements au code HTML. De plus, cette API de gestion des vnements permet de passer outre les incohrences des navigateurs, qui constituent une vritable nuisance pour les dveloppeurs web. Animer les modifications dun document. Pour la bonne mise en uvre des comportements interactifs, le concepteur doit galement fournir un retour visuel lutilisateur. La bibliothque jQuery apporte son aide en proposant des animations, comme les effets de fondu et de volet, ainsi quune bote outils pour en construire de nouvelles.

Chapitre 1

Premiers pas

Rcuprer des informations partir dun serveur sans actualiser la page. Ce type de code, connu sous le nom AJAX (Asynchronous JavaScript And XML), aide les dveloppeurs web crer un site ractif proposant des fonctionnalits riches. La bibliothque jQuery permet de retirer de ce code les complexits propres aux navigateurs et de laisser les dveloppeurs se concentrer sur laspect serveur. Simplifier les tches JavaScript courantes. Outre les fonctionnalits de jQuery orientes document, la bibliothque apporte des amliorations aux constructions JavaScript de base, comme les itrations et la manipulation des tableaux.

1.2

Efficacit de jQuery

Avec lintrt rcent port au HTML dynamique, les frameworks JavaScript ont prolifr. Certains sont spcialiss et se focalisent sur une ou deux des tches prcdentes. Dautres tentent de runir au sein dun mme paquetage tous les comportements et toutes les animations imaginables. Pour offrir les diverses fonctionnalits dcrites prcdemment, tout en restant compact, jQuery emploie plusieurs stratgies :
n

Exploiter CSS. En fondant le mcanisme de localisation des lments de la page sur les slecteurs CSS, jQuery hrite dune mthode concise mais lisible pour exprimer une structure de document. La bibliothque devient un point dentre pour les concepteurs qui souhaitent ajouter des comportements la page, car la connaissance de la syntaxe de CSS est un prrequis au dveloppement web professionnel. Accepter les extensions. Pour viter les dangers de la prolifration des fonctionnalits, jQuery relgue la prise en charge des cas dutilisation spciaux aux plugins. La procdure de cration de nouveaux plugins tant simple et parfaitement documente, elle a encourag le dveloppement dune grande diversit de modules cratifs et utiles. Par ailleurs, les fonctionnalits standard de jQuery sont elles-mmes ralises par des plugins et peuvent tre retires pour obtenir une bibliothque plus compacte. Masquer les excentricits du navigateur. Malheureusement, le dveloppement web est confront au non-respect des standards par les diffrents navigateurs. Une part importante dune application web peut tre ddie aux diffrentes mises en uvre des fonctionnalits pour chaque plateforme. Si le monde des navigateurs en constante volution rend impossible la cration dune base de code parfaitement indpendante de la plateforme pour les fonctions labores, jQuery ajoute une couche dabstraction qui normalise les tches courantes, permettant ainsi de simplifier le code et den rduire la taille. Manipuler des ensembles. Lorsque nous demandons jQuery de "trouver tous les lments de classe collapsible et de les masquer", il nest pas ncessaire de parcourir chaque lment retourn. En effet, les mthodes comme .hide() sont

jQuery

conues pour travailler automatiquement sur des ensembles dobjets la place dobjets individuels. Grce cette technique, nomme itration implicite, les constructions de type boucle sont gnralement inutiles et le code devient plus court.
n

Autoriser plusieurs actions sur une ligne. Pour viter un abus des variables temporaires ou des rptitions coteuses, jQuery emploie avec la plupart de ses mthodes un motif de programmation appel chanage. Autrement dit, le rsultat de la plupart des oprations sur un objet est lobjet lui-mme, auquel la prochaine action peut tre applique.

Grce toutes ces stratgies, le paquetage jQuery a pu rester compact moins de 20 ko aprs compression , tout en proposant des techniques qui permettent au code qui utilise la bibliothque de rester lui aussi compact. Llgance de la bibliothque vient en partie de sa conception et en partie de son processus volutionniste encourag par la communaut dynamique qui sest dveloppe autour du projet. Les utilisateurs de jQuery se rassemblent pour discuter non seulement du dveloppement des plugins, mais galement des amliorations du noyau de la bibliothque. LAnnexe A, Ressources en ligne, dtaille plusieurs ressources communautaires proposes aux dveloppeurs jQuery. En dpit de tout le travail ncessaire la conception dun systme aussi souple et robuste, le produit final est libre dutilisation. Ce projet open-source est la fois sous licence GNU Public License (adapte une utilisation dans dautres projets opensource) et sous licence MIT License (pour faciliter son utilisation dans des logiciels propritaires).

1.3

Historique du projet jQuery

Cet ouvrage sarticule autour de jQuery 1.3.x, cest--dire la version la plus rcente de la bibliothque au moment de lcriture de ces lignes. Lobjectif de la bibliothque apporter une solution simple au problme de recherche des lments dans une page web et de leur manipulation na pas chang au cours de son dveloppement, contrairement certains dtails de sa syntaxe et ses fonctionnalits. Ce court historique du projet dcrit les changements les plus importants entre les versions.
n

Phase de dveloppement public. John Resig a mentionn pour la premire fois une amlioration de la bibliothque "Behaviour" de Prototype en aot 2005. Ce nouveau framework est officiellement sorti le 14 janvier 2006 sous le nom jQuery. jQuery 1.0 (aot 2006). Cette premire version stable de la bibliothque disposait dj dune prise en charge robuste des slecteurs CSS, de la gestion des vnements et des interactions AJAX.

Chapitre 1

Premiers pas

jQuery 1.1 (janvier 2007). Cette version a considrablement assaini lAPI. Les mthodes rarement employes ont t combines afin de rduire le nombre de mthodes apprendre et documenter. jQuery 1.1.3 (juillet 2007). Cette version intermdiaire a intgr de nombreuses amliorations des performances du moteur de slection de jQuery. partir de l, lefficacit de jQuery est devenue suprieure celle des bibliothques JavaScript comparables, comme Prototype, Mootools et Dojo. jQuery 1.2 (septembre 2007). La syntaxe XPath pour la slection des lments a t retire de cette version car elle tait redondante avec celle de CSS. La personnalisation des effets est devenue beaucoup plus souple et le dveloppement des plugins, beaucoup plus simple grce lajout des vnements lis un espace de noms. jQuery UI (septembre 2007). Cette nouvelle suite de plugins a remplac le plugin Interface, certes rput mais vieillissant. Une riche collection de widgets prfabriqus tait incluse, ainsi quun ensemble doutils pour construire des lments sophistiqus comme des interfaces avec glisser-dposer. jQuery 1.2.6 (mai 2008). Les fonctionnalits du plugin Dimensions de Brandon Aaron ont t intgres la bibliothque principale. jQuery 1.3 (janvier 2009). Une refonte majeure du moteur de slection (Sizzle) a permis une grande amlioration des performances de la bibliothque. La dlgation des vnements est officiellement prise en charge.
INFO

Les notes pour les versions antrieures de jQuery peuvent tre consultes sur le site web du projet (http://docs.jquery.com/History_of_jQuery).

1.4

Premire page web avec jQuery

Puisque nous avons vu ltendue des fonctionnalits que propose jQuery, voyons prsent comment mettre en uvre cette bibliothque. Tlcharger jQuery Sur le site web officiel de jQuery (http://jquery.com/), vous trouverez toujours la version la plus rcente de la bibliothque et son actualit. Pour dmarrer, nous avons besoin dune copie de jQuery, tlchargeable directement depuis la page daccueil du site. Plusieurs versions de la bibliothque peuvent tre disponibles tout moment. En tant que dveloppeurs de sites, la version non compresse la plus rcente nous convien-

jQuery

dra parfaitement. Elle pourra tre remplace par une version compresse dans les environnements de production. Aucune installation nest ncessaire. Pour utiliser la bibliothque jQuery, il suffit de la placer sur le site dans un emplacement public. Puisque JavaScript est un langage interprt, aucune phase de compilation ou de construction nest requise. Lorsquune page a besoin de jQuery, il suffit simplement de faire rfrence au fichier correspondant depuis le document HTML. Configurer le document HTML La plupart des exemples dutilisation de jQuery comprennent trois composants : le document HTML lui-mme, les fichiers CSS dfinissant ses styles et les fichiers JavaScript pour sa manipulation. Dans notre premier exemple, nous allons utiliser une page contenant un extrait de livre, avec plusieurs classes appliques diffrents lments.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>De l'autre ct du miroir</title> <link rel="stylesheet" href="alice.css" type="text/css" /> <script src="jquery.js" type="text/javascript"></script> <script src="alice.js" type="text/javascript"></script> </head> <body> <h1>De l'autre ct du miroir</h1> <div class="author">par Lewis Carroll</div> <div class="chapter" id="chapter-1"> <h2 class="chapter-title">1. La maison du miroir</h2> <p>Sur la table, tout prs d'Alice, il y avait un livre. Tout en observant le Roi Blanc (car elle tait encore un peu inquite son sujet, et se tenait prte lui jeter de l'encre la figure au cas o il s'vanouirait de nouveau), elle se mit tourner les pages pour trouver un passage qu'elle pt lire... <span class="spoken">&nbsp;car c'est crit dans une langue que je ne connais pas&nbsp;,</span> se dit-elle.</p> <p>Et voici ce qu'elle avait sous les yeux&nbsp;:</p> <div class="poem"> <h3 class="poem-title">YKCOWREBBAJ</h3> <div class="poem-stanza"> <div>sevot xueutcils sel ; eruehlirg tiat lI'</div> <div>:&nbsp;tneialbirv te edniolla'l rus tneiaryG</div> <div>; sevogorob sel tneialla xuerovilf tuoT</div> <div>.tneialfirnuob sugruof snohcrev seL</div> </div> </div>

Chapitre 1

Premiers pas

<p>Elle se cassa la tte l-dessus pendant un certain temps, puis, brusquement, une ide lumineuse lui vint l'esprit&nbsp;: <span class="spoken">&nbsp;Mais bien sr&nbsp;! c'est un livre du Miroir&nbsp;! Si je le tiens devant un miroir, les mots seront de nouveau comme ils doivent tre.&nbsp;</span></p> <p>Et voici le pome qu'elle lut&nbsp;:</p> <div class="poem"> <h3 class="poem-title">JABBERWOCKY</h3> <div class="poem-stanza"> <div>'Il tait grilheure&nbsp;; les slictueux toves</div> <div>Gyraient sur l'alloinde et vriblaient&nbsp;:</div> <div>Tout flivoreux allaient les borogoves&nbsp;;</div> <div>Les verchons fourgus bourniflaient.</div> </div> </div> </div> </body> </html>

INFO
Lorganisation des fichiers sur le serveur na pas rellement dimportance. Les rfrences entre les fichiers doivent simplement tre adaptes lorganisation choisie. Dans la plupart des exemples tudis dans cet ouvrage, nous dsignerons les fichiers par des chemins relatifs (../images/foo.png) la place de chemins absolus (/images/foo.png). Le code pourra ainsi tre excut localement sans ncessiter un serveur web.

Immdiatement aprs le prambule HTML classique, la feuille de style est charge. Pour cet exemple, elle est relativement rduite.
body { font: 62.5% Arial, Verdana, sans-serif; } h1 { font-size: 2.5em; margin-bottom: 0; } h2 { font-size: 1.3em; margin-bottom: .5em; } h3 { font-size: 1.1em; margin-bottom: 0; } .poem { margin: 0 2em; } .highlight { font-style: italic; border: 1px solid #888; padding: 0.5em; margin: 0.5em 0; background-color: #ffc; }

jQuery

Aprs la rfrence la feuille de style, les fichiers JavaScript sont inclus. Il est important que la balise <script> pour jQuery soit place avant celle de nos scripts. Dans le cas contraire, la bibliothque ne serait pas disponible au moment o notre code tenterait de lutiliser.
INFO
Dans la suite de cet ouvrage, seules les parties pertinentes des fichiers HTML et CSS seront prsentes. Les fichiers complets sont disponibles en franais sur le site de lditeur (http:// www.pearson.fr) et en anglais sur le site web ddi ce livre (http://book.learningjquery.com).

La page obtenue est illustre la Figure 1.1.


Figure 1.1
La page avant application dun style avec jQuery.

Nous allons utiliser jQuery pour appliquer un nouveau style au texte du pome.
INFO
Cet exemple a pour objectif dillustrer une utilisation simple de jQuery. Dans la ralit, un tel style pourrait tre appliqu uniquement laide de CSS.

Chapitre 1

Premiers pas

Ajouter jQuery Notre code personnalis doit tre plac dans le second fichier JavaScript, actuellement vide, qui est inclus dans le document HTML en utilisant <script src="alice.js" type="text/javascript"></script>. Pour cet exemple, nous avons seulement besoin de trois lignes de code :
$(document).ready(function() { $('.poem-stanza').addClass('highlight'); });

Rechercher le texte du pome Dans jQuery, la slection dune partie du document constitue lopration fondamentale. Pour cela, nous utilisons la construction $(). De manire gnrale, une chane de caractres contenant une expression de slection CSS est passe en paramtre. Dans notre exemple, nous souhaitons rechercher toutes les parties du document auxquelles la classe poem-stanza est applique. Par consquent, le slecteur est trs simple. Nous rencontrerons des options plus complexes tout au long de cet ouvrage. Au Chapitre 2, nous dtaillerons les diffrentes manires de localiser les lments dun document. La fonction $() est en ralit une fabrique pour lobjet jQuery, qui constitue la brique de base avec laquelle nous travaillerons partir de maintenant. Lobjet jQuery encapsule des lments du DOM, zro ou plus, et nous permet dinteragir avec eux de diffrentes manires. Dans notre cas, nous souhaitons modifier laspect de ces parties de la page et, pour cela, nous changerons les classes appliques au texte du pome. Injecter la nouvelle classe Comme la plupart des mthodes de jQuery, la mthode .addClass() a un nom parfaitement explicite : elle applique une classe CSS la partie de la page que nous avons slectionne. Elle prend comme seul paramtre le nom de la classe ajouter. Cette mthode ainsi que son complment .removeClass() nous permettront dobserver facilement le fonctionnement de jQuery au cours de notre exploration des diffrentes expressions de slection. Pour le moment, notre exemple ajoute simplement la classe highlight, que la feuille de style dfinit comme du texte en italique avec une bordure. Vous remarquerez quaucune itration nest requise pour ajouter la classe toutes les strophes du pome. Nous lavons indiqu, jQuery utilise une itration implicite dans les mthodes comme .addClass() et un seul appel de fonction suffit pour modifier toutes les parties slectionnes du document. Excuter le code
$() et .addClass() suffisent pour atteindre notre but, cest--dire changer laspect du texte du pome. Cependant, si cette ligne de code est insre en tant que telle dans lentte du document, elle naura aucun effet. Le code JavaScript est gnralement excut

10

jQuery

ds quil est rencontr par le navigateur et, au moment du traitement de len-tte, il nexiste encore aucun contenu HTML auquel appliquer un style. Nous devons retarder lexcution du code jusqu ce que le DOM soit disponible. Pour contrler lexcution dun code JavaScript, le mcanisme classique consiste invoquer ce code depuis des gestionnaires dvnements. Il existe de nombreux gestionnaires pour les vnements dclenchs par lutilisateur, comme les clics avec les boutons de la souris et les appuis sur les touches du clavier. Si nous navions pas jQuery notre disposition, nous devrions employer le gestionnaire onload, qui est invoqu aprs que la page, ainsi que toutes ses images, a t affiche. Pour dclencher lappel notre code suite lvnement onload, nous devons le placer dans une fonction :
function highlightPoemStanzas() { $('.poem-stanza').addClass('highlight'); }

Ensuite, nous associons la fonction lvnement en y faisant rfrence dans la balise HTML <body> :
<body onload="highlightPoemStanzas();">

De cette manire, notre code est excut lorsque le chargement de la page est termin. Cette approche prsente quelques inconvnients. Nous avons altr le contenu HTML luimme pour mettre en place ce comportement. Ce couplage troit entre la structure et la fonction conduit un encombrement du code, avec un risque de rptition des mmes appels de fonctions sur diffrentes pages ou, dans le cas dautres vnements tels que les clics de souris, sur chaque instance dun lment dune page. Lajout de nouveaux comportements impose une intervention en plusieurs endroits, ce qui augmente les sources derreurs et complique le travail parallle des concepteurs et des programmeurs. Pour viter ce problme, jQuery nous permet de planifier linvocation de fonctions aprs que le DOM a t charg, sans attendre laffichage des images, en utilisant la construction $(document).ready(). Avec la fonction dfinie prcdemment, nous pouvons crire la ligne suivante :
$(document).ready(highlightPoemStanzas);

Cette technique ne ncessite aucune modification du code HTML. Le comportement est totalement associ partir du fichier JavaScript. Au Chapitre 3, nous verrons comment rpondre dautres types dvnements, en sparant galement leurs effets de la structure HTML. La solution propose reste toutefois peu conomique, car nous avons d dfinir une fonction, highlightPoemStanzas(), alors quelle est employe immdiatement et une seule fois. Autrement dit, nous avons cr un identifiant dans lespace de noms global des fonctions, sans que cela nous apporte un rel avantage, avec linconvnient davoir nous rappeler de ne pas le rutiliser. Comme dautres langages de programmation,

Chapitre 1

Premiers pas

11

JavaScript propose une rponse cette inefficacit : les fonctions anonymes (parfois appeles fonctions lambda). Grce ces fonctions, nous pouvons crire le code tel quil tait initialement prsent :
$(document).ready(function() { $('.poem-stanza').addClass('highlight'); });

En utilisant le mot cl function sans prciser un nom de fonction, nous dfinissons une fonction exactement l o elle est requise, non avant. Cela permet de rduire le dsordre dans le code et nous conduit trois lignes de JavaScript. Cet idiome est extrmement pratique avec le code jQuery car de nombreuses mthodes prennent en argument une fonction et toutes ces fonctions sont rarement rutilisables. Lorsque cette syntaxe est employe pour dfinir une fonction anonyme dans le corps dune autre fonction, il est possible de crer une fermeture (closure). Il sagit dun concept labor et puissant quil est indispensable de matriser car la dfinition dun grand nombre de fonctions imbriques peut avoir des consquences et des implications inattendues sur lusage de la mmoire. Nous y reviendrons en dtail lAnnexe C. Le produit final Notre code JavaScript tant en place, nous obtenons la page illustre la Figure 1.2.
Figure 1.2
La page aprs application dun style avec jQuery.

12

jQuery

Les strophes du pome sont prsent en italique et places dans des botes, comme prcis par la feuille de style alice.css. En effet, notre code JavaScript a ajout la classe highlight ces lments du document.

1.5

En rsum

prsent, nous comprenons pourquoi un dveloppeur optera pour un framework JavaScript au lieu dcrire tout le code partir de zro, mme pour les tches les plus lmentaires. Nous avons vu quelques cas dans lesquels la bibliothque jQuery excelle et pourquoi son choix simpose. Nous savons galement quelles tches elle permet de simplifier. Dans ce chapitre, nous avons appris disposer de jQuery dans le code JavaScript de la page web, utiliser la fonction $() pour localiser les lments de la page ayant une certaine classe, invoquer .addClass() pour appliquer des styles supplmentaires ces parties de la page, et appeler $(document).ready() pour dclencher lexcution de ce code aprs le chargement de la page. Lexemple choisi a permis dillustrer le fonctionnement de jQuery, mais il nest pas trs utile dans les cas rels. Au chapitre suivant, nous tendrons ce code pour explorer les slections sophistiques de jQuery, en choisissant des utilisations pratiques de cette technique.

2
Slecteurs
Au sommaire de ce chapitre
U U U U U U U U

Le DOM La fonction $() Slecteurs CSS Slecteurs dattribut Slecteurs personnaliss Mthodes de parcours du DOM Accder aux lments du DOM En rsum

La bibliothque jQuery se fonde sur la puissance des slecteurs CSS (Cascading Style Sheets) pour que nous puissions accder rapidement et facilement des lments ou des groupes dlments du DOM. Dans ce chapitre, nous examinerons quelques slecteurs CSS, ainsi que des slecteurs propres jQuery. Nous examinerons galement le parcours du DOM laide des mthodes de jQuery, qui apportent une plus grande souplesse encore.

2.1

Le DOM

Lun des aspects les plus puissants de jQuery rside dans sa capacit slectionner aisment des lments dans le DOM (Document Object Model). Le DOM est une sorte de structure gnalogique arborescente. HTML, comme dautres langages de balisage, utilise ce modle pour dcrire les relations entre les lments dune page. Pour faire rfrence ces relations, nous employons la terminologie associe aux relations familiales : parents, enfants, etc. Un exemple simple permettra de mieux comprendre lapplication de la mtaphore gnalogique un document :

14

jQuery

<html> <head> <title>Le titre</title> </head> <body> <div> <p>Un paragraphe.</p> <p>Un autre paragraphe.</p> <p>Encore un autre paragraphe.</p> </div> </body> </html>

<html> est lanctre de tous les autres lments ; autrement dit, tous les autres lments sont des descendants de <html>. Les lments <head> et <body> sont non seulement des descendants mais galement des enfants de <html>. De mme, en plus dtre lanctre de <head> et de <body>, <html> est galement leur parent. Les lments <p> sont des enfants (et des descendants) de <div>, des descendants de <body> et de <html>, et des frres. Pour de plus amples informations concernant laffichage de la structure arborescente du DOM laide de logiciels tiers, consultez lAnnexe B.

Avant daller plus loin, il est important de noter que le jeu dlments obtenu laide des slecteurs et des mthodes est toujours plac dans un objet jQuery. Les objets jQuery permettent de manipuler trs facilement les lments recherchs dans une page. Nous pouvons aisment lier des vnements ces objets et leur ajouter de jolis effets, tout comme enchaner de multiples modifications ou effets. Toutefois, les objets jQuery diffrent des lments du DOM ou des listes de nuds et, en tant que tels, noffrent pas ncessairement les mmes mthodes et proprits pour certaines tches. la fin de ce chapitre, nous verrons comment accder aux lments du DOM qui sont encapsuls dans un objet jQuery.

2.2

La fonction $()

Quel que soit le type de slecteur que nous voulons employer dans jQuery, linstruction commence toujours avec le symbole du dollar et des parenthses : $(). Tout ce quil est possible dutiliser dans une feuille de style peut galement tre plac entre guillemets et insr entre les parenthses. Ainsi, nous pouvons ensuite appliquer des mthodes jQuery la collection dlments obtenue. Les trois constituants de base des slecteurs sont un nom de balise, un identifiant (ID) et une classe. Ils peuvent tre employs de manire indpendante ou en association avec dautres slecteurs. Le Tableau 2.1 donne un exemple pour chacun de ces trois slecteurs lorsquils sont employs indpendamment.

Chapitre 2

Slecteurs

15

viter un conflit entre jQuery et dautres bibliothques JavaScript


Dans jQuery, le symbole du dollar ($) est un alias pour jQuery. Lorsque plusieurs bibliothques sont utilises dans une mme page, il est possible que des conflits surviennent car la fonction $() est trs souvent dfinie par les bibliothques JavaScript. Pour viter de tels conflits, nous pouvons remplacer chaque occurrence de $ par jQuery dans le code jQuery. Le Chapitre 10 proposera dautres solutions ce problme.

Tableau 2.1 : Utilisation des trois slecteurs de base

Slecteur

CSS

jQuery
$('p') $('#un-id')

Description Slectionne tous les paragraphes du document. Slectionne llment unique du document dont lidentifiant est un-id.

Nom de balise p Identifiant Classe


#un-id .une-classe

$('.une-classe') Slectionne tous les lments du document dont la classe est une-classe.

Nous lavons mentionn au Chapitre 1, lorsque nous associons des mthodes la fonction $(), les lments encapsuls dans lobjet jQuery sont parcourus automatiquement et implicitement. Par consquent, nous pouvons gnralement viter litration explicite, par exemple avec une boucle for, qui est souvent de mise dans les scripts manipulant le DOM. Puisque les bases sont prsent poses, nous sommes prts utiliser les slecteurs de manire plus labore.

2.3

Slecteurs CSS

La bibliothque jQuery prend en charge pratiquement tous les slecteurs dcrits dans les spcifications 1 3 de CSS (voir le site du World Wide Web Consortium ladresse http://www.w3.org/Style/CSS/#specs). Ainsi, tant que JavaScript est activ, les dveloppeurs peuvent enrichir leurs sites web sans se demander si le navigateur de lutilisateur, en particulier Internet Explorer 6, comprendra ou non les slecteurs labors.
INFO
Les dveloppeurs jQuery srieux doivent toujours appliquer les concepts damlioration progressive et de dgradation lgante leur code afin de sassurer quune page sera toujours affiche correctement, si ce nest joliment, que JavaScript soit activ ou non. Nous reviendrons sur ces concepts tout au long de cet ouvrage.

16

jQuery

Pour comprendre le fonctionnement de jQuery avec les slecteurs CSS, nous nous appuierons sur une structure souvent employe par les sites web pour la navigation : la liste imbrique non ordonne.
<ul id="selected-plays" class="clear-after"> <li>Comdies <ul> <li><a href="/commeilvousplaira/">Comme il vous plaira</a></li> <li>Tout est bien qui finit bien</li> <li>Le Songe d'une nuit d't</li> <li>La Nuit des rois</li> </ul> </li> <li>Tragdies <ul> <li><a href="hamlet.pdf">Hamlet</a></li> <li>Macbeth</li> <li>Romo et Juliette</li> </ul> </li> <li>Historiques <ul> <li>Henry IV (<a href="mailto:henryiv@king.co.uk">email</a>) <ul> <li>Partie I</li> <li>Partie II</li> </ul> </li> <li><a href="http://www.shakespeare.co.uk/henryv.htm">Henry V</a></li> <li>Richard II</li> </ul> </li> </ul>

Vous remarquerez que lidentifiant de la premire balise <ul> est selected-plays, mais quaucune classe nest associe aux balises <li>. Lorsque aucun style nest appliqu, la liste est affiche conformment la Figure 2.1.
Figure 2.1
Aspect de la liste sans application dun style.

Chapitre 2

Slecteurs

17

Elle se prsente comme attendu, avec des puces et des lments organiss verticalement et indents en fonction de leur niveau. Appliquer un style aux lments de liste Supposons que nous voulions que les lments de premier niveau, et uniquement ceuxl, soient organiss lhorizontale. Nous pouvons commencer par dfinir une classe horizontal dans la feuille de style :
.horizontal { float: left; list-style: none; margin: 10px; }

La classe horizontal fait en sorte que llment soit flottant gauche de celui qui le suit, lui retire la puce sil sagit dun lment de liste et ajoute une marge de 10 pixels sur ses quatre cts. Au lieu dassocier directement la classe horizontal un lment dans le document HTML, nous lajoutons dynamiquement aux lments de premier niveau de la liste, cest--dire COMDIES, TRAGDIES et HISTORIQUES. Cela nous permet dillustrer lutilisation des slecteurs dans jQuery :
$(document).ready(function() { $('#selected-plays > li').addClass('horizontal'); });

Notre code jQuery dbute par lenveloppe $(document).ready(), qui sexcute ds que le DOM a t charg. La deuxime ligne utilise le combinateur denfant (>) pour ajouter la classe horizontal tous les lments de premier niveau et eux seuls. Le slecteur plac dans la fonction $() signifie "rechercher chaque lment de liste (li) qui est un enfant (>) de llment dont lidentifiant est selected-plays (#selected-plays)". La Figure 2.2 prsente laffichage de la liste aprs que la classe a t applique.

Figure 2.2
Aspect de la liste aprs application dune classe aux lments de premier niveau.

18

jQuery

Pour donner un style tous les autres lments, cest--dire ceux qui ne sont pas de premier niveau, nous avons plusieurs mthodes notre disposition. Puisque nous avons dj appliqu la classe horizontal aux lments de premier niveau, nous pouvons slectionner les lments secondaires en utilisant une pseudo-classe de ngation pour identifier tous les lments de liste qui ne sont pas de la classe horizontal. Cela se passe dans la troisime ligne de code :
$(document).ready(function() { $('#selected-plays > li').addClass('horizontal'); $('#selected-plays li:not(.horizontal)').addClass('sub-level'); });

Cette fois-ci, nous slectionnons chaque lment de liste (li) qui est un descendant de llment dont lidentifiant est selected-plays (#selected-plays) et dont la classe nest pas horizontal (:not(.horizontal)). Lorsque nous ajoutons la classe sub-level ces lments, un arrire-plan gris leur est attribu par la feuille de style. La Figure 2.3 illustre la nouvelle prsentation de la liste imbrique.

Figure 2.3
Aspect de la liste aprs application dune classe aux lments secondaires.

2.4

Slecteurs dattribut

Les slecteurs dattributs de CSS sont particulirement utiles. Ils permettent de dsigner un lment par lune de ses proprits HTML, comme lattribut title dun lien ou lattribut alt dune image. Par exemple, pour slectionner toutes les images qui possdent un attribut alt, nous crivons le code suivant :
$('img[alt]')

INFO
Dans les versions antrieures la 1.2, jQuery utilisait la syntaxe du langage XML Path (XPath) pour ses slecteurs dattribut et proposait plusieurs autres slecteurs XPath. Mme si ces slecteurs ont depuis t retirs de la bibliothque jQuery standard, ils restent disponibles sous forme dun plugin (http://plugins.jquery.com/project/xpath/).

Chapitre 2

Slecteurs

19

Appliquer un style aux liens Les slecteurs dattribut utilisent une syntaxe issue des expressions rgulires pour identifier la valeur au dbut (^) ou la fin ($) dune chane de caractres. Nous pouvons galement nous servir de lastrisque (*) pour indiquer une valeur place nimporte o dans une chane et un point dexclamation (!) pour indiquer linverse dune valeur. Supposons que nous souhaitions appliquer des styles diffrents aux diffrents types de liens. Nous commenons par les dfinir dans la feuille de style :
a { color: #00c; } a.mailto { background: url(images/mail.png) no-repeat right top; padding-right: 18px; } a.pdflink { background: url(images/pdf.png) no-repeat right top; padding-right: 18px; } a.henrylink { background-color: #fff; padding: 2px; border: 1px solid #000; }

Ensuite, nous utilisons jQuery pour ajouter les classes mailto, pdflink et henrylink aux liens appropris. Pour ajouter une classe tous les liens de type courrier lectronique, nous construisons un slecteur qui recherche toutes les ancres (a) dont lattribut href ([href) commence par mailto: (^=mailto:]) :
$(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); });

Pour ajouter une classe tous les liens vers des fichiers PDF, nous utilisons le symbole du dollar la place du symbole de laccent circonflexe. En effet, nous voulons slectionner des liens ayant un attribut href qui se termine par .pdf :
$(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); $('a[href$=.pdf]').addClass('pdflink'); });

Il est galement possible de combiner les slecteurs dattribut. Par exemple, nous pouvons ajouter la classe henrylink tous les liens dont lattribut href commence par http et inclut henry :

20

jQuery

$(document).ready(function() { $('a[href^=mailto:]').addClass('mailto'); $('a[href$=.pdf]').addClass('pdflink'); $('a[href^=http][href*=henry]').addClass('henrylink'); });

La Figure 2.4 prsente la nouvelle version de la liste aprs que ces trois classes ont t appliques aux trois types de liens.

Figure 2.4
Aspect de la liste aprs application des classes aux diffrents types de liens.

Notez licne PDF droite du lien HAMLET, licne denveloppe ct du lien ainsi que le fond blanc et la bordure noire autour du lien HENRY V.

EMAIL,

2.5

Slecteurs personnaliss

la grande diversit de slecteurs CSS, jQuery ajoute ses propres slecteurs personnaliss. Pour la plupart, ils permettent de slectionner certains lments dans un groupe. La syntaxe est identique celle des pseudo-classes CSS, dans laquelle le slecteur commence par des deux-points (:). Par exemple, pour slectionner le deuxime lment parmi lensemble obtenu laide dun slecteur de balises <div> dont la classe est horizontal, nous crivons le code suivant :
$('div.horizontal:eq(1)')

La partie :eq(1) slectionne le deuxime lment de lensemble car les indices des tableaux JavaScript commencent zro. loppos, CSS numrote partir de un. Par consquent, un slecteur CSS comme $('div:nth-child(1)') slectionne tous les lments <div> qui sont le premier enfant de leur parent (toutefois, dans ce cas, il est plus simple dutiliser $('div:first-child')).

Chapitre 2

Slecteurs

21

Appliquer un style en alternance aux lignes dune table Avec :odd et :even, jQuery propose deux slecteurs personnaliss trs intressants. Voyons comment utiliser lun deux pour crer des bandes dans la table suivante :
<table> <tr> <td>Comme il vous plaira</td> <td>Comdie</td> <td></td> </tr> <tr> <td>Tout est bien qui finit bien</td> <td>Comdie</td> <td>1601</td> </tr> <tr> <td>Hamlet</td> <td>Tragdie</td> <td>1604</td> </tr> <tr> <td>Macbeth</td> <td>Tragdie</td> <td>1606</td> </tr> <tr> <td>Romo et Juliette</td> <td>Tragdie</td> <td>1595</td> </tr> <tr> <td>Henry IV, Partie I</td> <td>Historique</td> <td>1596</td> </tr> <tr> <td>Henry V</td> <td>Historique</td> <td>1599</td> </tr> </table>

Nous compltons la feuille de style de manire appliquer un style toutes les lignes de la table et dfinir une classe alt pour les lignes paires :
tr { background-color: #fff; } .alt { background-color: #ccc; }

Notre code jQuery associe la classe alt aux lignes paires de la table (balises <tr>) :

22

jQuery

$(document).ready(function() { $('tr:odd').addClass('alt'); });

Attention, pourquoi utilisons-nous le slecteur :odd (impair) pour cibler des lignes paires ? Comme dans le cas du slecteur :eq(), :odd et :even se fondent sur la numrotation JavaScript, qui commence zro. Par consquent, la premire ligne possde le numro 0 (pair), la deuxime, le numro 1 (impair), etc. La Figure 2.5 montre le rsultat de notre petit bout de code sur la table.
Figure 2.5
Application alterne dun style aux lignes dune table.

Vous constaterez un rsultat sans doute inattendu si la page contient plusieurs tables. Par exemple, puisque la dernire ligne de cette table possde un fond blanc, la premire ligne de la table suivante aura un fond gris. Pour viter ce type de problme, la solution consiste utiliser le slecteur :nth-child(). Il peut prendre en argument un nombre pair ou impair. Attention, cependant, car :nth-child() est le seul slecteur jQuery qui commence un. Pour obtenir le mme effet que prcdemment et pour quil reste cohrent sur les multiples tables dun document, nous modifions le code de la manire suivante :
$(document).ready(function() { $('tr:nth-child(even)').addClass('alt'); });

Supposons que, pour une raison ou pour une autre, nous souhaitions mettre en exergue les cellules de la table qui font rfrence lune des pices HENRY. Pour cela, aprs avoir ajout une classe la feuille de style de manire placer le texte en gras et en italique (.highlight {font-weight:bold; font-style: italics;}), il suffit dajouter notre code jQuery la ligne suivante, fonde sur le slecteur :contains() :
$(document).ready(function() { $('tr:nth-child(even)').addClass('alt'); $('td:contains(Henry)').addClass('highlight'); });

La Figure 2.6 montre notre jolie table bandes, dans laquelle les pices HENRY sont mises en exergue.

Chapitre 2

Slecteurs

23

Figure 2.6
Application dun style certaines cellules de la table.

Il est important de noter que le slecteur :contains() est sensible la casse. Si nous avions utilis $('td:contains(henry)'), cest--dire sans le H majuscule, aucune cellule naurait t slectionne. Bien videmment, il existe des solutions pour crer des tables bandes et mettre du texte en exergue sans passer par jQuery ni utiliser un script ct client. Quoi quil en soit, la combinaison de jQuery et de CSS constitue une bonne solution pour mettre en place des styles de ce type lorsque le contenu est gnr dynamiquement et que nous navons accs ni au contenu HTML ni au code ct serveur. Slecteurs pour formulaires Lorsquon manipule des formulaires, les slecteurs personnaliss de jQuery peuvent simplifier la slection des lments recherchs. Le Tableau 2.2 dcrit quelques-uns de ces slecteurs.
Tableau 2.2 : Slecteurs jQuery pour les formulaires

Slecteur
:text, :checkbox, :radio, :image, :submit, :reset, :password, :file :input :button :enabled :disabled :checked :selected

Correspondance lments de saisie dont lattribut de type est gal au nom du slecteur (sans les deux-points). Par exemple, :text slectionne <input type="text">. lments de type input, textarea, select et button. lments button et input dont lattribut de type est gal button. lments de formulaire activs. lments de formulaire dsactivs. Boutons radio ou cases cocher slectionns. lments option slectionns.

24

jQuery

Comme les autres slecteurs, il est possible de combiner les slecteurs pour formulaires afin dobtenir une meilleure prcision. Par exemple, nous pouvons slectionner tous les boutons radio cochs (non les cases cocher) avec $(':radio:checked') ou slectionner toutes les zones de saisie de mot de passe et les champs de saisie de texte dsactivs avec $(':password, :text:disabled'). Avec les slecteurs personnaliss, nous appliquons les principes de base de CSS pour construire la liste des lments correspondants.

2.6

Mthodes de parcours du DOM

Les slecteurs jQuery tudis jusqu prsent permettent de slectionner des lments dans larborescence du DOM, que ce soit latralement ou vers le bas, et de filtrer les rsultats. Sil sagissait de la seule manire de slectionner les lments, nos possibilits seraient relativement limites (mme si les expressions de slection sont plutt robustes, en particulier par rapport aux solutions classiques daccs au DOM). En de nombreuses occasions, il est ncessaire de slectionner un lment parent ou un lment anctre. Cest ce moment-l que les mthodes jQuery pour le parcours du DOM entrent en scne. Elles permettent de parcourir le DOM dans toutes les directions. Pour certaines mthodes, lexpression de slection scrit de manire quasi identique. Par exemple, la ligne $('tr:odd').addClass('alt'); employe initialement pour ajouter la classe alt peut tre rcrite avec la mthode .filter() :
$('tr').filter(':odd').addClass('alt');

Cependant, en gnral, les deux manires de slectionner les lments se compltent. De plus, la mthode .filter() est particulirement puissante car elle peut prendre une fonction en argument. Cette fonction permet de crer des tests complexes pour le choix des lments qui feront partie du jeu correspondant. Par exemple, supposons que nous voulions ajouter une classe tous les liens externes. jQuery ne propose aucun slecteur pour rpondre ce besoin. Sans une fonction de filtre, nous serions obligs de parcourir explicitement chaque lment et de les tester un par un. En revanche, grce la fonction de filtre, nous pouvons nous appuyer sur litration implicite de jQuery et garder un code concis :
$('a').filter(function() { return this.hostname && this.hostname != location.hostname; }).addClass('external');

La deuxime ligne applique un filtre deux critres sur la collection dlments <a> : 1. Ils doivent possder un attribut href avec un nom de domaine (this.hostname). Ce test permet dexclure les liens mailto et ceux dautres espces. 2. Le nom de domaine cibl par le lien ( nouveau this.hostname) ne doit pas correspondre (!=) au nom de domaine de la page courante (location.hostname).

Chapitre 2

Slecteurs

25

Plus prcisment, la mthode .filter() itre sur le jeu dlments correspondants, en testant la valeur de retour de la fonction applique chacun. Si elle retourne false, llment est retir de la collection obtenue. Si elle retourne true, il est conserv. Revenons prsent notre table bandes pour voir si nous ne pourrions pas exploiter les mthodes de parcours. Appliquer un style certaines cellules Prcdemment, nous avons ajout la classe highlight toutes les cellules qui contiennent le texte Henry. Pour appliquer un style la cellule qui suit chaque cellule contenant ce nom, nous pouvons partir du slecteur crit et lui enchaner la mthode next() :
$(document).ready(function() { $('td:contains(Henry)').next().addClass('highlight'); });

La Figure 2.7 illustre la nouvelle prsentation de la table.


Figure 2.7
Application dun style aux cellules qui suivent certaines autres cellules.

La mthode .next() slectionne uniquement llment frre suivant. Pour mettre en exergue toutes les cellules qui viennent aprs celle qui contient Henry, nous pouvons employer la mthode .nextAll() :
$(document).ready(function() { $('td:contains(Henry)').nextAll().addClass('highlight'); });

INFO
Comme nous pouvions le supposer, les mthodes .next() et .nextAll() ont leurs homologues .prev() et .prevAll(). Par ailleurs, la mthode .siblings() slectionne tous les autres lments au mme niveau du DOM, quils viennent avant ou aprs llment prcdemment slectionn.

26

jQuery

Pour inclure la cellule dorigine (celle qui contient Henry) avec les cellules qui suivent, nous pouvons ajouter la mthode .andSelf() :
$(document).ready(function() { $('td:contains(Henry)').nextAll().andSelf().addClass('highlight'); });

Vous le constatez, il existe diffrentes manires de combiner les slecteurs et les mthodes de parcours pour slectionner la mme collection dlments. Voici par exemple une autre faon de slectionner chaque cellule dune ligne dont au moins lune contient le nom Henry :
$(document).ready(function() { $('td:contains(Henry)').parent().children().addClass('highlight'); });

Dans ce cas, au lieu de parcourir transversalement les lments frres, nous remontons dun niveau dans le DOM avec .parent(), vers llment <tr>, puis nous slectionnons toutes les cellules de la ligne avec .children(). Enchaner des mthodes Les combinaisons de mthodes de parcours que nous venons de prsenter illustrent les possibilits de chanage de jQuery. Avec jQuery, il est possible de slectionner plusieurs jeux dlments et de leur appliquer plusieurs oprations, le tout en une seule ligne de code. Ce chanage permet non seulement de garder un code JavaScript concis, mais galement damliorer les performances dun script lorsque lalternative est de prciser nouveau un slecteur. Il est aussi possible de dcouper une mme ligne de code en plusieurs lignes de manire faciliter la lecture. Par exemple, une succession de mthodes peut scrire sur une seule ligne :
$('td:contains(Henry)').parent().find('td:eq(1)') .addClass('highlight').end().find('td:eq(2)') .addClass('highlight');

ou sur sept lignes :


$('td:contains(Henry)') .parent() .find('td:eq(1)') .addClass('highlight') .end() // .find('td:eq(2)') .addClass('highlight'); // Rechercher chaque cellule qui contient "Henry". // Slectionner son parent. // Rechercher la deuxime cellule descendante. // Ajouter la classe "highlight". Retourner au parent de la cellule qui contient "Henry". // Rechercher la troisime cellule descendante. // Ajouter la classe "highlight".

videmment, le parcours du DOM dans cet exemple est absurde. Nous ne conseillons pas une telle approche, dautant quil existe des mthodes plus simples et plus directes.

Chapitre 2

Slecteurs

27

Le but de cet exemple est simplement de dmontrer lextrme souplesse de lenchanement des mthodes. Nous pourrions le comparer la lecture haute voix dun paragraphe sans reprendre son souffle. Cela permet daller vite, mais les auditeurs risquent davoir du mal comprendre ce qui est lu. En dcoupant linstruction en plusieurs lignes et en ajoutant des commentaires, nous pouvons gagner du temps sur le long terme.

2.7

Accder aux lments du DOM

Les expressions de slection et la plupart des mthodes jQuery retournent un objet jQuery. En gnral, cest ce que nous souhaitons, en raison de litration implicite et des possibilits de chanage que cela permet. Toutefois, il peut arriver que nous ayons besoin daccder directement un lment du DOM dans le code. Par exemple, nous pourrions avoir besoin de fournir le jeu dlments obtenu une autre bibliothque JavaScript. Ou bien nous pourrions avoir besoin daccder au nom de balise dun lment, qui est disponible sous forme de proprit de llment du DOM. Pour ces cas relativement rares, jQuery propose la mthode .get(). Pour accder au premier lment du DOM rfrenc par un objet jQuery, nous utilisons .get(0). Si laccs llment doit se faire dans une boucle, nous pouvons crire .get(indice). Ainsi, pour connatre le nom de balise dun lment dont lidentifiant est mon-element, nous crivons :
var maBalise = $('#mon-element').get(0).tagName;

Pour plus de commodit, jQuery propose un raccourci .get(). Au lieu dcrire la ligne prcdente, nous pouvons utiliser des crochets aprs le slecteur :
var maBalise = $('#mon-element')[0].tagName;

Ne soyez pas surpris par le fait que cette syntaxe ressemble un tableau dlments du DOM. Utiliser les crochets quivaut retirer lenveloppe jQuery pour accder la liste des nuds, tandis quutiliser lindice (dans ce cas 0) quivaut rcuprer llment du DOM.

2.8

En rsum

Grce aux techniques dcrites dans ce chapitre, nous pouvons prsent appliquer un style aux lments de premier niveau et aux lments secondaires dune liste imbrique en utilisant les slecteurs CSS de base, appliquer diffrents styles diffrents types de liens en utilisant des slecteurs dattribut, ajouter des bandes une table en utilisant les slecteurs jQuery personnaliss :odd et :even ou le slecteur CSS labor :nthchild(), et mettre en exergue du texte dans certaines cellules de la table en chanant des mthodes jQuery.

28

jQuery

Jusque-l, nous avons employ lvnement $(document).ready() pour ajouter une classe aux lments obtenus. Au chapitre suivant, nous allons voir comment ajouter une classe en rponse aux vnements gnrs par lutilisateur.

3
vnements
Au sommaire de ce chapitre
U U U U U U U U

Effectuer des tches au chargement de la page vnements simples vnements composs Le priple dun vnement Modifier le priple : lobjet event Retirer un gestionnaire dvnements Simuler une action de lutilisateur En rsum

En JavaScript, il existe plusieurs mthodes pour ragir aux actions de lutilisateur et aux autres vnements. Nous devons les utiliser de manire exploiter, au moment opportun, les techniques jQuery dcrites prcdemment et celles que nous verrons plus loin. Nous obtiendrons ainsi des pages dynamiques et ractives. Sil est parfaitement possible de mettre tout cela en uvre avec du code JavaScript pur, la bibliothque jQuery tend et amliore les mcanismes de gestion des vnements, en leur donnant une syntaxe plus lgante tout en les rendant plus puissants.

3.1

Effectuer des tches au chargement de la page

Nous avons dj vu une solution pour que jQuery ragisse au chargement de la page web. Le gestionnaire dvnement $(document).ready() permet de dclencher lexcution du code dune fonction, mais les possibilits ne sarrtent pas l. Fixer le moment dexcution Au Chapitre 1, nous avons vu que $(document).ready() tait la solution jQuery pour effectuer des tches habituellement dclenches par lvnement onload de JavaScript.

30

jQuery

Cependant, si les rsultats sont quivalents, les actions ne sont pas vraiment excutes au mme moment. Lvnement window.onload est dclench lorsquun document est intgralement charg dans le navigateur, cest--dire lorsque chaque lment de la page est prt tre manipul avec JavaScript. Ainsi, nous navons pas tenir compte de lordre de chargement des lments dans le code. En revanche, un gestionnaire dvnements enregistr avec $(document).ready() est invoqu lorsque le DOM est prt tre utilis. Cela signifie que tous les lments sont accessibles depuis les scripts, mais pas ncessairement que chaque fichier rfrenc ait t tlcharg. Ds que le contenu HTML a t tlcharg et converti en une arborescence DOM, le code peut sexcuter.
INFO
Pour sassurer que les styles ont galement t appliqus la page avant que le code JavaScript ne sexcute, il faut placer les balises <link rel="stylesheet"> avant les balises <script> dans llment <head> du document.

Par exemple, prenons le cas dune page qui affiche une galerie de photos. Elle contient potentiellement de nombreuses images volumineuses, quil est possible de masquer, afficher, dplacer et manipuler avec jQuery. Si nous fondons linterface sur lvnement onload, les utilisateurs devront attendre que toutes les images soient intgralement charges avant de pouvoir accder la page. Par ailleurs, si les comportements personnaliss ne sont pas encore associs aux lments qui possdent des comportements par dfaut, comme les liens, les actions de lutilisateur peuvent provoquer des rsultats inattendus. En revanche, si la configuration se fait partir de $(document).ready(), linterface est disponible beaucoup plus tt, avec les comportements appropris.
INFO
De manire gnrale, il est prfrable dutiliser $(document).ready() la place du gestionnaire onload, mais il ne faut pas oublier que, tous les fichiers ntant pas ncessairement chargs, certains attributs, comme la hauteur et la largeur dune image, peuvent ne pas tre disponibles. Lorsque laccs ces donnes est requis, il faudra mettre en place un gestionnaire onload ou, de prfrence, utiliser jQuery pour dfinir un gestionnaire pour lvnement load ; les deux mcanismes peuvent parfaitement coexister.

Plusieurs scripts sur une page Pour enregistrer des gestionnaires dvnements sans les indiquer directement dans le balisage HTML, le mcanisme JavaScript classique consiste affecter une fonction

Chapitre 3

vnements

31

lattribut correspondant de llment du DOM. Par exemple, supposons que nous ayons dfini la fonction suivante :
function faireAction() { // Effectuer une tche... }

Nous pouvons lintgrer directement au balisage HTML :


<body onload="faireAction();">

ou laffecter lvnement avec du code JavaScript :


window.onload = faireAction;

Ces deux solutions permettent dexcuter la fonction aprs que la page a t charge, la seconde ayant lavantage de sparer clairement le comportement du balisage.
INFO
Lorsquune fonction est affecte en tant que gestionnaire, son nom est utilis sans les parenthses. Si les parenthses sont prsentes, la fonction est invoque immdiatement. Sans les parenthses, le nom identifie simplement la fonction et peut tre utilis pour linvoquer ultrieurement.

Avec une seule fonction, cette approche fonctionne plutt bien. Toutefois, supposons que nous ayons dfini une seconde fonction :
function faireUneAutreAction() { // Effectuer une autre tche... }

Nous pouvons alors tenter dinvoquer cette fonction lors du chargement de la page :
window.onload = faireUneAutreAction;

Malheureusement, cette affectation annule la premire. Lattribut .onload ne pouvant enregistrer quune seule rfrence de fonction la fois, nous ne pouvons pas ajouter un nouveau comportement celui existant. En revanche, le mcanisme $(document).ready() prend parfaitement en charge ce cas. Chaque appel de la mthode ajoute la nouvelle fonction une file de comportements interne. Lorsque la page est charge, toutes les fonctions prsentes dans la file sont excutes dans lordre o elles ont t enregistres.
INFO
Pour tre honnte, jQuery na pas le monopole des solutions ce problme. Nous pouvons crire une fonction JavaScript qui invoque le gestionnaire onload existant puis appelle le gestionnaire indiqu. Cette approche, retenue notamment par la fonction addLoadEvent()

32

jQuery

de Simon Willison, vite les conflits entre les gestionnaires rivaux, comme cest le cas avec $(document).ready(), mais certains des avantages mentionns lui font dfaut. Les mthodes propres aux navigateurs, comme document.addEventListener() et document.attachEvent(), apportent une fonctionnalit semblable, mais jQuery permet darriver au mme rsultat sans que nous ayons tenir compte des incompatibilits entre les navigateurs.

Raccourcis pour la concision du code La construction $(document).ready() invoque la mthode .ready() sur un objet jQuery obtenu partir de llment document du DOM. Cette opration tant trs frquente, la fonction $() en propose un raccourci. En linvoquant sans argument, elle se comporte comme si document tait pass en paramtre. Autrement dit, nous pouvons crire :
$(document).ready(function() { // Notre code... });

de la manire suivante :
$().ready(function() { // Notre code... });

Par ailleurs, la fonction $() accepte une autre fonction en argument, auquel cas jQuery appelle implicitement .ready(). Par consquent, nous pouvons crire :
$(function() { // Notre code... });

Bien que ces autres syntaxes soient plus courtes, nous conseillons dopter pour la version longue car elle montre plus clairement ce que fait le code. Coexistence avec dautres bibliothques Pour simplifier la mise en uvre dune page, il faudra parfois employer plusieurs bibliothques JavaScript. En raison de sa concision et de sa commodit, elles sont nombreuses utiliser lidentifiant $, entranant des conflits de noms. Pour les viter, jQuery fournit la mthode .noConflict(). Elle rend la gestion de lidentifiant $ aux autres bibliothques. Voici comment lutiliser :
<script src="prototype.js" type="text/javascript"></script> <script src="jquery.js" type="text/javascript"></script> <script type="text/javascript"> jQuery.noConflict(); </script> <script src="monscript.js" type="text/javascript"></script>

Chapitre 3

vnements

33

Tout dabord, lautre bibliothque est incluse (Prototype dans cet exemple). Ensuite, jQuery est charge et saccapare lidentifiant $. Puis linvocation de .noConflict() libre $ de manire le redonner la premire bibliothque (Prototype). Dans notre script, nous pouvons utiliser les deux bibliothques, mais linvocation des mthodes jQuery doit se faire avec lidentifiant jQuery la place de $. Pour nous simplifier la vie, la mthode .ready() a un autre tour dans sa manche. La fonction de rappel passe en argument peut prendre un seul paramtre : lobjet jQuery lui-mme. Nous pouvons ainsi le renommer $ sans craindre les conflits :
jQuery(document).ready(function($) { // Dans le code suivant, nous pouvons utiliser $ normalement ! });

Ou, avec la syntaxe abrge dcrite prcdemment :


jQuery(function($) { // Code qui utilise $. });

3.2

vnements simples

Nous venons de voir comment effectuer une tche aprs le chargement de la page, mais il existe bien dautres moments o raliser des oprations. Tout comme nous pouvons intercepter lvnement de chargement dune page avec <body onload=""> ou window. onload, JavaScript permet de ragir aux vnements provenant des utilisateurs, comme les clics de souris (onclick), la modification des champs dun formulaire (onchange) ou le redimensionnement de la taille des fentres (onresize). Lorsquils sont affects directement aux lments dans le DOM, ces gestionnaires prsentent les inconvnients dj mentionns pour onload. jQuery propose une meilleure solution pour grer ces vnements. Un slecteur de style simple Pour servir dillustration certaines techniques de gestion des vnements, nous allons offrir linternaute la possibilit de modifier laspect dune page. Il pourra cliquer sur des boutons permettant de passer en vue normale, dans une vue o le texte est affich dans une colonne troite et dans une vue o la zone de contenu est affiche avec des caractres plus grands. Dans un exemple rel, le dveloppeur web srieux utiliserait ici le principe damlioration progressive. Le slecteur de style serait cach lorsque JavaScript est indisponible ou, mieux encore, utiliserait des liens vers des versions alternatives de la page. Pour les besoins de ce didacticiel, nous supposerons que tous les utilisateurs ont activ JavaScript.

34

jQuery

Voici le balisage HTML du slecteur de style :


<div id="switcher"> <h3>Slecteur de style</h3> <div class="button selected" id="switcher-default"> Par dfaut </div> <div class="button" id="switcher-narrow"> Colonne troite </div> <div class="button" id="switcher-large"> Grande police </div> </div>

La Figure 3.1 prsente la page rsultante, avec le contenu HTML restant et quelques lments de style CSS.

Figure 3.1
La prsentation initiale de page.

Nous allons commencer par rendre oprationnel le bouton GRANDE POLICE. Pour obtenir la nouvelle prsentation de la page, nous dfinissons un nouveau style CSS :

Chapitre 3

vnements

35

body.large .chapter { font-size: 1.5em; }

Lide est ensuite dappliquer la classe large la balise <body> pour que laspect de la page soit modifi conformment la feuille de style. Avec les connaissances acquises au Chapitre 2, nous savons dj comment procder :
$('body').addClass('large');

Toutefois, nous voulons que cela se produise suite au clic sur le bouton GRANDE POLICE, non aprs le chargement de la page. Pour cela, nous allons utiliser la mthode .bind(). Elle permet dindiquer nimporte quel vnement JavaScript et de lui associer un comportement. Dans notre exemple, lvnement se nomme click et le comportement est une fonction constitue de la ligne prcdente :
$(document).ready(function() { $('#switcher-large').bind('click', function() { $('body').addClass('large'); }); });

prsent, si nous cliquons sur le bouton GRANDE POLICE, notre code est excut et le texte saffiche dans une police de caractres plus grande (voir Figure 3.2).

Figure 3.2
Passer la page dans une grande police.

36

jQuery

La liaison dun vnement nest pas plus complique que cela. Les avantages mentionns pour la mthode .ready() sappliquent galement dans ce cas. Il est possible dinvoquer plusieurs fois .bind() pour ajouter de nouveaux comportements au mme vnement. Toutefois, cette solution nest pas ncessairement la plus lgante ni la plus efficace. Au cours de ce chapitre, nous tendrons et amliorerons ce code, jusqu obtenir une version dont nous pourrons tre fiers. Activer les autres boutons Le bouton GRANDE POLICE fonctionne prsent comme voulu. Nous allons donc appliquer un traitement semblable aux deux autres boutons (PAR DFAUT et COLONNE TROITE) pour quils ralisent leur propre opration. Cela na rien de complexe : nous utilisons .bind() pour enregistrer un gestionnaire de click, en ajoutant et en retirant les classes comme ncessaire. Voici le nouveau code :
$(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass('narrow'); $('body').removeClass('large'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow'); $('body').removeClass('large'); }); $('#switcher-large').bind('click', function() { $('body').removeClass('narrow'); $('body').addClass('large'); }); });

Il est combin une rgle CSS pour la classe narrow :


body.narrow .chapter { width: 400px; }

Dsormais, en cliquant sur le bouton COLONNE TROITE, le style CSS correspondant est appliqu et le texte est affich diffremment (voir Figure 3.3). En cliquant sur le bouton PAR DFAUT, les deux classes sont retires de la balise <body> et la page revient dans sa prsentation initiale. Contexte dun gestionnaire dvnements Notre slecteur fonctionne correctement, mais lutilisateur ne sait pas quel bouton est actif. Pour proposer ce retour visuel, nous allons appliquer la classe selected au bouton sur lequel lutilisateur a cliqu et la retirer aux deux autres boutons. La classe selected met simplement le texte du bouton en gras :

Chapitre 3

vnements

37

Figure 3.3
Afficher le texte de la page dans une colonne troite. .selected { font-weight: bold; }

Pour modifier la classe, nous pouvons procder comme prcdemment, en faisant rfrence chaque bouton par son identifiant et en appliquant ou en retirant les classes, mais nous allons examiner une solution plus lgante et volutive qui se fonde sur le contexte dexcution des gestionnaires dvnements. Lorsquun gestionnaire dvnements est invoqu, le mot cl this fait rfrence llment du DOM auquel le comportement a t associ. Nous lavons mentionn prcdemment, la fonction $() peut prendre en argument un lment du DOM ; cest dailleurs son utilisation principale. En crivant $(this) dans le gestionnaire dvnements, nous crons un objet jQuery qui correspond llment cibl et nous pouvons le manipuler comme si nous lavions obtenu laide dun slecteur CSS. Nous pouvons donc crire le code suivant :
$(this).addClass('selected');

38

jQuery

Si nous plaons cette ligne dans chacun des trois gestionnaires, la classe est ajoute ds que lutilisateur clique sur un bouton. Pour retirer la classe des autres boutons, nous pouvons tirer profit de litration implicite de jQuery :
$('#switcher .button').removeClass('selected');

Cette ligne retire la classe de tous les boutons prsents dans le slecteur de style. En plaant les instructions dans lordre adquat, nous obtenons le code suivant :
$(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass('narrow'); $('body').removeClass('large'); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow'); $('body').removeClass('large'); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); $('#switcher-large').bind('click', function() { $('body').removeClass('narrow'); $('body').addClass('large'); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); });

La Figure 3.4 montre que le slecteur de style indique prsent le bouton actif. En gnralisant le contexte du gestionnaire toutes les instructions, nous pouvons tre plus efficaces. Le code de mise en exergue peut tre extrait dans un gestionnaire spar car il est identique pour les trois boutons :
$(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass('narrow').removeClass('large'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow').removeClass('large'); }); $('#switcher-large').bind('click', function() { $('body').removeClass('narrow').addClass('large'); }); $('#switcher .button').bind('click', function() { $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); });

Cette optimisation tire parti des trois caractristiques de jQuery que nous avons tudies. Premirement, litration implicite permet de lier le mme gestionnaire de click

Chapitre 3

vnements

39

Figure 3.4
Le bouton sur lequel lutilisateur a cliqu est indiqu en gras.

chaque bouton par un seul appel .bind(). Deuximement, la file des comportements permet de lier deux fonctions au mme vnement click, sans que le second remplace le premier. Troisimement, nous utilisons les possibilits de chanage de jQuery pour regrouper lajout et la suppression des classes dans une seule ligne de code. Autre consolidation Loptimisation du code que nous venons deffectuer est un exemple de remaniement, cest--dire une modification du code permettant de raliser la mme tche de manire plus efficace et/ou plus lgante. Afin dillustrer dautres opportunits de remaniement, examinons les comportements associs chaque bouton. Largument de la mthode .removeClass() est facultatif. Lorsquil est absent, la mthode retire toutes les classes de llment. Nous pouvons allger notre code en exploitant cette possibilit de la manire suivante :
$(document).ready(function() { $('#switcher-default').bind('click', function() { $('body').removeClass(); });

40

jQuery

$('#switcher-narrow').bind('click', function() { $('body').removeClass().addClass('narrow'); }); $('#switcher-large').bind('click', function() { $('body').removeClass().addClass('large'); }); $('#switcher .button').bind('click', function() { $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); });

Lordre des oprations a lgrement chang pour tenir compte de la suppression plus gnrale des classes. Nous devons commencer par invoquer .removeClass() afin quelle nannule pas les effets de lappel .addClass().
ATTENTION
Nous pouvons retirer toutes les classes sans inquitude car le contenu HTML est galement sous notre responsabilit. Si vous crivez du code en vue de sa rutilisation, par exemple sous forme de plugin, vous devez tenir compte des classes qui peuvent exister et les conserver.

Chaque gestionnaire de bouton excute dsormais un code semblable. Nous pouvons facilement lextraire dans notre gestionnaire gnral de clic sur un bouton :
$(document).ready(function() { $('#switcher .button').bind('click', function() { $('body').removeClass(); $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); $('#switcher-narrow').bind('click', function() { $('body').addClass('narrow'); }); $('#switcher-large').bind('click', function() { $('body').addClass('large'); }); });

Vous aurez remarqu que le gestionnaire gnral est plac avant les gestionnaires plus spcifiques. La mthode .removeClass() doit tre invoque avant .addClass(). Nous pouvons le certifier car jQuery appelle toujours les gestionnaires dvnements dans lordre de leur enregistrement. Enfin, nous pouvons totalement nous dbarrasser des gestionnaires spcifiques en tirant profit du contexte dvnement. Puisque le mot cl this nous retourne un lment du DOM la place dun objet jQuery, nous pouvons nous servir des proprits natives du DOM pour dterminer lidentifiant de llment sur lequel lutilisateur a cliqu. Nous pouvons ainsi lier le mme gestionnaire tous les boutons et y effectuer les diffrentes actions :

Chapitre 3

vnements

41

$(document).ready(function() { $('#switcher .button').bind('click', function() { $('body').removeClass(); if (this.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (this.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); });

Raccourcis pour les vnements Lassociation dun gestionnaire un vnement, par exemple un simple vnement click, est une opration si frquente que jQuery propose une solution plus concise pour la raliser. Les mthodes abrges pour les vnements oprent de la mme manire que leurs quivalents .bind(), mais demandent une saisie moindre. Par exemple, notre slecteur de style peut invoquer la mthode .click() la place de .bind() :
$(document).ready(function() { $('#switcher .button').click(function() { $('body').removeClass(); if (this.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (this.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); }); });

Le Tableau 3.1 recense les diffrentes mthodes abrges pour les vnements. Chacune associe un gestionnaire lvnement ponyme.
Tableau 3.1 : Les mthodes abrges pour les vnements

blur change click dblclick

error focus keydown keypress

keyup load mousedown mousemove

mouseout mouseover mouseup resize

scroll select submit unload

42

jQuery

3.3

vnements composs

La plupart des mthodes jQuery pour la gestion des vnements correspondent directement aux vnements JavaScript natifs. Toutefois, quelques gestionnaires personnaliss ont t ajouts pour des raisons de commodit et doptimisation entre navigateurs. Nous avons dj rencontr lun deux : .ready(). Les mthodes .toggle() et .hover() en sont deux autres ; il sagit de gestionnaires dvnements composs car elles interceptent une combinaison dactions de lutilisateur et y rpondent en utilisant plusieurs fonctions. Afficher et masquer des fonctions avances Supposons que nous souhaitions pouvoir masquer le slecteur de style lorsquil nest pas utile. Pour masquer des fonctionnalits avances, une solution commode consiste les rendre escamotables. Nous allons faire en sorte quun clic sur le libell masque les boutons et ne laisse affich que le libell. Un autre clic sur le libell raffichera les boutons. Nous avons besoin dune nouvelle classe pour les boutons masqus :
.hidden { display: none; }

Pour mettre en place cette fonctionnalit, nous pourrions enregistrer ltat courant des boutons dans une variable et vrifier la valeur de celle-ci chaque clic sur le libell de manire savoir si la classe hidden doit tre ajoute ou retire aux boutons. Nous pourrions galement vrifier directement lexistence de cette classe sur un bouton et nous servir de cette information pour dcider de laction mener. Cependant, jQuery propose la mthode .toggle(), qui prend tout cela en charge notre place.
INFO
jQuery dfinit en ralit deux mthodes .toggle(). Pour de plus amples informations sur la mthode deffet de mme nom, qui se distingue par des types darguments diffrents, consultez la page http://docs.jquery.com/Effects/toggle.

La mthode dvnement .toggle() prend au moins deux arguments, chacun tant une fonction. Le premier clic sur llment provoque lexcution de la premire fonction, le deuxime clic, lexcution de la deuxime fonction, etc. Aprs que chaque fonction a t invoque, le cycle reprend partir de la premire. Grce .toggle(), nous pouvons mettre en uvre notre slecteur de style escamotable assez facilement :
$(document).ready(function() { $('#switcher h3').toggle(function() { $('#switcher .button').addClass('hidden'); }, function() {

Chapitre 3

vnements

43

$('#switcher .button').removeClass('hidden'); }); });

Aprs un premier clic, tous les boutons sont masqus (voir Figure 3.5), un second les affiche nouveau (voir Figure 3.6).
Figure 3.5
Un premier clic sur le libell masque les boutons.

Figure 3.6
Un second clic sur le libell raffiche les boutons.

Nous avons nouveau exploit litration implicite, cette fois-ci pour masquer tous les boutons en une seule fois sans avoir besoin dun lment englobant. Pour ce type descamotage, jQuery propose un autre mcanisme. La mthode .toggleClass() permet de vrifier automatiquement lexistence de la classe avant de lappliquer ou de la retirer :
$(document).ready(function() { $('#switcher h3').click(function() { $('#switcher .button').toggleClass('hidden'); }); });

Dans notre cas, .toggleClass() constitue probablement la solution la plus lgante. .toggle() est une mthode plus gnrale pour effectuer alternativement deux actions diffrentes ou plus. Signaler les lments cliquables En utilisant lvnement click avec des lments de la page normalement non cliquables, nous avons fabriqu une interface qui donne peu dindications sur le fait que les boutons, qui ne sont en ralit que des lments <div>, constituent la partie active de la page qui attend une action de lutilisateur. Pour remdier ce dfaut, nous pouvons associer un effet de survol aux boutons de manire indiquer clairement quils peuvent ragir au clic :
#switcher .hover { cursor: pointer; background-color: #afa; }

44

jQuery

La spcification CSS mentionne la pseudo-classe nomme :hover. Elle permet de changer le style dun lment lorsque le pointeur de la souris le survole. Dans Internet Explorer 6, cette fonctionnalit se borne aux liens et nous ne pouvons donc pas lutiliser pour dautres lments dans un code multinavigateur. En revanche, jQuery nous permet dutiliser JavaScript pour modifier le style dun lment et, videmment, deffectuer nimporte quelle action, lorsque le pointeur de la souris arrive sur llment et lorsquil en sort. linstar de .toggle() dans notre exemple prcdent, la mthode .hover() prend deux fonctions en argument. La premire est excute lorsque le pointeur de la souris entre dans llment slectionn, la seconde, lorsquil le quitte. Pour obtenir leffet de survol, nous pouvons modifier ces moments-l les classes appliques aux boutons :
$(document).ready(function() { $('#switcher .button').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }); });

Nous employons nouveau litration implicite et le contexte dvnement pour garder un code simple et concis. Lorsque le pointeur de la souris survole un bouton, notre classe lui est applique et produit laffichage illustr la Figure 3.7.

Figure 3.7
Le survol dun bouton met celui-ci en exergue.

En utilisant .hover(), nous vitons galement les problmes associs la propagation des vnements en JavaScript. Pour les comprendre, nous devons examiner la faon dont JavaScript dcide de llment qui traite un vnement donn.

Chapitre 3

vnements

45

3.4

Le priple dun vnement

Lorsquun vnement se produit sur une page, tous les lments de larborescence du DOM ont lopportunit de le prendre en charge. Prenons lextrait de page suivant :
<div class="foo"> <span class="bar"> <a href="http://www.example.com/"> Zoe ma grande fille veut que je boive ce whisky dont je ne veux pas. </a> </span> <p> Voyez ce bon fakir moqueur pousser un wagon en jouant du xylophone. </p> </div>

La Figure 3.8 reprsente ce balisage sous forme dlments imbriqus.


Figure 3.8
Limbrication des lments de la page.
<div> <span> <a> <p>

Quel que soit lvnement, plusieurs lments peuvent logiquement tre responsables de sa prise en charge. Par exemple, le clic sur le lien de la page peut tre trait par les lments <div>, <span> ou <a>. En effet, ils sont tous les trois sous le pointeur de la souris en mme temps. En revanche, llment <p> ne fait pas partie de linteraction. Pour permettre plusieurs lments de rpondre au clic, une stratgie consiste capturer lvnement. Avec cette approche, lvnement est tout dabord pass llment le plus englobant puis, successivement, aux lments internes. Dans notre exemple, cela signifie que le <div> reoit en premier lvnement, puis le <span> et finalement le <a>. La Figure 3.9 illustre cette stratgie.
Figure 3.9
Principe de la capture de lvnement.
<div> <span> <a> <p>

Capture

46

jQuery

INFO
Techniquement, dans la mise en uvre de la capture dvnements par les navigateurs, chaque lment senregistre en tant quauditeur des vnements qui surviennent dans leurs descendants. Lapproximation faite ici suffit nos besoins.

La stratgie oppose se nomme bouillonnement de lvnement. Lvnement est envoy llment le plus interne et, aprs que celui-ci a eu lopportunit de le traiter, lvnement remonte vers les lments externes. Dans notre exemple, le <a> traiterait en premier lvnement, puis ce serait au tour du <span> et enfin du <div> (voir Figure 3.10).
Figure 3.10
Principe du bouillonnement de lvnement.
<div> <span> <a> <p>

Bouillonnement

Les dveloppeurs des diffrents navigateurs ont, sans surprise, choisi des modles diffrents pour la propagation des vnements. La spcification du DOM, qui a fini par tre rdige, stipule que les deux stratgies doivent tre employes : tout dabord, lvnement est captur par les lments externes et transmis aux lments internes, puis lvnement bouillonne vers le sommet de larborescence du DOM. Les gestionnaires dvnements peuvent tre enregistrs dans lune des phases du processus. Tous les navigateurs nont pas t mis jour pour se conformer ce nouveau standard et, pour ceux qui prennent en charge la capture, elle doit gnralement tre active explicitement. Par consquent, pour assurer la compatibilit entre les navigateurs, jQuery enregistre toujours les gestionnaires dvnements dans la phase de remonte. Nous pouvons donc toujours supposer que llment le plus interne a en premier lopportunit de rpondre nimporte quel vnement. Effets secondaires du bouillonnement dvnement La remonte dvnement peut provoquer des comportements inattendus, en particulier lorsque le mauvais lment rpond un mouseover ou un mouseout. Prenons le cas dun gestionnaire dvnements mouseout associ au <div> de notre exemple. Lorsque le pointeur de la souris quitte le <div>, le gestionnaire de mouseout est invoqu, comme attendu. Puisquil se trouve au sommet de la hirarchie, aucun autre lment ne reoit

Chapitre 3

vnements

47

lvnement. En revanche, lorsque le pointeur quitte llment <a>, un vnement mouseout lui est pass. Cet vnement remonte ensuite jusquau <span> et au <div>, dclenchant linvocation du mme gestionnaire. Cette squence de remonte nest pas vraiment souhaite. Dans le cas des boutons de notre slecteur de style, cela pourrait conduire la dsactivation prmature de la mise en exergue. La mthode .hover() tient compte de ces problmes de remonte et, lorsque nous lutilisons pour associer des vnements, nous pouvons ignorer les problmes lis la rception dun vnement mouseover ou mouseout par le mauvais lment. En cela, .hover() est une alternative sduisante la liaison individuelle des vnements de la souris.
INFO
Si une action doit tre effectue uniquement lorsque le pointeur de la souris entre dans un lment ou en sort, mais pas les deux, vous pouvez lier les vnements jQuery mouseenter et mouseleave, qui vitent galement les problmes du bouillonnement. Toutefois, puisque ces vnements sont souvent utiliss par paire, la mthode .hover() constitue gnralement la meilleure solution.

Le scnario mouseout que nous venons de dcrire illustre le besoin dune contrainte sur la porte dun vnement. Si la mthode .hover() prend en charge ce cas prcis, nous rencontrerons dautres situations dans lesquelles nous devrons borner un vnement dun point de vue spatial (empcher sa propagation certains lments) ou temporel (empcher sa propagation certains moments).

3.5

Modifier le priple : lobjet event

Nous avons dj dcrit une situation dans laquelle la remonte dvnements peut tre source de problmes. De manire illustrer un cas o la mthode .hover() napporte aucune aide, nous allons modifier la procdure descamotage mise en uvre. Supposons que nous souhaitions tendre la zone cliquable qui dclenche lescamotage ou lapparition du slecteur de style. Pour cela, nous pouvons retirer le gestionnaire dvnements du libell <h3> et lattribuer son lment <div> englobant :
$(document).ready(function() { $('#switcher').click(function() { $('#switcher .button').toggleClass('hidden'); }); });

Suite cette modification, lintgralit de la zone occupe par le slecteur de style est cliquable et permet de modifier son affichage. Toutefois, en cliquant sur un bouton,

48

jQuery

nous masquons galement le slecteur de style aprs avoir modifi le style du contenu. Ce comportement inattendu vient du bouillonnement de lvnement. Il est tout dabord trait par les boutons, puis il remonte larborescence du DOM jusqu atteindre llment <div id="switcher">, sur lequel notre nouveau gestionnaire est activ, avec pour consquence de masquer les boutons. Pour rsoudre ce problme, nous devons accder lobjet event. Il sagit dune construction JavaScript passe tout gestionnaire dvnements dun lment lors de son invocation. Il fournit diffrentes informations concernant lvnement, comme la position du pointeur de la souris au moment de lvnement. Certaines de ses mthodes permettent galement dintervenir sur le priple de lvnement dans le DOM. Pour utiliser lobjet event dans nos gestionnaires, nous devons simplement ajouter un paramtre la fonction :
$(document).ready(function() { $('#switcher').click(function(event) { $('#switcher .button').toggleClass('hidden'); }); });

Cibles dun vnement Lobjet event est prsent disponible dans le gestionnaire, par lintermdiaire de la variable event. La proprit event.target permet de contrler la cible dun vnement. Elle fait partie de lAPI du DOM, mais elle nest pas disponible dans tous les navigateurs ; jQuery modifie lobjet event pour que cette proprit existe dans tous les navigateurs. Grce .target, nous pouvons dterminer llment du DOM qui tait le premier recevoir lvnement. Autrement dit, dans le cas dun vnement click, llment sur lequel lutilisateur a cliqu. Puisque nous pouvons ainsi connatre llment du DOM qui traite lvnement, nous crivons le code suivant :
$(document).ready(function() { $('#switcher').click(function(event) { if (event.target == this) { $('#switcher .button').toggleClass('hidden'); } }); });

Il vrifie que lutilisateur a bien cliqu sur llment <div id="switcher">, non sur lun de ses sous-lments. prsent, un clic sur lun des boutons nescamotera pas le slecteur de style, contrairement un clic sur larrire-plan du slecteur. Toutefois, un clic sur le libell <h3> naura aucun effet car il sagit galement dun sous-lment. Au lieu de placer la vrification ce niveau, nous pouvons modifier le comportement des boutons de manire atteindre notre objectif.

Chapitre 3

vnements

49

Stopper la propagation dun vnement Lobjet event fournit la mthode .stopPropagation() pour interrompre totalement le processus de remonte de lvnement. Comme .target, cette mthode est une fonctionnalit purement JavaScript, mais sa fiabilit nest pas garantie sur tous les navigateurs. Toutefois, tant que les gestionnaires dvnements sont enregistrs laide de jQuery, nous pouvons lemployer sans inquitude. Nous retirons le test event.target == this prcdent et ajoutons du code dans les gestionnaires de click associs aux boutons :
$(document).ready(function() { $('#switcher .button').click(function(event) { $('body').removeClass(); if (this.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (this.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(this).addClass('selected'); event.stopPropagation(); }); });

Nous ajoutons videmment un paramtre la fonction utilise pour le traitement de click de manire avoir accs lobjet event. Ensuite, nous invoquons simplement event.stopPropagation() pour viter que dautres lments du DOM rpondent lvnement. prsent, les clics sur les boutons sont pris en charge uniquement par les boutons ; les clics sur dautres lments du slecteur de style lafficheront ou le masqueront. Actions par dfaut Si notre gestionnaire de lvnement click tait associ un lien (<a>) la place dun <div> gnrique, nous ferions face un autre problme. Lorsquun utilisateur clique sur un lien, le navigateur charge une nouvelle page. Ce comportement ne correspond pas un gestionnaire dvnements tels que ceux dcrits prcdemment. Il sagit dune action par dfaut pour un clic sur un lien. De manire quivalente, un appui sur la touche Entre pendant ldition dun formulaire dclenche lvnement submit sur le formulaire, qui est ensuite envoy. Si ces actions par dfaut ne sont pas souhaites, linvocation de .stopPropagation() sur lvnement ne sera daucune aide car elles ne font pas partie du flux normal de propagation dun vnement. Pour empcher que lvnement ne dclenche laction par dfaut, il faut utiliser la mthode .preventDefault().

50

jQuery

INFO
Linvocation de .preventDefault() est souvent utile aprs avoir effectu quelques tests sur le contexte de lvnement. Par exemple, au cours de la soumission dun formulaire, vous pourriez vouloir vrifier que les champs obligatoires sont remplis et, dans la ngative, empcher que laction par dfaut ne soit effectue. Nous y reviendrons au Chapitre 8.

La propagation des vnements et les actions par dfaut sont des mcanismes indpendants. Lun peut tre arrt pendant que lautre se poursuit. Pour stopper les deux, le gestionnaire dvnements doit retourner false. Cela quivaut une invocation de .stopPropagation() et de .preventDefault() sur lvnement. Dlguer un vnement Le bouillonnement nest pas toujours gnant. Nous pouvons mme souvent lutiliser notre avantage. La dlgation dvnement est une technique intressante qui se fonde sur le bouillonnement. Elle nous permet dutiliser un gestionnaire dvnements dun seul lment pour effectuer le travail de plusieurs.
INFO
Dans jQuery 1.3, deux nouvelles mthodes, .live() et .die(), sont apparues. Elles jouent le mme rle que .bind() et .unbind(), mais elles se fondent sur la dlgation dvnement pour bnficier des avantages dcrits dans cette section. Pour de plus amples informations concernant ces mthodes, consultez la page http://docs.jquery.com/Events/live.

Dans notre exemple, des gestionnaires de click sont associs seulement trois lments <div class="button">. Que se passerait-il sils taient plus nombreux ? Ce cas se produit plus frquemment quon pourrait le croire. Par exemple, prenez un grand tableau dinformations dans lequel chaque ligne contient un lment interactif ayant besoin dun gestionnaire de click. Grce litration implicite, il est trs facile dassocier tous ces gestionnaires, mais les performances peuvent se dgrader en raison des boucles internes effectues par jQuery et de la mmoire requise par les gestionnaires. la place, nous pouvons lier un seul gestionnaire de click un lment anctre et, grce au bouillonnement, un click non interrompu finira par atteindre cet anctre, o le traitement requis sera ralis. Afin dillustrer cette technique, appliquons-la notre slecteur de style, mme si le nombre dlments ne limpose pas. Nous lavons vu prcdemment, la proprit event.target nous permet de connatre llment qui se trouvait sous le pointeur de la souris au moment du clic :

Chapitre 3

vnements

51

$(document).ready(function() { $('#switcher').click(function(event) { if ($(event.target).is('.button')) { $('body').removeClass(); if (event.target.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (event.target.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(event.target).addClass('selected'); event.stopPropagation(); } }); });

Dans ce code, nous utilisons une nouvelle mthode nomme .is(). Elle accepte en argument les expressions de slection dcrites au Chapitre 2 et compare lobjet jQuery courant celui obtenu avec le slecteur. Si au moins lun des lments du jeu correspond au slecteur, .is() retourne true. Dans notre code, la construction $(event.target).is('.button') vrifie si llment sur lequel lutilisateur a cliqu possde la classe button. Dans laffirmative, le corps de la condition est excut, avec une modification importante : le mot cl this fait prsent rfrence au <div id="switcher"> et, chaque fois que nous voulons faire rfrence au bouton sur lequel on a cliqu, nous devons le dsigner par event.target.
INFO
Vous pouvez galement vrifier la prsence dune classe sur un lment en utilisant la mthode .hasClass(). Toutefois, .is() est plus souple et permet de tester nimporte quelle expression de slection.

Ce code prsente cependant un effet secondaire involontaire. Le clic sur un bouton escamote le slecteur, comme cela se produisait lors de lajout de lappel .stopPropagation(). Le gestionnaire daffichage du slecteur est prsent associ au mme lment que le gestionnaire des boutons. Par consquent, linterruption du bouillonnement nempche pas lexcution de la procdure descamotage. Pour rsoudre ce problme, nous pouvons retirer lappel .stopPropagation() et ajouter la place un autre test .is() :
$(document).ready(function() { $('#switcher').click(function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }); });

52

jQuery

$(document).ready(function() { $('#switcher').click(function(event) { if ($(event.target).is('.button')) { $('body').removeClass(); if (event.target.id == 'switcher-narrow') { $('body').addClass('narrow'); } else if (event.target.id == 'switcher-large') { $('body').addClass('large'); } $('#switcher .button').removeClass('selected'); $(event.target).addClass('selected'); } }); });

Cet exemple est un peu complexe pour sa taille. Toutefois, lorsque le nombre dlments possdant des gestionnaires dvnements augmente, la dlgation dvnement est une technique de choix.
INFO
Vous le verrez, la dlgation dvnement est galement utile dans dautres situations, par exemple lorsque de nouveaux lments sont ajouts laide des mthodes de manipulation du DOM (voir Chapitre 5) ou des procdures AJAX (voir Chapitre 6).

3.6

Retirer un gestionnaire dvnements

Il arrive parfois quun gestionnaire dvnements prcdemment enregistr ne soit plus utile. Ltat de la page a peut-tre volu et laction effectue na alors plus de sens. Ce type de situation peut tre pris en charge par des instructions conditionnelles dans le gestionnaire dvnements, mais il est sans doute plus lgant de le retirer totalement. Supposons que nous voulions que notre slecteur de style escamotable reste ouvert lorsque la page ne se trouve pas dans le style normal. Si le bouton COLONNE TROITE ou le bouton GRANDE POLICE est slectionn, un clic sur larrire-plan du slecteur de style ne doit avoir aucun effet. Pour cela, suite au clic sur lun des boutons autres que PAR DFAUT, nous pouvons invoquer la mthode .unbind() de manire retirer le gestionnaire descamotage :
$(document).ready(function() { $('#switcher').click(function(event) { if (!$(event.tsearget).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click'); }); });

Chapitre 3

vnements

53

Un clic sur un bouton comme COLONNE TROITE retire ainsi le gestionnaire de click sur le <div> du slecteur de style, et un clic sur larrire-plan du rectangle ne le masque plus. Toutefois, le bouton nest plus oprationnel ! En effet, il est galement li lvnement click du <div> du slecteur, car nous avons rcrit le code de gestion du bouton en utilisant la dlgation dvnement. Autrement dit, linstruction $('#switcher'). unbind('click') retire les deux comportements. Espace de noms dun vnement Notre invocation de .unbind() doit tre plus prcise afin de ne pas retirer les deux gestionnaires de click enregistrs. Pour cela, nous pouvons utiliser les espaces de noms des vnements. Au moment de la liaison dun vnement, nous pouvons fournir des informations complmentaires qui permettront didentifier ultrieurement le gestionnaire. Pour utiliser les espaces de noms, nous devons revenir la mthode non abrge de liaison des gestionnaires dvnements, cest--dire .bind(). Le premier paramtre pass .bind() correspond au nom de lvnement JavaScript que nous souhaitons surveiller. Nous pouvons opter pour une syntaxe particulire qui permet de crer des sous-catgories dvnements.
$(document).ready(function() { $('#switcher').bind('click.collapse', function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click.collapse'); }); });

Le suffixe .collapse nest pas interprt par le systme de gestion des vnements ; les vnements click sont traits par cette fonction comme si nous avions crit simplement .bind('click'). Toutefois, lajout dun espace de noms nous permet de retirer uniquement le gestionnaire dsign, sans affecter celui associ aux boutons.
INFO
Nous allons le voir, il existe dautres manires de rendre lappel .unbind() plus prcis. Cependant, les espaces de noms des vnements constituent un outil intressant, en particulier pour la cration de plugins (voir Chapitre 11).

Lier nouveau des vnements Un clic sur les boutons COLONNE TROITE ou GRANDE POLICE dsactive prsent la fonctionnalit descamotage. Toutefois, nous souhaitons que ce comportement soit rtabli

54

jQuery

lors dun clic sur le bouton PAR DFAUT. Pour cela, nous devons lier nouveau le gestionnaire suite au clic sur le bouton PAR DFAUT. Tout dabord, nous nommons la fonction du gestionnaire afin de pouvoir la rutiliser sans avoir rpter le code :
$(document).ready(function() { var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').bind('click.collapse', toggleStyleSwitcher); });

Vous constatez que nous choisissons une nouvelle syntaxe pour la dfinition de la fonction. Au lieu dutiliser le mot cl function, nous affectons une fonction anonyme une variable locale. Grce cette mthode, les dfinitions des gestionnaires dvnements et des autres fonctions sont semblables ; les deux syntaxes sont fonctionnellement quivalentes. Rappelons galement que la mthode .bind() prend une rfrence de fonction en second argument. Lorsquon utilise une fonction nomme ici, il ne faut pas oublier domettre les parenthses aprs le nom, car les parenthses provoqueraient linvocation de la fonction au lieu den passer une rfrence. Puisque la fonction est nomme, nous pouvons la lier nouveau sans rpter sa dfinition :
$(document).ready(function() { var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').bind('click.collapse', toggleStyleSwitcher); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click.collapse'); }); $('#switcher-default').click(function() { $('#switcher').bind('click.collapse', toggleStyleSwitcher); }); });

La procdure descamotage est lie au chargement du document, dlie lors dun clic sur COLONNE TROITE ou GRANDE POLICE et lie nouveau lors du clic sur PAR DFAUT. Nous avons russi viter un pige potentiel ici. Lorsquun gestionnaire est li un vnement dans jQuery, les gestionnaires prcdents restent actifs. Autrement dit, en cas de multiples clics successifs sur le bouton PAR DFAUT, plusieurs exemplaires du gestionnaire toggleStyleSwitcher seront lis, conduisant un comportement trange

Chapitre 3

vnements

55

lors dun clic sur le <div>. Ce serait effectivement le cas si nous avions utilis des fonctions anonymes. Toutefois, puisque la fonction est nomme et que nous utilisons le mme nom tout au long du code, le comportement nest li quune seule fois. La mthode .bind() nassocie pas un gestionnaire dvnements un lment sil y est dj associ. Le nommage de la fonction prsente un autre intrt : nous navons plus utiliser les espaces de noms. La mthode .unbind() est en mesure de dlier un gestionnaire spcifique en passant la fonction correspondante en second argument :
$(document).ready(function() { var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').click(toggleStyleSwitcher); $('#switcher-narrow, #switcher-large').click(function() { $('#switcher').unbind('click', toggleStyleSwitcher); }); $('#switcher-default').click(function() { $('#switcher').click(toggleStyleSwitcher); }); });

Il existe galement une version abrge pour le cas o nous souhaiterions dlier un gestionnaire dvnements juste aprs son premier dclenchement. Voici comment utiliser la mthode .one() :
$(document).ready(function() { $('#switcher').one('click', toggleStyleSwitcher); });

Cela permet deffectuer laction une seule fois.

3.7

Simuler une action de lutilisateur

Parfois, il peut tre utile dexcuter le code que nous avons li un vnement, mme si les circonstances normales de lvnement ne se produisent pas. Par exemple, supposons que nous souhaitions que le slecteur de style soit initialement escamot. Pour cela, nous pouvons masquer les boutons en utilisant la feuille de style ou en invoquant la mthode .hide() partir dun gestionnaire $(document).ready(). Il existe cependant une autre solution : simuler un clic sur le slecteur de style afin que le mcanisme descamotage mis en place soit dclench. La mthode .trigger() nous permet de mettre en uvre cette solution :
$(document).ready(function() { $('#switcher').trigger('click'); });

56

jQuery

Lorsque la page est charge, le slecteur est escamot, comme si lutilisateur avait cliqu dessus (voir Figure 3.11). Si nous masquions du contenu en voulant une solution compatible avec les navigateurs dans lesquels JavaScript nest pas activ, cette approche serait une bonne manire de mettre en place une amlioration progressive.

Figure 3.11
Escamoter le slecteur de style en simulant un clic.

La mthode .trigger() propose le mme jeu de raccourcis que la mthode .bind(). Lorsque ces versions abrges sont utilises sans argument, laction est dclenche au lieu dtre lie :
$(document).ready(function() { $('#switcher').click(); });

vnements du clavier Dans ce nouvel exemple, nous allons ajouter des raccourcis clavier notre slecteur de style. En appuyant sur la touche quivalant la premire lettre dun style, la page est affiche comme si lutilisateur avait cliqu sur le bouton correspondant. Pour mettre en uvre cette fonctionnalit, nous devons examiner les vnements du clavier, dont le comportement diffre lgrement des vnements de la souris. Il existe deux types dvnements du clavier : ceux qui proviennent directement du clavier (keyup et keydown) et ceux qui proviennent de la saisie dun texte (keypress). Un vnement issu de la saisie dun seul caractre peut correspondre plusieurs touches, par exemple lorsque la touche Maj est utilise conjointement la touche X pour obtenir la lettre X majuscule. Si les dtails de limplmentation varient dun navigateur lautre, voici une rgle gnrale relativement fiable : si vous souhaitez connatre la

Chapitre 3

vnements

57

touche sur laquelle lutilisateur a appuy, vous devez intercepter les vnements keyup ou keydown ; si vous souhaitez connatre le caractre affich sur lcran suite cette action, vous devez auditer lvnement keypress. Dans notre cas, puisque nous voulons savoir si lutilisateur a appuy sur les touches P, C ou G, nous utiliserons keyup. Ensuite, nous devons dterminer les lments concerns par lvnement. La rponse est moins vidente que dans le cas des vnements de la souris, o le pointeur nous indiquait clairement la cible de lvnement. La cible dun vnement du clavier est llment qui dtient le focus du clavier. Il existe plusieurs manires de changer llment qui a le focus, notamment en cliquant avec la souris ou en appuyant sur la touche Tab. Par ailleurs, tous les lments ne reoivent pas le focus. Seuls ceux qui possdent par dfaut des comportements orients clavier, comme les champs de formulaire, les liens et les lments avec une proprit .tabIndex, sont candidats. Dans notre cas, llment qui possde le focus na pas rellement dimportance. Nous souhaitons que notre slecteur ragisse ds que lutilisateur appuie sur lune des touches. Le bouillonnement dvnement va encore se rvler pratique, car nous pouvons associer lvnement keyup llment document en tant certains quil finira par arriver jusqu nous. Enfin, nous devons savoir quelle touche a dclench le gestionnaire de keyup. Pour cela, nous pouvons examiner lobjet event. La proprit .keyCode de lvnement contient un identifiant de la touche appuye, qui, pour les touches alphabtiques, correspond la valeur ASCII de la lettre majuscule. Nous pouvons donc nous fonder sur cette valeur pour simuler un clic sur le bouton appropri :
$(document).ready(function() { $(document).keyup(function(event) { switch (String.fromCharCode(event.keyCode)) { case 'P': $('#switcher-default').click(); break; case 'C': $('#switcher-narrow').click(); break; case 'G': $('#switcher-large').click(); break; } }); });

Un appui sur lune de ces trois touches simule un clic sur le bouton correspondant, condition que lvnement du clavier ne soit pas interrompu par des fonctionnalits du navigateur comme "la recherche du texte lorsque la saisie dbute" mise en uvre par Firefox.

58

jQuery

Au lieu dutiliser .trigger() pour simuler un clic, voyons comment dplacer le code dans une fonction afin que plusieurs gestionnaires puissent linvoquer dans notre cas, ceux de click et de keyup. Bien quelle ne simpose pas dans notre exemple, cette technique permet de rduire la redondance du code.
$(document).ready(function() { // Activer leffet de survol sur les boutons du slecteur de style. $('#switcher .button').hover(function() { $(this).addClass('hover'); }, function() { $(this).removeClass('hover'); }); // Permettre laffichage et le masquage du slecteur de style. var toggleStyleSwitcher = function(event) { if (!$(event.target).is('.button')) { $('#switcher .button').toggleClass('hidden'); } }; $('#switcher').click(toggleStyleSwitcher); // Simuler un clic de manire dbuter dans ltat escamot. $('#switcher').click(); // La fonction setBodyClass() modifie le style de la page. // Ltat du slecteur de style est galement actualis. var setBodyClass = function(className) { $('body').removeClass(); $('body').addClass(className); $('#switcher .button').removeClass('selected'); $('#switcher-' + className).addClass('selected'); if (className == 'default') { $('#switcher').click(toggleStyleSwitcher); } else { $('#switcher').unbind('click', toggleStyleSwitcher); $('#switcher .button').removeClass('hidden'); } }; // Invoquer setBodyClass() lors du clic sur un bouton. $('#switcher').click(function(event) { if ($(event.target).is('.button')) { if (event.target.id == 'switcher-default') { setBodyClass('default'); } if (event.target.id == 'switcher-narrow') { setBodyClass('narrow'); } else if (event.target.id == 'switcher-large') { setBodyClass('large'); } } });

Chapitre 3

vnements

59

// Invoquer setBodyClass() lors de lappui sur une touche. $(document).keyup(function(event) { switch (String.fromCharCode(event.keyCode)) { case 'P': setBodyClass('default'); break; case 'C': setBodyClass('narrow'); break; case 'G': setBodyClass('large'); break; } }); });

3.8

En rsum

Les points examins dans ce chapitre nous permettent plusieurs choses :


n

Utiliser plusieurs bibliothques JavaScript dans une mme page en invoquant la mthode .noConflict(). Dfinir des gestionnaires dvnements de la souris pour ragir au clic effectu par lutilisateur sur un lment de la page, en utilisant .bind() ou .click(). Examiner le contexte dvnement de manire raliser des actions diffrentes selon llment de la page sur lequel on a cliqu, mme lorsque le gestionnaire est li plusieurs lments. Basculer entre laffichage et le masquage dun lment de la page, en utilisant la mthode .toggle(). Mettre en exergue llment de la page qui se trouve sous le pointeur de la souris, en utilisant .hover(). Intervenir sur la propagation dun vnement pour choisir les lments qui traiteront lvnement, en utilisant .stopPropagation() et .preventDefault(). Mettre en uvre la dlgation dvnement de manire rduire le nombre de gestionnaires dvnements lis. Invoquer .unbind() pour retirer un gestionnaire dvnements lorsquil est devenu inutile. Isoler des gestionnaires dvnements connexes laide dun espace de noms dvnements afin de les manipuler comme un groupe. Dclencher lexcution de gestionnaires dvnements avec .trigger().

60

jQuery

Employer les gestionnaires dvnements du clavier pour ragir aux appuis sur les touches du clavier par lutilisateur, en utilisant .keyup().

Lorsquelles sont combines, toutes ces possibilits permettent de construire des pages relativement interactives. Au chapitre suivant, nous verrons comment fournir un retour visuel lutilisateur au cours de ces interactions.

4
Effets
Au sommaire de ce chapitre
U U U U U U U

Modifier les styles CSS Bases du masquage/affichage Effets et vitesse Effets composs Crer des animations personnalises Effets simultans ou squentiels En rsum

Si les images sont plus parlantes que les mots, alors, dans le monde JavaScript, les effets rendent les images encore plus parlantes. Avec les jeux deffets visuels simples de jQuery, nous pouvons facilement donner de limpact nos actions, et mme construire nos propres animations sophistiques. Les effets jQuery ajoutent sans conteste une certaine classe la page, par exemple avec des lments qui saffichent progressivement au lieu dapparatre immdiatement. Mais ils amliorent galement lutilisation de la page, en aidant linternaute sorienter lorsque des modifications y sont apportes, en particulier avec les applications AJAX. Dans ce chapitre, nous examinerons ces effets et les combinerons de manire intressante.

4.1

Modifier les styles CSS

Avant dentrer dans les dtails des effets jQuery, nous devons tudier rapidement CSS. Dans les chapitres prcdents, nous avons modifi laspect dun document en dfinissant des classes dans une feuille de style spare et en les ajoutant ou en les retirant ensuite laide de jQuery. Cette forme dinjection des styles CSS dans un contenu HTML est gnralement conseille, car elle limite le rle de la feuille de style la prsentation dune page. Toutefois, il peut arriver que nous ayons besoin dappliquer des

62

jQuery

styles qui nont pas encore t dfinis dans une feuille de style, ou qui ne peuvent pas ltre aisment. Pour de tels cas, jQuery propose la mthode .css(). Cette mthode joue le rle daccesseur (getter) et de mutateur (setter). Pour obtenir la valeur dune proprit de style, nous passons simplement son nom sous forme dune chane de caractres, par exemple .css('backgroundColor'). jQuery est capable dinterprter les proprits dont le nom est constitu de plusieurs mots, que ce soit dans leur version avec tiret, comme dans la notation CSS (background-color), ou dans leur version avec majuscule au dbut des mots internes, comme dans la notation du DOM (backgroundColor). Pour fixer la valeur des proprits de style, la mthode .css() propose deux variantes. La premire prend en argument une seule proprit de style et sa valeur, la seconde, une mappe de couples proprit-valeur :
.css('proprit','valeur') .css({proprit1: 'valeur1', 'proprit-2': 'valeur2'})

Les programmeurs JavaScript expriments auront reconnu dans ces mappes jQuery des objets littraux JavaScript.
INFO
Les valeurs numriques ne sont pas places entre des apostrophes, contrairement aux chanes de caractres. Toutefois, pour les mappes, les apostrophes ne sont pas obligatoires autour des noms des proprits lorsquils sont crits en utilisant la notation du DOM.

Nous utilisons la mthode .css() de la mme manire que la mthode .addClass(), cest--dire en lenchanant un slecteur et en lassociant un vnement. Pour illustrer cela, revenons au slecteur de style du Chapitre 3, mais avec un balisage HTML diffrent :
<div id="switcher"> <div class="label">Taille du texte</div> <button id="switcher-default">Par dfaut</button> <button id="switcher-large">Plus grande</button> <button id="switcher-small">Plus petite</button> </div> <div class="speech"> <p>Devant Douaumont, en fvrier, mon tour arriva, et je partis. Je suis la ligne et rpare plusieurs fois jusqu' la ferme de Thiaumont. Les balles de mitrailleuses rasaient le boyau, et j'tais aveugl et couvert de terre par les obus.</p> </div>

Avec quelques rgles de style, nous obtenons la page illustre la Figure 4.1. Dans cette version du slecteur de style, nous utilisons des lments <button>. En cliquant sur les boutons PLUS GRANDE ou PLUS PETITE, nous augmentons ou diminuons la taille du texte contenu dans <div class="speech">. Un clic sur le bouton PAR DFAUT replace le texte de <div class="speech"> dans sa taille initiale.

Chapitre 4

Effets

63

Figure 4.1
Aspect initial de la page.

Si nous voulions simplement fixer la taille de la police une valeur fige, nous pourrions toujours utiliser la mthode .addClass(). Mais nous souhaitons prsent que la taille du texte continue augmenter ou diminuer lors de chaque appui sur le bouton correspondant. Mme sil est possible de dfinir une classe spare pour chaque clic et de les attribuer au fur et mesure, une approche plus simple est de calculer chaque fois la nouvelle taille du texte en obtenant la taille actuelle et en laugmentant dun certain facteur, par exemple 40 %. Notre code commence par les gestionnaires dvnements $(document).ready() et $('#switcher-large').click() :
$(document).ready(function() { $('#switcher-large').click(function() { }); });

Nous pouvons facilement obtenir la taille du texte laide de la mthode .css() : $('div.speech').css('fontSize'). Nanmoins, puisque la valeur retourne se terminera par "px", nous devons retirer cette partie pour effectuer des calculs avec la valeur. Par ailleurs, lorsquil est prvu dutiliser un objet jQuery plusieurs fois, il est prfrable de mettre en cache le slecteur en plaant lobjet jQuery rsultant dans une variable.
$(document).ready(function() { var $speech = $('div.speech'); $('#switcher-large').click(function() { var num = parseFloat($speech.css('fontSize')); }); });

La premire ligne de $(document).ready() enregistre dans une variable lobjet qui correspond au <div class="speech">. Vous remarquerez lutilisation du symbole $ dans le nom de la variable, $speech. Puisque le caractre $ est accept dans les variables JavaScript, nous lutilisons pour indiquer que la variable contient un objet jQuery. Dans le gestionnaire .click(), nous utilisons parseFloat() pour obtenir uniquement la valeur numrique de la proprit fontSize. Cette fonction examine une chane de

64

jQuery

gauche droite jusqu ce quelle rencontre un caractre non numrique. La suite de chiffres est convertie en nombre virgule flottante (rel). Par exemple, elle convertit la chane "12" dans le nombre 12. Par ailleurs, puisquelle retire les caractres non numriques qui se trouvent la fin de la chane, "12px" devient 12. Si la chane ne commence pas par un caractre numrique, parseFloat() retourne NaN, qui signifie Not a Number ou Non un nombre. Pour terminer, si nous augmentons la taille de 40 %, il ne nous reste plus qu multiplier num par 1.4 et fixer la taille de police en concatnant num et 'px' :
$(document).ready(function() { var $speech = $('div.speech'); $('#switcher-large').click(function() { var num = parseFloat($speech.css('fontSize') ); num *= 1.4; $speech.css('fontSize', num + 'px'); }); });

INFO
Linstruction num *= 1.4 est une version abrge de num = num * 1.4. Nous pouvons utiliser le mme type de raccourci avec les autres oprations : addition, num += 1.4 ; soustraction, num -= 1.4 ; division, num /= 1.4 ; et modulo (reste dune division), num %= 1.4.

Si lutilisateur clique prsent sur le bouton PLUS GRANDE, le texte saffiche avec une taille de police plus grande. Un autre clic et le texte devient encore plus grand (voir Figure 4.2).
Figure 4.2
Le texte aprs deux clics sur le bouton PLUS GRANDE.

Pour que le bouton PLUS PETITE diminue la taille de la police, nous divisons la taille courante par 1.4 au lieu de la multiplier par cette valeur. Mais nous allons runir les deux

Chapitre 4

Effets

65

oprations dans un mme gestionnaire .click() associes tous les lments <button> du <div id="switcher">. Aprs avoir obtenu la valeur numrique de la taille, nous la multiplions ou la divisons en fonction de lidentifiant du bouton sur lequel lutilisateur a cliqu. Voici la nouvelle version du code :
$(document).ready(function() { var $speech = $('div.speech'); $('#switcher button').click(function() { var num = parseFloat( $speech.css('fontSize') ); if (this.id == 'switcher-large') { num *= 1.4; } else if (this.id == 'switcher-small') { num /= 1.4; } $speech.css('fontSize', num + 'px); }); });

Au Chapitre 3, nous avons vu que nous pouvions accder la proprit id de llment du DOM rfrenc par this. Nous retrouvons cela dans les instructions if et else if. Dans cet exemple, il est plus efficace dutiliser this que de crer un objet jQuery uniquement pour tester la valeur dune proprit. Nous avons galement besoin dune solution pour redonner sa valeur initiale la taille de la police. Pour cela, nous pouvons simplement enregistrer cette taille dans une variable ds que le DOM est prt et lutiliser ensuite dans la gestion du clic sur le bouton PAR DFAUT. Pour cette gestion, nous pouvons ajouter une autre instruction else if, mais un switch semble plus appropri :
$(document).ready(function() { var $speech = $('div.speech'); var defaultSize = $speech.css('fontSize'); $('#switcher button').click(function() { var num = parseFloat( $speech.css('fontSize') ); switch (this.id) { case 'switcher-large': num *= 1.4; break; case 'switcher-small': num /= 1.4; break; default: num = parseFloat(defaultSize); } $speech.css('fontSize', num + 'px'); }); });

Nous consultons la valeur de this.id pour changer la taille de la police, mais, si cette valeur nest pas 'switcher-large' ou 'switcher-small', nous choisissons la taille de police initiale.

66

jQuery

4.2

Bases du masquage/affichage

Sans paramtres, les mthodes .hide() et .show() peuvent tre considres comme des mthodes abrges intelligentes pour .css('display','string'), o 'string' correspond la valeur daffichage adquate. Elles ont pour effet de masquer ou dafficher immdiatement les lments slectionns, sans aucune animation. La mthode .hide() affecte display:none lattribut de style des lments du jeu correspondant. Son ct intelligent rside dans le fait quelle mmorise la valeur de la proprit display, en gnral block ou inline, avant de la fixer none. linverse, la mthode .show() replace la proprit display des lments du jeu correspondant la valeur quelle avait avant sa modification none.
INFO
Pour de plus amples informations concernant la proprit display et la reprsentation visuelle de ses valeurs dans une page web, visitez le Mozilla Developer Center ladresse https://developer.mozilla.org/fr/CSS/display/ et tudiez les exemples https://developer. mozilla.org/samples/cssref/display.html.

Cette caractristique de .show() et de .hide() se rvle particulirement utile pour masquer les lments dont la valeur par dfaut de la proprit display est redfinie dans une feuille de style. Par exemple, la proprit display de llment <li> est fixe par dfaut block, mais nous pouvons la modifier inline pour obtenir un menu horizontal. En utilisant la mthode .show() sur cet lment <li> masqu, sa proprit display nest pas rinitialise sa valeur par dfaut, block, ce qui vite quil ne soit remis sur sa propre ligne. la place, llment est restaur dans son tat prcdent, display:inline, prservant ainsi le menu horizontal. Nous pouvons illustrer rapidement ces deux mthodes en ajoutant un second paragraphe et un lien "Pour en savoir plus..." aprs le premier paragraphe du contenu HTML dexemple :
<div id="switcher"> <div class="label">Taille du texte</div> <button id="switcher-default">Par dfaut</button> <button id="switcher-large">Plus grande</button> <button id="switcher-small">Plus petite</button> </div> <div class="speech"> <p>Devant Douaumont, en fvrier, mon tour arriva, et je partis. Je suis la ligne et rpare plusieurs fois jusqu' la ferme de Thiaumont. Les balles de mitrailleuses rasaient le boyau, et j'tais aveugl et couvert de terre par les obus.</p> <p>Le matin, au petit jour, nous recevons l'ordre de poser une nouvelle ligne. Nous voil donc partis quatre ou cinq avec les bobines&nbsp;; seulement, cette fois, il faisait jour et l'ennemi nous tirait dessus avec les mitrailleuses des glacis du fort de Douaumont.</p>

Chapitre 4

Effets

67

<a href="#" class="more">Pour en savoir plus...</a> </div>

Lorsque le DOM est prt, nous masquons le second paragraphe :


$(document).ready(function() { $('p:eq(1)').hide(); });

La page rsultante est illustre la Figure 4.3.


Figure 4.3
Au chargement de la page, le second paragraphe est masqu.

Ensuite, lorsque lutilisateur clique sur POUR EN SAVOIR PLUS... la fin du premier paragraphe, le lien est masqu et le second paragraphe est affich (voir Figure 4.4) :
$(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').show(); $(this).hide(); return false; }); });

Figure 4.4
Un clic sur le lien affiche le second paragraphe.

68

jQuery

Notez le retour de la valeur false. Cela permet dviter que le lien nexcute son action par dfaut. Les mthodes .hide() et .show() sont faciles demploi, mais le rsultat nest pas trs clinquant. Pour que la page soit plus attrayante, nous pouvons faire intervenir une vitesse dexcution.

4.3

Effets et vitesse

Lorsque nous fixons une vitesse, ou plus prcisment une dure, aux mthodes .show() ou .hide(), elles mettent en place une animation qui se produit sur la priode de temps indique. Par exemple, la mthode .hide(vitesse) diminue simultanment la hauteur, la largeur et lopacit dun lment jusqu zro, puis applique ce moment-l la rgle CSS display:none. La mthode .show(vitesse) augmente la hauteur de llment du haut vers le bas, la largeur de la gauche vers la droite et lopacit de 0 1, jusqu ce que son contenu soit totalement visible. Ajouter une vitesse Quel que soit leffet jQuery, nous pouvons prciser lune des trois vitesses prdfinies : 'slow', 'normal' et 'fast'. Avec .show('slow'), lanimation se termine en 0,6 seconde, avec .show('normal'), en 0,4 seconde et, avec .show('fast'), en 0,2 seconde. Pour une meilleure prcision, nous pouvons mme indiquer un nombre de millisecondes, par exemple .show(850). Contrairement aux noms des vitesses prdfinies, les nombres ne sont pas placs entre apostrophes. Ajoutons une vitesse notre exemple pour laffichage du second paragraphe des rcits de la bataille de Verdun :
$(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').show('slow'); $(this).hide(); return false; }); });

La Figure 4.5 montre laspect du paragraphe lorsque la moiti de la dure de leffet, environ, est coule. Fondu enchan Si les mthodes .show() et .hide() animes sont certainement attrayantes, elles peuvent parfois tre trop clinquantes. jQuery propose donc deux autres animations prdfinies, pour un effet plus subtil. Par exemple, pour que lintgralit du paragraphe saffiche en ne changeant que son opacit, nous pouvons utiliser .fadeIn('slow') :

Chapitre 4

Effets

69

Figure 4.5
Laspect du second paragraphe la moiti de lanimation.

$(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').fadeIn('slow'); $(this).hide(); return false; }); });

La Figure 4.6 illustre leffet obtenu. Avec .fadeIn(), les dimensions du bloc du paragraphe sont tout dabord fixes afin que le contenu puisse sy afficher progressivement. Pour diminuer progressivement lopacit, nous utilisons .fadeOut().
Figure 4.6
Afficher le paragraphe en modifiant son opacit.

4.4

Effets composs

Nous avons parfois besoin dinverser la visibilit des lments, au lieu de les afficher une fois comme dans lexemple prcdent. Le basculement peut tre obtenu en examinant tout dabord la visibilit des lments slectionns, puis en invoquant la mthode

70

jQuery

approprie. Nous modifions le script de la manire suivante, en utilisant nouveau les effets de fondu :
$(document).ready(function() { var $firstPara = $('p:eq(1)'); $firstPara.hide(); $('a.more').click(function() { if ($firstPara.is(':hidden')) { $firstPara.fadeIn('slow'); $(this).text('Pour en savoir moins...'); } else { $firstPara.fadeOut('slow'); $(this).text('Pour en savoir plus...'); } return false; }); });

Comme nous lavons fait prcdemment dans ce chapitre, nous plaons en cache notre slecteur de manire viter un nouveau parcours du DOM. Par ailleurs, nous ne masquons plus le lien sur lequel lutilisateur a cliqu. la place, nous modifions son texte. Une instruction if else est une solution parfaitement sense pour inverser la visibilit des lments. Mais, laide des effets composs de jQuery, nous pouvons nous passer des instructions conditionnelles (dans cet exemple, nous avons cependant besoin dune telle instruction pour le texte du lien). jQuery fournit la mthode .toggle(), qui opre comme .show() et .hide() et qui, linstar de ces deux mthodes, peut tre employe avec ou sans un argument de vitesse. Lautre mthode compose se nomme .slideToggle(). Elle affiche ou masque des lments en augmentant ou en diminuant progressivement leur hauteur. Voici une nouvelle version du script fonde sur la mthode .slideToggle() :
$(document).ready(function() { var $firstPara = $('p:eq(1)'); $firstPara.hide(); $('a.more').click(function() { $firstPara.slideToggle('slow'); var $link = $(this); if ( $link.text() == "Pour en savoir plus..." ) { $link.text('Pour en savoir moins...'); } else { $link.text('Pour en savoir plus...'); } return false; }); });

Cette fois-ci, la rptition concernerait $(this). Par consquent, nous lenregistrons dans la variable $link pour amliorer les performances et la lisibilit du code. Par ailleurs, linstruction conditionnelle sert uniquement modifier le lien du texte, elle vrifie le contenu de ce lien la place de la visibilit du second paragraphe.

Chapitre 4

Effets

71

4.5

Crer des animations personnalises

Outre les mthodes pour les effets prdfinis, jQuery met notre disposition une mthode puissante, .animate(), pour crer nos propres animations personnalises. Elle existe en deux variantes. La premire prend quatre arguments : 1. Une mappe de proprits et de valeurs de style, semblable la mappe de .css() mentionne prcdemment dans ce chapitre. 2. Une vitesse facultative, qui peut tre indique laide de lune des chanes prdfinies ou en millisecondes. 3. Un type deasing1 facultatif ; cette option avance sera prsente au Chapitre 10. 4. Une fonction de rappel facultative, sur laquelle nous reviendrons plus loin dans ce chapitre. Voici un exemple dinvocation de cette mthode avec quatre arguments :
.animate({proprit1: 'valeur1', proprit2: 'valeur2'}, vitesse, easing, function() { alert("Lanimation est termine."); } );

La seconde variante prend deux arguments : une mappe de proprits et une mappe doptions :
.animate({proprits}, {options})

En ralit, le second argument regroupe les trois derniers de la premire forme dans une mappe et ajoute deux nouvelles options. En utilisant les sauts de ligne pour faciliter la lecture, la seconde variante prend la forme suivante :
.animate({ proprit1: 'valeur1', proprit2: 'valeur2' }, { duration: 'valeur', easing: 'valeur', complete: function() { alert("Lanimation est termine."); }, queue: boolean, step: callback });

1. N.d.T. : selon Adobe, le terme easing se rapporte lacclration ou la dclration graduelle, durant une animation, qui permet de rendre les animations plus ralistes. Cette technique cre une acclration ou une dclration dapparence plus naturelle en ajustant graduellement le taux de modification.

72

jQuery

Pour le moment, nous utiliserons la premire variante de la mthode .animate(), mais nous passerons la seconde lorsque nous prsenterons les effets squentiels. Inverser leffet de fondu Lorsque nous avons vu les effets composs, avez-vous remarqu quil nexistait pas toujours une mthode pour raliser un effet inverse ? Par exemple, si les mthodes de leffet de glissement ont bien une mthode .slideToggle(), il nexiste en revanche aucune mthode .fadeToggle() pour accompagner .fadeIn() et .fadeOut(). Cela dit, nous pouvons utiliser la mthode .animate() pour crer facilement notre propre animation de fondu invers. Dans lexemple prcdent, nous remplaons .slideToggle() par notre propre animation :
$(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').animate({opacity: 'toggle'}, 'slow'); var $link = $(this); if ( $link.text() == "Pour en savoir plus..." ) { $link.text('Pour en savoir moins...'); } else { $link.text('Pour en savoir plus...'); } return false; }); });

Comme lillustre lexemple, la mthode .animate() accepte des valeurs abrges pour les proprits CSS 'show', 'hide' et 'toggle' de manire simplifier notre travail lorsque les mthodes abrges ne sont pas adaptes une certaine tche. Animer plusieurs proprits Avec la mthode .animate(), nous pouvons affecter simultanment nimporte quelle combinaison de proprits. Par exemple, pour crer un effet de glissement et de fondu sur le second paragraphe, nous pouvons simplement ajouter un couple proprit-valeur pour height dans la mappe des proprits de .animate() :
$(document).ready(function() { $('p:eq(1)').hide(); $('a.more').click(function() { $('p:eq(1)').animate({ opacity: 'toggle', height: 'toggle' }, 'slow'); var $link = $(this); if ( $link.text() == "Pour en savoir plus..." ) { $link.text('Pour en savoir moins...'); } else { $link.text('Pour en savoir plus...');

Chapitre 4

Effets

73

} return false; }); });

De plus, nous pouvons non seulement utiliser les proprits de style pour les effets, mais galement dautres proprits comme left, top, fontSize, margin, padding et borderWidth. Revenons au script qui modifie la taille du texte des paragraphes et animons laugmentation ou la diminution de la taille en substituant simplement la mthode .animate() la mthode .css() :
$(document).ready(function() { var $speech = $('div.speech'); var defaultSize = $speech.css('fontSize'); $('#switcher button').click(function() { var num = parseFloat( $speech.css('fontSize') ); switch (this.id) { case 'switcher-large': num *= 1.4; break; case 'switcher-small': num /= 1.4; break; default: num = parseFloat(defaultSize); } $speech.animate({fontSize: num + 'px'}, 'slow'); }); });

Les proprits supplmentaires nous permettent galement de crer des effets beaucoup plus complexes. Par exemple, nous pouvons dplacer un lment du ct gauche de la page vers le ct droit, tout en augmentant sa hauteur de 20 pixels et en modifiant sa bordure 5 pixels. Appliquons cet effet au rectangle <div id="switcher">. La Figure 4.7 prsente son aspect avant que nous ne lanimions.
Figure 4.7
Le slecteur de taille du texte avant son animation.

74

jQuery

La largeur de la page ntant pas fige, nous devons calculer la distance du dplacement du rectangle avant de pouvoir laligner sur le bord droit de la page. Si lon suppose que la largeur du paragraphe est de 100 %, nous pouvons soustraire la largeur du rectangle TAILLE DU TEXTE celle du paragraphe. La mthode .width() de jQuery est souvent pratique pour de tels calculs, mais elle ne tient pas compte des espacements gauche et droit ni de lpaisseur des bordures. Depuis jQuery version 1.2.6, nous disposons toutefois de la mthode .outerWidth(), qui tient compte de ces caractristiques dans la dtermination de la largeur. Nous lutilisons donc dans notre exemple. Lanimation est dclenche en cliquant sur le libell TAILLE DU TEXTE, situ juste au-dessus des boutons. Voici le code correspondant :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher.animate({left: paraWidth - switcherWidth, height: '+=20px', borderWidth: '5px'}, 'slow'); }); });

Vous remarquerez que la valeur de la proprit height est prcde de +=. Cette expression, introduite dans jQuery 1.2, indique une valeur relative. Par consquent, au lieu danimer la hauteur jusqu 20 pixels, elle est anime jusqu la hauteur courante plus 20 pixels. Ce code augmente bien la hauteur du <div> et paissit ses bordures, mais, pour le moment, son ancrage gauche ne peut pas tre modifi. Nous devons autoriser le changement de sa position dans la feuille de style CSS. Positionnement avec CSS Lorsquon utilise .animate(), il ne faut surtout pas oublier les limites imposes par CSS sur les lments qui sont anims. Par exemple, la modification de la proprit left naura aucun effet sur les lments si leur positionnement CSS nest pas fix relative ou absolute. Pour tous les lments de type bloc, le positionnement CSS par dfaut est static. Il indique prcisment que les lments resteront leur place si nous tentons de les dplacer sans modifier auparavant la valeur de leur proprit position.
INFO
Pour de plus amples informations concernant le positionnement absolu et relatif, consultez larticle Absolutely Relative de Joe Gillespie (http://www.wpdfd.com/issues/78/absolutely_ relative/).

Chapitre 4

Effets

75

Nous modifions la feuille de style pour que le positionnement de <div id="switcher"> soit relatif :
#switcher { position: relative; }

prsent, en cliquant sur le libell TAILLE DU TEXTE, lanimation se termine dans ltat illustr la Figure 4.8.
Figure 4.8
Le slecteur de taille du texte lorsque lanimation est termine.

4.6

Effets simultans ou squentiels

Nous venons de le voir, la mthode .animate() est trs pratique pour appliquer simultanment des effets sur un jeu dlments. Cependant, nous souhaitons parfois placer les effets dans une file afin quils produisent squentiellement. Manipuler un seul jeu dlments Lorsque plusieurs effets sont appliqus au mme jeu dlments, il est trs facile de les mettre la suite par simple chanage. Pour illustrer cette mthode, nous allons nouveau dplacer le rectangle TAILLE DU TEXTE vers la droite, en augmentant sa hauteur et lpaisseur de sa bordure. Cependant, cette fois-ci, nous raliserons les trois effets squentiellement, en plaant chacun deux dans sa propre mthode .animate() et en les chanant :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .animate({left: paraWidth - switcherWidth}, 'slow') .animate({height: '+=20px'}, 'slow')

76

jQuery

.animate({borderWidth: '5px'}, 'slow'); }); });

Nous pouvons videmment mettre les trois mthodes .animate() sur la mme ligne, mais, pour des questions de facilit de lecture, nous les avons places chacune sur leur propre ligne et les avons indentes. Toutes les mthodes deffets jQuery, pas seulement la mthode .animate(), peuvent ainsi tre mises la suite par chanage. Par exemple, nous pouvons appliquer la squence deffets suivante sur <div id="switcher"> : 1. Diminuer son opacit 0,5 avec .fadeTo(). 2. Le dplacer vers la droite avec .animate(). 3. Augmenter son opacit 1 avec .fadeTo(). 4. Le masquer avec .slideUp(). 5. Lafficher nouveau avec .slideDown(). Pour cela, il suffit que le code enchane ces effets dans cet ordre :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({ 'left': paraWidth - switcherWidth }, 'slow') .fadeTo('slow',1.0) .slideUp('slow') .slideDown('slow'); }); });

Toutefois, comment pouvons-nous simultanment dplacer le <div> vers la droite et diminuer son opacit de moiti ? Si les deux animations se produisent la mme vitesse, il suffit de les combiner dans une mthode .animate(). Cependant, dans cet exemple, le fondu est rapide (vitesse 'fast') tandis que le dplacement vers la droite est lent (vitesse 'slow'). Cest dans de telles circonstances que la seconde variante de la mthode .animate() simpose :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({'left': paraWidth - switcherWidth}, {duration: 'slow', queue: false})

Chapitre 4

Effets

77

.fadeTo('slow',1.0) .slideUp('slow') .slideDown('slow'); }); });

Le deuxime argument, une mappe doptions, prcise loption queue, qui, lorsquelle vaut false, fait que lanimation dmarre simultanment la prcdente. Dans une suite deffets appliqus un mme jeu dlments, il faut savoir que le squencement ne sapplique pas automatiquement aux mthodes qui ne mettent pas en uvre des effets, comme .css(). Supposons que nous voulions changer la couleur darrire-plan de <div id="switcher"> rouge aprs .slideUp() mais avant slideDown(). Nous essayons donc le code suivant :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({'left': paraWidth - switcherWidth}, 'slow') .fadeTo('slow',1.0) .slideUp('slow') .css('backgroundColor','#f00') .slideDown('slow'); }); });

Mais, bien que la modification de larrire-plan se trouve la place dans la chane, elle a lieu juste aprs le clic sur le libell. Pour ajouter de telles mthodes dans la squence, une solution consiste employer la mthode nomme fort propos .queue(). Voici comment lutiliser dans notre script :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('fast',0.5) .animate({'left': paraWidth - switcherWidth}, 'slow') .fadeTo('slow',1.0) .slideUp('slow') .queue(function() { $switcher .css('backgroundColor', '#f00') .dequeue(); }) .slideDown('slow'); }); });

78

jQuery

Lorsquune fonction de rappel est passe la mthode .queue(), comme cest le cas ici, elle ajoute cette fonction la file des effets associe aux lments correspondants. Dans cette fonction, nous fixons la couleur de larrire-plan rouge, puis invoquons la mthode .dequeue(). Grce cet appel, la squence danimation reprend l o elle a t dtourne et termine la chane avec la ligne .slideDown('slow'). Sans linvocation de .dequeue(), lanimation se serait arrte.
INFO
Pour de plus amples informations concernant les mthodes .queue() et .dequeue(), ainsi que des exemples dutilisation, consultez la page http://docs.jquery.com/Effects.

Nous verrons une autre manire de placer dans la file des mthodes non lies aux effets lorsque nous examinerons lapplication des effets plusieurs jeux dlments. Manipuler plusieurs jeux dlments Lorsque les effets sont appliqus des jeux dlments diffrents, ils se produisent virtuellement au mme moment. Pour le constater, nous allons faire glisser un paragraphe vers le bas tout en en faisant glisser un autre vers le haut. Tout dabord, nous ajoutons dautres paragraphes au fichier HTML des rcits de la bataille de Verdun :
<div id="switcher"> <div class="label">Taille du texte</div> <button id="switcher-default">Par dfaut</button> <button id="switcher-large">Plus grande</button> <button id="switcher-small">Plus petite</button> </div> <div class="speech"> <p>Devant Douaumont, en fvrier, mon tour arriva, et je partis. Je suis la ligne et rpare plusieurs fois jusqu' la ferme de Thiaumont. Les balles de mitrailleuses rasaient le boyau, et j'tais aveugl et couvert de terre par les obus.</p> <p>Le matin, au petit jour, nous recevons l'ordre de poser une nouvelle ligne. Nous voil donc partis quatre ou cinq avec les bobines&nbsp;; seulement, cette fois, il faisait jour et l'ennemi nous tirait dessus avec les mitrailleuses des glacis du fort de Douaumont.</p> <a href="#" class="more">Pour en savoir plus...</a> <p>Sur le coup de midi, je reois l'ordre de poser une nouvelle ligne de Thiamont Fleury. Nous partons deux, chacun avec une bobine de fil. </p> <p>Mon camarade droule, moi je suis en attachant le fil tout ce que je peux trouver comme support. A un moment donn, j'ai beau chercher, je ne trouve rien comme support, lorsque j'aperois plusieurs corps tendus&nbsp;; je me prcipite et un tour de fil dans les jambes de l'un, un tour dans celles des autres, ma ligne se trouve fixe dans la plaine. </p> </div>

Chapitre 4

Effets

79

Ensuite, pour que nous puissions voir ce qui se produit au cours de lanimation, nous affectons au troisime paragraphe une bordure de 1 pixel et au quatrime, un fond gris. Nous masquons galement ce dernier lorsque le DOM est prt :
$(document).ready(function() { $('p:eq(2)').css('border', '1px solid #333'); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); });

Enfin, nous invoquons la mthode .click() sur le troisime paragraphe afin quun clic le fasse glisser vers le haut (pour le faire disparatre) et fasse glisser le quatrime vers le bas (pour le faire apparatre) :
$(document).ready(function() { $('p:eq(2)') .css('border', '1px solid #333') .click(function() { $(this).slideUp('slow').next().slideDown('slow'); }); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); });

La Figure 4.9 illustre ces deux effets mi-chemin et confirme quils se produisent quasi simultanment.
Figure 4.9
Effets simultans sur des lments diffrents.

Le troisime paragraphe, qui tait initialement visible, est prsent moiti masqu, tandis que le quatrime, qui tait initialement masqu, est prsent moiti affich. Fonctions de rappel Pour pouvoir enchaner des effets sur des lments diffrents, jQuery permet de passer une fonction de rappel chaque mthode deffet. Nous lavons vu avec les gestionnaires dvnements et la mthode .queue(), les fonctions de rappel ne sont rien dautre que des fonctions passes en argument des mthodes. Dans le contexte des effets, elles apparaissent en dernier argument de la mthode.

80

jQuery

En utilisant une fonction de rappel pour mettre deux effets de glissement la suite, nous pouvons faire en sorte que lanimation du quatrime paragraphe se termine avant celle du troisime. Commenons par prsenter lutilisation dune fonction de rappel avec la mthode .slideDown() :
$(document).ready(function() { $('p:eq(2)') .css('border', '1px solid #333') .click(function() { $(this).next().slideDown('slow',function() { // Ce code est excut aprs que leffet de glissement // vers le bas du quatrime paragraphe est termin. }); }); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); });

Toutefois, nous devons faire attention la cible du glissement vers le haut. Le contexte de $(this) a chang car la fonction de rappel se trouve dans la mthode .slideDown(). Par consquent, $(this) ne correspond plus au troisime paragraphe comme cest le cas dans la mthode .click(). Puisque la mthode .slideDown() est invoque sur $(this).next(), son code voit en $(this) le frre suivant, cest--dire le quatrime paragraphe. Par consquent, si nous plaons $(this).slideUp('slow') dans la fonction de rappel, nous masquons en ralit le paragraphe que nous venons de faire apparatre. Pour que les rfrences $(this) restent cohrentes, il suffit denregistrer $(this) dans une variable ds le dbut de la mthode .click() : var $thirdPara = $(this). La variable $thirdPara fait alors rfrence au troisime paragraphe, que ce soit dans ou lextrieur de la fonction de rappel. Voici le nouveau code qui utilise cette variable :
$(document).ready(function() { var $thirdPara = $('p:eq(2)'); $thirdPara .css('border', '1px solid #333') .click(function() { $(this).next().slideDown('slow',function() { $thirdPara.slideUp('slow'); }); }); $('p:eq(3)').css('backgroundColor', '#ccc').hide(); });

Lutilisation de $thirdPara dans la fonction de rappel .slideDown() est en rapport avec les proprits des fermetures. lAnnexe C, nous reviendrons sur ce sujet important mais difficile matriser. La Figure 4.10 illustre les effets mi-parcours. Cette fois-ci, les troisime et quatrime paragraphes sont visibles ; le glissement vers le bas du quatrime est termin, tandis que le glissement vers le haut du troisime va commencer.

Chapitre 4

Effets

81

Figure 4.10
Lanimation du troisime paragraphe dbute lorsque celle du quatrime est termine.

Puisque nous connaissons prsent les fonctions de rappel, revenons au code qui modifiait la couleur darrire-plan vers la fin dune suite deffets. Nous pouvons remplacer la mthode .queue() utilise prcdemment par une fonction de rappel :
$(document).ready(function() { $('div.label').click(function() { var paraWidth = $('div.speech p').outerWidth(); var $switcher = $(this).parent(); var switcherWidth = $switcher.outerWidth(); $switcher .fadeTo('slow',0.5) .animate({'left': paraWidth - switcherWidth}, 'slow') .fadeTo('slow',1.0) .slideUp('slow', function() { $switcher .css('backgroundColor', '#f00'); }) .slideDown('slow'); }); });

Dans cette version galement, la couleur darrire-plan de <div id="switcher"> passe au rouge aprs le glissement vers le haut et avant le glissement vers le bas. En bref Avec toutes ces possibilits de mise en application des effets, il peut tre difficile de dterminer sils se produiront simultanment ou squentiellement. Un bref rcapitulatif facilitera la rflexion : 1. Les effets appliqus un mme jeu dlments sont :
n

simultans lorsquils concernent plusieurs proprits dans une mme mthode .animate() ; squentiels lorsquils sont mis en uvre par un enchanement de mthodes, sauf si loption queue est fixe false.

82

jQuery

2. Les effets appliqus plusieurs jeux dlments sont :


n n

simultans par dfaut ; squentiels lorsquils sont mis en uvre dans la fonction de rappel dun autre effet ou dans celle de la mthode .queue().

4.7

En rsum

En utilisant les mthodes deffet tudies dans ce chapitre, nous sommes prsent capables daugmenter et de diminuer progressivement la taille dun texte, que ce soit avec .css() ou .animate(). Nous connaissons galement diffrentes manires dappliquer diffrents effets pour masquer et afficher progressivement des lments de la page, ainsi que pour animer des lments, simultanment ou squentiellement. Dans les quatre premiers chapitres de cet ouvrage, tous les exemples manipulaient des lments dfinis dans le fichier HTML de la page. Au Chapitre 5, nous prsenterons plusieurs faons dutiliser jQuery pour crer de nouveaux lments et les insrer o bon nous semble dans le DOM.

5
Manipulation du DOM
Au sommaire de ce chapitre
U U U U U U U

Manipuler des attributs Insrer de nouveaux lments Dplacer des lments Envelopper des lments Copier des lments Les mthodes du DOM en bref En rsum

Tel le magicien qui fait sortir un bouquet de fleurs de nulle part, jQuery permet de crer des lments, des attributs et du texte dans une page, comme par magie. Mais ce nest pas tout. Avec jQuery, nous pouvons galement les faire disparatre. Nous pouvons mme transformer le bouquet de fleurs en colombe.

5.1

Manipuler des attributs

Tout au long des quatre premiers chapitres, nous avons employ les mthodes .addClass() et .removeClass() pour illustrer la modification de laspect des lments dune page. Ces deux mthodes manipulent lattribut class ou, dans le jargon DOM, la proprit className. La mthode .addClass() cre lattribut ou lui ajoute une classe, tandis que .removeClass() le supprime ou lui retire une classe. Par ailleurs, avec la mthode .toggleClass(), qui alterne entre lajout et la suppression dune classe, nous disposons dun mcanisme efficace et robuste pour la gestion des classes. Toutefois, lattribut class nest pas le seul que nous puissions manipuler. Pour examiner ou modifier les autres attributs, comme id, rel ou href, jQuery fournit les mthodes .attr() et .removeAttr(). Elles peuvent mme servir pour lattribut class, mais

84

jQuery

les mthodes spcialises .addClass() et .removeClass() doivent tre prfres car elles prennent en charge les lments ayant plusieurs classes, comme <div class="un deux">. Attributs autres que class Sans laide de jQuery, la manipulation de certains attributs nest pas aise. Par ailleurs, jQuery nous permet de modifier plusieurs attributs la fois, de la mme manire que nous avons manipul plusieurs proprits CSS laide de la mthode .css() au Chapitre 4. Par exemple, nous pouvons trs facilement fixer la valeur des attributs id, rel et title des liens, tous la fois. Partons du balisage HTML suivant :
<h1 id="f-title">Flatland : une aventure plusieurs dimensions</h1> <div id="f-author">par Edwin A. Abbott</div> <h2>Partie 1, Section 3</h2> <h3 id="f-subtitle"> Des habitants de Flatland </h3> <div id="excerpt">un extrait</div> <div class="chapter"> <p class="square">Les Membres des Professions Librales et les Gentilshommes sont des Carrs (c'est cette classe que j'appartiens personnellement) et des Figures Cinq-Cts ou <a href="http://fr.wikipedia.org/wiki/Pentagone_(figure)">Pentagones</a>. </p> <p class="nobility hexagon">Vient ensuite la Noblesse, qui comporte plusieurs degrs, en commenant par les Figures Six-Cts, ou <a href="http://fr.wikipedia.org/wiki/Hexagone">Hexagones</a>, et ainsi de suite, le nombre des cts s'levant sans cesse, jusqu'aux Personnages qui reoivent le titre honorable de Polygones. Enfin, lorsque le nombre des cts devient si grand, et que les cts eux-mmes sont si petits qu'il est impossible de distinguer la Figure d'un <a href="http://fr.wikipedia.org/wiki/Cercle">Cercle</a>, elle entre dans la classe Circulaire ou Ecclsiastique : c'est l'ordre le plus lev de tous. </p> <p><span class="pull-quote"><span class="drop">Chez nous, </span>une Loi de la Nature veut qu'un enfant mle ait toujours <strong>un ct de plus </strong> que son pre</span>, de sorte que chaque gnration s'lve (en rgle gnrale) d'un chelon sur la voie du progrs et de l'anoblissement. Ainsi le fils d'un Carr sera un Pentagone ; le fils du Pentagone, un Hexagone ; etc. </p> <!-- . . . le code continue . . . --> </div>

Nous pouvons itrer sur les liens contenus dans <div class="chapter"> et leur appliquer les attributs un par un. Si nous voulons donner une mme valeur dattribut tous les liens, nous pouvons crire une seule ligne de code dans le gestionnaire $(document).ready() :

Chapitre 5

Manipulation du DOM

85

$(document).ready(function() { $('div.chapter a').attr({'rel': 'external'}); });

Cette solution fonctionne car nous souhaitons que le nouvel attribut rel ait la mme valeur pour chaque lien. Toutefois, les attributs que nous ajoutons ou modifions doivent souvent avoir des valeurs diffrentes selon llment. Par exemple, dans un document donn, chaque id dlment doit tre unique pour que le code JavaScript ait un comportement prvisible. Pour affecter un id chaque lien, nous abandonnons la ligne prcdente et optons pour la mthode .each() de jQuery :
$(document).ready(function() { $('div.chapter a').each(function(index) { $(this).attr({ 'rel': 'external', 'id': 'wikilink-' + index }); }); });

La mthode .each(), qui opre comme un itrateur explicite, est une version plus pratique de la boucle for. Elle peut tre utilise lorsque le code de traitement de chaque lment de la collection obtenue laide du slecteur est trop complexe pour litration implicite. Dans notre exemple, la fonction anonyme de la mthode .each() reoit un indice, qui est ajout chaque identifiant. Largument index joue le rle de compteur, qui commence zro pour le premier lien et augmente de un pour chaque lien successif. Ainsi, laffectation de la valeur 'wikilink-' + index lattribut id donne au premier lien lidentifiant wikilink-0, au second, lidentifiant wikilink-1, etc.
INFO
En ralit, nous aurions pu nous contenter de litration implicite dans ce cas, car la mthode .attr() accepte une fonction en second argument, de manire semblable la mthode .filter() rencontre au Chapitre 2 (pour de plus amples informations, consultez la page http://docs.jquery.com/Attributes/attr#keyfn). Cependant, lutilisation de .each() semble mieux correspondre nos besoins.

Nous utilisons lattribut title pour inviter linternaute consulter larticle Wikipdia qui correspond au terme du lien. Dans notre exemple, le slecteur est simple car tous les liens pointent vers Wikipdia. Toutefois, il est sans doute prfrable que lexpression de slection soit un peu plus prcise de manire ne retenir que les liens dont lattribut href contient wikipedia, simplement pour le cas o nous dciderions dajouter ultrieurement un lien vers un autre site que Wikipdia :
$(document).ready(function() { $('div.chapter a[href*=wikipedia]').each(function(index) { var $thisLink = $(this);

86

jQuery

$thisLink.attr({ 'rel': 'external', 'id': 'wikilink-' + index, 'title': 'en savoir plus sur ' + $thisLink.text() + ' sur Wikipdia' }); }); });

Vous remarquerez que nous enregistrons $(this) dans la variable $thisLink, car cette rfrence est utilise plusieurs fois. Aprs que les valeurs des trois attributs ont t fixes, le balisage HTML du premier lien est devenu le suivant :
<a href="http://fr.wikipedia.org/wiki/Pentagone_(figure)" rel="external" id="wikilink-0" title="en savoir plus sur Pentagones sur Wikipdia">Pentagones</a>

La fonction $() revisite Depuis le dbut de ce livre, la fonction $() nous sert accder aux lments dun document. En un sens, elle constitue le cur de la bibliothque jQuery, puisque nous lutilisons chaque fois que nous voulons associer un effet, un vnement ou une proprit une collection dlments correspondants. Mais la fonction $() cache un autre secret entre ses parenthses une fonctionnalit si puissante quelle peut non seulement modifier lapparence visuelle dune page, mais galement son contenu rel. En plaant simplement un morceau de code HTML entre les parenthses, nous pouvons crer une structure DOM totalement nouvelle. Rappel concernant laccessibilit
Vous ne devez pas oublier les risques quil y a proposer certaines fonctionnalits, certaines amliorations visuelles et certaines informations textuelles uniquement aux internautes dont les navigateurs web prennent en charge JavaScript. Les informations importantes doivent tre accessibles tous, pas uniquement aux utilisateurs qui disposent du logiciel compatible.

Les pages de FAQ proposent souvent un lien de retour au dbut du document aprs chaque question-rponse. Ces liens nont, notre avis, aucune existence smantique et peuvent donc tre ajouts laide dun code JavaScript, en tant quamlioration propose aux visiteurs. Dans notre exemple, nous allons ajouter un lien de retour vers le dbut de la page aprs chaque paragraphe, ainsi que lancre cible par ces liens. Tout dabord, nous crons simplement les nouveaux lments :
$(document).ready(function() { $('<a href="#top">retour au dbut</a>'); $('<a id="top"></a>'); });

Chapitre 5

Manipulation du DOM

87

La Figure 5.1 illustre laspect de la page ce stade. O sont donc les liens de retour vers le dbut et lancre ? Ne devraient-ils pas apparatre ? La rponse est non. En effet, si les deux lignes crent bien les lments, ceux-ci ne sont pas encore ajouts la page. Pour cela, nous devons employer lune des nombreuses mthodes dinsertion de jQuery.

Figure 5.1
Laspect initial de la page.

5.2

Insrer de nouveaux lments

jQuery propose deux mthodes pour insrer des lments avant dautres lments : .insertBefore() et .before(). Elles ont la mme fonction, leur diffrence rsidant dans la manire de les chaner dautres mthodes. Deux autres mthodes, .insertAfter() et .after(), jouent des rles quivalents, mais, comme leur nom le suggre, elles insrent des lments aprs dautres lments. Pour ajouter les liens de retour au dbut, nous invoquons la mthode .insertAfter() :
$(document).ready(function() { $('<a href="#top">retour au dbut</a>').insertAfter('div.chapter p'); $('<a id="top"></a>'); });

88

jQuery

La mthode .after() produirait le mme rsultat que .insertAfter(), mais lexpression de slection doit tre indique avant la mthode, non aprs. Avec .after(), la premire ligne qui suit $(document).ready() devient :
$('div.chapter p').after('<a href="#top">retour au dbut</a>');

Avec .insertAfter(), nous pouvons continuer manipuler llment <a> cr en chanant simplement des mthodes supplmentaires. Avec .after(), ces mthodes opreraient la place sur les lments obtenus laide du slecteur $('div.chapter p'). prsent, puisque nous insrons les liens dans la page (et dans le DOM) aprs chaque paragraphe qui apparat dans <div class="chapter">, les liens de retour au dbut sont affichs (voir Figure 5.2).

Figure 5.2
Les liens de retour au dbut du document sont insrs.

Malheureusement, les liens ne sont pas encore oprationnels, car il manque lancre id="top". Pour lajouter, nous pouvons nous servir de lune des mthodes qui insrent des lments dans dautres lments :

Chapitre 5

Manipulation du DOM

89

$(document).ready(function() { $('<a href="#top">retour au dbut</a>').insertAfter('div.chapter p'); $('<a id="top" name="top"></a>').prependTo('body'); });

Le code ajout insre lancre juste au dbut de llment <body>, cest--dire au dbut de la page. prsent, puisque les liens ont t ajouts laide de la mthode .insertAfter(), comme lancre avec la mthode .prependTo(), nous disposons dun ensemble de liens totalement oprationnels pour revenir au dbut de la page. Toutefois, il est inutile dafficher des liens de retour au dbut lorsque le dbut de la page est visible. Une rapide amlioration du script permet dajouter les liens uniquement aprs le quatrime paragraphe, par exemple. Pour cela, il suffit simplement de modifier lexpression de slection : .insertAfter('div.chapter p:gt(2)'). Pourquoi 2 ? Il ne faut pas oublier que les indices JavaScript commencent zro. Par consquent, le premier paragraphe possde lindice 0, le deuxime, lindice 1, le troisime, lindice 2, et le quatrime, lindice 3. Notre expression de slection dbute linsertion des liens aprs chaque paragraphe ds que lindice est suprieur 2, cest--dire 3. La Figure 5.3 (page suivante) illustre le changement apport par cette nouvelle expression de slection.

5.3

Dplacer des lments

Avec les liens de retour au dbut, nous avons cr de nouveaux lments et les avons ajouts dans la page. Mais nous pouvons galement prendre des lments de la page et les dplacer un autre endroit. Cette possibilit peut tre utile pour mettre en forme dynamiquement des notes de bas de page. Une telle note apparat dj dans le texte original de Flatland utilis dans notre exemple, mais, pour illustrer notre propos, nous allons considrer dautres parties du texte comme des notes de bas de page :
<p>Il est bien rare &mdash; en proportion du trs grand nombre de naissances Isocles &mdash; qu'un Triangle quilatral authentique et certifiable naisse de parents Isocles. <span class="footnote"> Quel besoin a-t-on d'un certificat ? demandera peut-tre un critique de Spaceland. La procration d'un Fils Carr n'est-elle pas un certificat de la Nature elle-mme et ne prouve-t-elle pas l'quilatralit du Pre ? Je rpondrai qu'aucune Dame de condition n'accepterait d'pouser un Triangle non certifi. On a vu parfois des Triangles lgrement irrguliers donner naissance des rejetons Carrs ; mais, dans presque tous les cas, l'Irrgularit de la premire gnration rapparat dans la troisime qui, soit ne parvient pas atteindre le rang de Pentagone, soit retombe dans le Triangulaire.</span> Pour arriver ce rsultat, toute une srie de mariages mixtes calculs avec soin est d'abord ncessaire ; encore faut-il que ceux qui aspirent devenir les anctres du futur quilatral s'exercent pendant un laps de temps prolong la frugalit, la matrise de soi, et qu' travers des gnrations successives s'opre un dveloppement patient, systmatique et continu de l'intellect Isocle. </p>

90

jQuery

Figure 5.3
Les liens de retour vers le dbut napparaissent quaprs le quatrime paragraphe. <p><span class="pull-quote">Dans notre pays, quand un Vrai Triangle quilatral nat de parents Isocles, c'est un vnement dont on se rjouit<span class="drop"> plusieurs lieues la ronde.</span></span> Aprs un svre examen effectu par le Conseil Sanitaire et Social, l'enfant, s'il est certifi Rgulier, est admis au cours d'une crmonie solennelle dans la classe des quilatraux. Il est immdiatement enlev ses parents, qui se sentent partags entre l'orgueil et l'affliction, et adopt par quelque quilatral sans descendance. <span class="footnote">Celui-ci s'engage par serment ne plus jamais laisser l'enfant pntrer dans son ancien domicile ou mme jeter les yeux sur un membre de sa famille, de crainte que l'organisme dont le dveloppement est si rcent ne retombe, sous l'effet d'une imitation inconsciente, jusqu' son niveau hrditaire.</span> </p> <p>Combien elle est admirable, cette Loi de la Compensation ! <span class="footnote">Et comme elle prouve merveille le bien-fond, le caractre conforme la nature, et j'irais presque jusqu' dire les origines divines de la constitution aristocratique qui rgit les tats de Flatland ! </span> En utilisant judicieusement cette Loi naturelle, les Polygones et les Cercles sont presque toujours en mesure d'touffer la sdition au

Chapitre 5

Manipulation du DOM

91

berceau : il leur suffit pour cela de mettre profit les rserves d'espoir irrpressibles et illimites que recle l'esprit humain.&hellip; </p>

Chacun de ces trois paragraphes inclut une note de bas de page balise par <span class="footnote"></span>. Grce ce balisage HTML, nous pouvons conserver le contexte de la note. Dans la feuille de style, une rgle CSS applique une police italique aux notes de bas de page (voir Figure 5.4).

Figure 5.4
Les trois nouveaux paragraphes comprennent des notes de bas de page affiches en italique.

Nous pouvons extraire les notes de bas de page et les dplacer entre les lments <div class="chapter"> et <div id="footer">. Noubliez pas que, mme en utilisant litration implicite, lordre des insertions est prdfini et commence au sommet de larborescence du DOM, pour se poursuivre vers le bas. Puisquil est important de conserver lordre des notes de bas de page leur nouvel emplacement, nous devons utiliser .insertBefore('#footer').

92

jQuery

Chaque note de bas de page est remise juste avant <div id="footer">, de manire que la premire se trouve entre <div class="chapter"> et <div id="footer">, la deuxime, entre la premire et <div id="footer">, etc. Si nous utilisions la place .insertAfter('div.chapter'), les notes de bas de page apparatraient dans lordre inverse. Voici donc notre code :
$(document).ready(function() { $('span.footnote').insertBefore('#footer'); });

Nous faisons cependant face un problme important. Les notes apparaissent dans des balises <span> et sont donc par dfaut affiches en ligne, cest--dire lune aprs lautre, sans aucune sparation (voir Figure 5.5).

Figure 5.5
Par dfaut, les notes de bas de page sont affiches en ligne.

Une solution ce problme consiste modifier la feuille de style CSS pour que les lments <span> soient affichs comme des blocs, mais uniquement sils ne se trouvent pas dans <div class="chapter"> :
span.footnote { font-style: italic; font-family: "Times New Roman", Times, serif; display: block; margin: 1em 0; } .chapter span.footnote { display: inline; }

La Figure 5.6 prsente un affichage plus correct des notes de bas de page. Les notes de bas de page sont prsent spares lune de lautre, mais nous nen avons pas termin. Voici une procdure qui permet dobtenir une meilleure prsentation des notes de bas de page :

Chapitre 5

Manipulation du DOM

93

Figure 5.6
Les notes de bas de page sont affiches comme des blocs.

1. Marquer dans le texte lemplacement de la rfrence de la note. 2. Numroter chaque rfrence et ajouter le numro correspondant au dbut de la note elle-mme. 3. Crer un lien entre la rfrence dans le texte et la note de bas de page correspondante, ainsi quentre la note de bas de page et lemplacement de sa rfrence (pour revenir au texte). Toutes ces tapes peuvent tre accomplies partir dune mthode .each(), mais nous devons commencer par dfinir un lment conteneur pour les notes en bas de la page :
$(document).ready(function() { $('<ol id="notes"></ol>').insertAfter('div.chapter'); });

Puisque les notes de bas de page doivent tre numrotes, nous utilisons une liste ordonne <ol id="notes"></ol>, qui gnrera automatiquement la numrotation notre place. Nous donnons lidentifiant notes la liste et linsrons aprs <div class="chapter">. Marquer, numroter et lier au contexte prsent, nous sommes prts marquer et numroter les emplacements do ont t extraites les notes :
$(document).ready(function() { $('<ol id="notes"></ol>').insertAfter('div.chapter'); $('span.footnote').each(function(index) { $(this) .before( ['<a href="#foot-note-',

94

jQuery

index+1, '" id="context-', index+1, '" class="context">', '<sup>' + (index+1) + '</sup>', '</a>' ].join('') ) }); });

Nous partons du slecteur que nous avons utilis avec lexemple plus simple de notes de bas de page, mais nous lui chanons la mthode .each(). Dans la mthode .each(), nous commenons par $(this), qui reprsente chaque note de bas de page successive et lui chanons la mthode .before(). Aprs leur concatnation, les lments du tableau qui se trouve entre les parenthses de la mthode .before() gnrent un lien en exposant qui sera insr avant chaque lment <span> de la note de bas de page. Voici par exemple le premier lien tel quil sera ajout au DOM :
<a href="#foot-note-1" id="context-1" class="context"><sup>1</sup></a>

Au premier abord, la syntaxe pourrait ne pas vous paratre familire. Voyons donc de manire plus prcise ce qui se passe. Entre les parenthses de la mthode .before(), nous plaons tout dabord des crochets ([]) qui reprsentent un tableau littral. Chaque lment du tableau est suivi dune virgule, lexception, et cest important, du dernier. Nous avons plac chaque lment sur sa propre ligne afin de faciliter la lecture. Ensuite, aprs avoir construit le tableau, nous le convertissons en une chane de caractres laide de la mthode JavaScript .join(). Son argument est une chane vide, reprsente par les deux apostrophes, car rien ne doit apparatre entre chaque lment du tableau lorsquils sont concatns. Notez lutilisation de index+1. Puisque le comptage commence zro, nous ajoutons 1 pour que les attributs href commencent #foot-note-1, les attributs id #context-1 et le texte du lien 1. La valeur de lattribut href est particulirement importante, car elle doit correspondre exactement celle de lattribut id de la note de bas de page (sans le caractre #). Nous pouvons obtenir le mme rsultat laide dune longue chane concatne au lieu dinvoquer .join() sur un tableau :
.before('<a href="#foot-note-' + (index+1) + '" id="context-' + (index+1) + '" class="context"><sup>' + (index+1) + '</sup></a>');

Toutefois, lutilisation du tableau nous parat donner un code plus facile grer.

Chapitre 5

Manipulation du DOM

95

INFO
Sur le Web, vous trouverez de nombreux dbats concernant les diffrences de performances entre linvocation de .join() sur un tableau et la concatnation des chanes. Pour les plus curieux, larticle disponible ladresse http://www.sitepen.com/blog/2008/05/09/stringperformance-an-analysis/ tudie les performances des deux techniques en se fondant sur plusieurs tests. Toutefois, dans la plupart des situations, les diffrences ne sont pas perceptibles. Si les performances dun script posent problme, il existe bien dautres facteurs dont limpact est beaucoup plus important (comme la mise en cache des slecteurs).

La Figure 5.7 montre les trois marqueurs des notes de bas de page lies.

Les trois marqueurs des notes de bas de page.

Figure 5.7
Les rfrences des notes sont insres dans le texte sous forme de liens.

Dplacer les notes de bas de page Ltape suivante consiste dplacer les lments <span class="footnote">, comme nous lavons fait dans lexemple simple. Cependant, nous les dposerons cette fois-ci

96

jQuery

dans le nouvel lment <ol id="notes"> cr. Nous utilisons .appendTo() de manire conserver lordre des notes de bas de page car elles sont insres successivement la fin de llment :
$(document).ready(function() { $('<ol id="notes"></ol>').insertAfter('div.chapter'); $('span.footnote').each(function(index) { $(this) .before( ['<a href="#foot-note-', index+1, '" id="context-', index+1, '" class="context">', '<sup>' + (index+1) + '</sup>', '</a>' ].join('') ) .appendTo('#notes') }); });

Noubliez pas que lappel de la mthode .appendTo() est toujours chan $(this). Autrement dit, nous demandons dajouter le <span> de la note de bas de page llment dont lidentifiant est 'notes'. Pour chaque note de bas de page dplace, nous ajoutons un autre lien qui cible la rfrence de la note dans le texte :
$(document).ready(function() { $('<ol id="notes"></ol>').insertAfter('div.chapter'); $('span.footnote').each(function(index) { $(this) .before( ['<a href="#foot-note-', index+1, '" id="context-', index+1, '" class="context">', '<sup>' + (index+1) + '</sup>', '</a>' ].join('') ) .appendTo('#notes') .append('&nbsp;(<a href="#context-' + (index+1) +'">contexte</a>)'); }); });

Lattribut href pointe vers lidentifiant du marqueur correspondant. La Figure 5.8 prsente les notes de bas de page avec le lien vers leur contexte. Il manque aux notes leur numro. Elles ont t construites partir dune liste <ol>, mais elles nont pas t places chacune dans leur propre lment <li>.

Chapitre 5

Manipulation du DOM

97

Les liens ajouts

Figure 5.8
Des liens ajouts aux notes de bas de page permettent de revenir au texte.

5.4

Envelopper des lments

Pour envelopper des lments autour dautres lments, la principale mthode jQuery se nomme .wrap(). Puisque chaque $(this) doit tre envelopp par <li></li>, nous compltons le code des notes de bas de page de la manire suivante :
$(document).ready(function() { $('<ol id="notes"></ol>').insertAfter('div.chapter'); $('span.footnote').each(function(index) { $(this) .before( ['<a href="#foot-note-', index+1, '" id="context-', index+1, '" class="context">', '<sup>' + (index+1) + '</sup>', '</a>' ].join('') ) .appendTo('#notes') .append('&nbsp;(<a href="#context-' + (index+1) +'">contexte</a>)') .wrap('<li id="foot-note-' + (index+1) + '"></li>'); }); });

prsent, les lments <li> sont complets, avec un attribut id qui correspond lattribut href du marqueur de la note. Nous avons enfin des notes de bas de page numrotes et lies (voir Figure 5.9). Bien entendu, nous aurions pu ajouter les numros avant chaque note de bas de page en procdant comme pour leur insertion dans les paragraphes, mais il est trs satisfaisant de gnrer dynamiquement un balisage smantique avec du code JavaScript.

98

jQuery

Figure 5.9
La prsentation finale des notes de bas de page.

INFO
Les autres mthodes jQuery pour envelopper des lments sont .wrapAll() et .wrapInner(). Pour de plus amples informations, consultez les pages http://docs.jquery.com/ Manipulation/wrapAll et http://docs.jquery.com/Manipulation/wrapInner.

5.5

Copier des lments

Jusque-l, nous avons insr de nouveaux lments crs, dplac des lments depuis un emplacement dans le document vers un autre et envelopp de nouveaux lments autour dlments existants. Cependant, il arrive parfois que nous ayons besoin de copier des lments. Par exemple, un menu de navigation qui apparat en haut de la page peut tre copi et ajout galement en bas de la page. Ds que la prsentation visuelle dune page peut tre amliore par recopie dlments, nous pouvons faire intervenir un script. En effet, pourquoi crire deux fois la mme chose, en multipliant dautant les risques derreur, alors que nous pouvons lcrire une seule fois et laisser jQuery se charger du reste ? Pour copier des lments, la mthode .clone() de jQuery convient parfaitement. Elle prend une collection dlments correspondants et en cre une copie pouvant tre utilise ultrieurement. linstar des lments crs avec du code, les lments copis napparatront pas dans le document tant que nous nutiliserons pas lune des mthodes dinsertion. Par exemple, la ligne suivante cre une copie du premier paragraphe contenu dans <div class="chapter"> :
$('div.chapter p:eq(0)').clone();

La Figure 5.10 montre que cela naffecte en rien le contenu de la page.

Chapitre 5

Manipulation du DOM

99

Figure 5.10
La copie dun lment ne change pas le contenu de la page.

Pour que le paragraphe copi fasse partie du contenu du document, nous pouvons linsrer avant <div class="chapter"> :
$('div.chapter p:eq(0)').clone().insertBefore('div.chapter');

prsent, le premier paragraphe apparat deux fois et, puisque sa premire instance ne se trouve plus dans llment <div class="chapter">, les styles associs ce <div> ne lui sont pas appliqus, notamment la largeur (voir Figure 5.11).

Figure 5.11
Le paragraphe clon a t ajout avant le contenu du chapitre.

Pour reprendre une analogie familire la plupart des gens, .clone() est aux mthodes dinsertion ce que "copier" est "coller".

100

jQuery

Cloner avec des vnements Par dfaut, la mthode .clone() ne copie pas les vnements lis llment correspondant ni ses descendants. Cependant, lorsque son seul argument boolen est fix true, elle clone galement les vnements : .clone(true). Cette copie des vnements est trs pratique car nous navons plus besoin de lier nouveau les vnements manuellement, comme nous avons pu le faire au Chapitre 3. Citer des passages Comme leurs homologues imprims, de nombreux sites web utilisent des passages pour mettre en valeur de petites parties du texte et attirer lil du lecteur. Nous pouvons facilement ajouter ce type de citation en utilisant la mthode .clone(). Tout dabord, examinons le troisime paragraphe de notre texte dexemple :
<p> <span class="pull-quote"> <span class="drop">Chez nous, </span>une Loi de la Nature veut qu'un enfant mle ait toujours <strong>un ct de plus</strong> que son pre</span>, de sorte que chaque gnration s'lve (en rgle gnrale) d'un chelon sur la voie du progrs et de l'anoblissement. Ainsi le fils d'un Carr sera un Pentagone ; le fils du Pentagone, un Hexagone ; etc. </p>

Il commence par <span class="pull-quote">. Cette classe sera la cible de notre clonage. Aprs avoir coll le texte copi du <span> un autre emplacement, nous devons modifier ses proprits de style pour le distinguer du texte normal. Petit tour par CSS Pour obtenir le style souhait, nous ajoutons la classe pulled la copie de llment <span>. Voici la rgle de style dfinie pour cette classe :
.pulled { background: #e5e5e5; position: absolute; width: 145px; top: -20px; right: -180px; padding: 12px 5px 12px 10px; font: italic 1.4em "Times New Roman", Times, serif; }

Le passage extrait reoit un arrire-plan gris clair, un espacement et une police diffrente. Mais le plus important est quil est positionn de manire absolue, vingt pixels au-dessus et droite de lanctre positionn (en absolu ou en relatif) le plus proche dans le DOM. Si un positionnement autre que static nest appliqu aucun anctre, le passage de texte sera positionn relativement llment <body> du document. Cest pour cette raison que nous devons vrifier dans le code jQuery que la proprit position de llment parent du passage extrait est fixe relative.

Chapitre 5

Manipulation du DOM

101

Si le positionnement haut est relativement intuitif, il nest pas forcment vident de comprendre comment le bloc du passage extrait sera plac vingt pixels droite de son parent positionn. Tout dabord, nous partons de la largeur totale du bloc du passage extrait. Elle correspond la valeur de la proprit width plus lespacement gauche et droit : 145 px + 5 px + 10 px, cest--dire 160 px. Nous fixons ensuite la proprit right du bloc. Une valeur 0 alignerait le bord droit du passage avec celui de son parent. Par consquent, pour placer son ct gauche vingt pixels droite de son parent, nous devons le dplacer dans le sens oppos de plus de vingt pixels par rapport la largeur totale, cest--dire 180 px. Retour au code Revenons prsent jQuery. Nous commenons par une expression de slection de tous les lments <span class="pull-quote"> et utilisons la mthode .each() afin deffectuer plusieurs oprations sur chacun deux :
$(document).ready(function() { $('span.pull-quote').each(function(index) { //... }); });

Ensuite, nous recherchons le paragraphe parent de chaque passage extrait et ajustons sa proprit CSS position :
$(document).ready(function() { $('span.pull-quote').each(function(index) { var $parentParagraph = $(this).parent('p'); $parentParagraph.css('position', 'relative'); }); });

Une fois encore, nous enregistrons dans une variable le slecteur qui sera utilis plusieurs fois afin damliorer les performances et la lisibilit du code. Nous sommes prsent certains que le CSS du passage extrait est prt. Nous pouvons donc cloner chaque <span>, ajouter la classe pulled la copie et insrer celle-ci au dbut du paragraphe :
$(document).ready(function() { $('span.pull-quote').each(function(index) { var $parentParagraph = $(this).parent('p'); $parentParagraph.css('position', 'relative'); $(this).clone() .addClass('pulled') .prependTo($parentParagraph); }); });

102

jQuery

Puisque nous choisissons un positionnement absolu du passage extrait, son emplacement dans le paragraphe na pas dimportance. Tant quil reste lintrieur du paragraphe, il sera positionn relativement ses bords suprieur et droit, conformment nos rgles CSS. En revanche, si nous voulions appliquer un flottement au passage extrait, son emplacement dans le paragraphe affecterait son positionnement vertical. Le paragraphe, accompagn du passage extrait, est illustr la Figure 5.12.

Figure 5.12
Le passage extrait est plac en regard de son lment parent.

Cest un bon dbut, mais, en gnral, les passages extraits ne conservent pas la mise en forme du texte, comme cest le cas dans notre exemple avec les mots "un ct de plus" affichs en gras. Nous voudrions que le texte du <span class="pull-quote"> soit dbarrass de toute balise du type <strong>, <em> ou <a href>. Par ailleurs, nous aimerions pouvoir modifier lgrement le passage extrait, en retirant certains mots et en les remplaant par des points de suspension. Pour cela, nous avons plac quelques mots du texte dexemple dans une balise <span> : <span class="drop">Chez nous, </span>. Nous commenons par ajouter les points de suspension, puis nous remplaons le contenu HTML du passage par une version texte uniquement :
$(document).ready(function() { $('span.pull-quote').each(function(index) { var $parentParagraph = $(this).parent('p'); $parentParagraph.css('position', 'relative'); var $clonedCopy = $(this).clone(); $clonedCopy .addClass('pulled') .find('span.drop') .html('&hellip;') .end() .prependTo($parentParagraph); var clonedText = $clonedCopy.text();

Chapitre 5

Manipulation du DOM

103

$clonedCopy.html(clonedText); }); });

La procdure de clonage dbute par lenregistrement de la copie dans une variable. Cette variable est indispensable car la manipulation de la copie ne peut pas se faire par un seul chanage de mthodes. Notez galement quaprs avoir recherch <span class="drop"> et avoir remplac son contenu HTML par des points de suspension (&hellip;), nous utilisons .end() pour sortir de la requte .find('span.drop'). Ainsi, nous insrons lintgralit de la copie, pas uniquement les points de suspension, au dbut du paragraphe. la fin du code, nous affectons une autre variable, clonedText, le contenu textuel de la copie. Ensuite, nous utilisons ce contenu en remplacement du contenu HTML de la copie. La nouvelle prsentation des passages extraits est illustre la Figure 5.13.

Figure 5.13
Quelques modifications sont apportes au contenu des passages extraits.

104

jQuery

Un <span class="pull-quote"> a galement t ajout un autre paragraphe pour que le code fonctionne sur plusieurs lments. Embellir les passages extraits Lextraction des passages fonctionne prsent comme voulu, avec suppression des lments enfants et remplacement dune partie du texte par des points de suspension. Cependant, puisque nous avons galement pour objectif damliorer laspect visuel de la page, nous allons ajouter des angles arrondis et une ombre porte aux passages extraits. Mais la hauteur variable des blocs des passages extraits pose problme car elle nous oblige ajouter deux images darrire-plan un lment, ce qui est impossible avec les navigateurs actuels, lexception des versions les plus rcentes de Safari. Pour contourner cette limitation, nous pouvons envelopper les passages dans un autre <div> :
$(document).ready(function() { $('span.pull-quote').each(function(index) { var $parentParagraph = $(this).parent('p'); $parentParagraph.css('position', 'relative'); var $clonedCopy = $(this).clone(); $clonedCopy .addClass('pulled') .find('span.drop') .html('&hellip;') .end() .prependTo($parentParagraph) .wrap('<div class="pulled-wrapper"></div>'); var clonedText = $clonedCopy.text(); $clonedCopy.html(clonedText); }); });

Nous devons galement modifier les rgles CSS pour tenir compte du nouveau <div> et des deux images darrire-plan :
.pulled-wrapper { background: url(pq-top.jpg) no-repeat left top; position: absolute; width: 160px; right: -180px; padding-top: 18px; } .pulled { background: url(pq-bottom.jpg) no-repeat left bottom; position: relative; display: block; width: 140px; padding: 0 10px 24px 10px; font: italic 1.4em "Times New Roman", Times, serif; }

Chapitre 5

Manipulation du DOM

105

Certaines des rgles appliques prcdemment <span class="pulled"> le sont prsent <div class="pulled-wrapper">. Nous procdons quelques ajustements de la largeur et de lespacement pour tenir compte des bordures de limage darrire-plan. Nous modifions galement les proprits position et display de la rgle .pulled afin que la prsentation soit correcte sur tous les navigateurs. La Figure 5.14 prsente laspect final des passages extraits.

Figure 5.14
Laspect final des passages extraits.

5.6

Les mthodes du DOM en bref

Les nombreuses mthodes de manipulation du DOM fournies par jQuery varient en termes dobjectifs et demplacement. Le rcapitulatif suivant vous permettra de dterminer les mthodes que vous pouvez utiliser pour atteindre ces objectifs.

106

jQuery

1. Pour crer de nouveaux lments HTML, utilisez la fonction $(). 2. Pour insrer de nouveaux lments dans chaque lment correspondant, utilisez les mthodes suivantes :
n n n n

.append() ; .appendTo() ; .prepend() ; .prependTo().

3. Pour insrer de nouveaux lments ct de chaque lment correspondant, utilisez les mthodes suivantes :
n n n n

.after() ; .insertAfter() ; .before() ; .insertBefore().

4. Pour insrer de nouveaux lments autour de chaque lment correspondant, utilisez les mthodes suivantes :
n n n

.wrap() ; .wrapAll() ; .wrapInner().

5. Pour remplacer chaque lment correspondant par de nouveaux lments ou du texte, utilisez les mthodes suivantes :
n n n n

.html() ; .text() ; .replaceAll() ; .replaceWith().

6. Pour supprimer des lments dans chaque lment correspondant, utilisez la mthode suivante :
n

.empty().

7. Pour retirer dun document chaque lment correspondant et ses descendants, sans rellement les supprimer, utilisez la mthode suivante :
n

.remove().

Chapitre 5

Manipulation du DOM

107

5.7

En rsum

Dans ce chapitre, nous avons cr, copi, rassembl et embelli du contenu en utilisant les mthodes jQuery de modification du DOM. Nous avons appliqu ces mthodes sur une seule page web, transformant ainsi des paragraphes gnraux en un extrait littraire styl accompagn de notes de bas de page, de citations de passages et de liens. La partie didacticiel de louvrage est presque termine. Toutefois, avant dtudier des exemples plus complexes, nous allons passer du ct du serveur via les mthodes AJAX de jQuery.

6
AJAX
Au sommaire de ce chapitre
U U U U U U U U

Charger des donnes la demande Choisir le format des donnes Passer des donnes au serveur Surveiller la requte AJAX et les vnements Questions de scurit Autres solutions En rsum

Ces dernires annes, il tait frquent de juger les sites selon leur utilisation de certaines technologies. AJAX a certainement t lun des mots les plus en vogue pour dcrire de nouvelles applications web. Ce terme tait employ dans de nombreux contextes diffrents, pour englober un ensemble de possibilits et de techniques connexes. Dun point de vue technique, AJAX est lacronyme de Asynchronous JavaScript and XML. Voici les technologies impliques dans une solution AJAX :
n

JavaScript, pour capturer les actions de lutilisateur ou dautres vnements du navigateur ; lobjet XMLHttpRequest, qui permet deffectuer des requtes sur le serveur sans interrompre les autres tches du navigateur ; des fichiers au format XML sur le serveur ou dans dautres formats de donnes semblables, comme HTML et JSON ; du code JavaScript supplmentaire, pour interprter les donnes transmises par le serveur et les prsenter sur la page.

110

jQuery

La technologie AJAX a t accueillie comme le sauveur du Web, car elle a permis de transformer des pages web statiques en applications web interactives. En raison des incohrences dans la mise en uvre de lobjet XMLHttpRequest par les navigateurs, de nombreux frameworks sont apparus pour aider les dveloppeurs matriser son utilisation ; jQuery en fait partie. Voyons prsent si AJAX peut effectivement faire des miracles.

6.1

Charger des donnes la demande

Derrire toute la publicit quon a pu lui faire, AJAX nest quun mcanisme permettant de charger des donnes depuis le serveur vers le navigateur web, ou client, sans un rafrachissement de la page. Ces donnes peuvent prendre plusieurs formes et plusieurs solutions soffrent nous pour les traiter lorsquelles arrivent. Nous allons illustrer cela en effectuant la mme tche de base de diffrentes manires. Nous allons construire une page qui affiche les entres dun dictionnaire, regroupes sous la premire lettre du terme. Le balisage HTML suivant dfinit la zone de contenu :
<div id="dictionary"> </div>

Vous ne rvez pas, la page est initialement vide. Nous allons nous servir des diffrentes mthodes AJAX de jQuery pour remplir ce <div> avec les entres du dictionnaire. Nous aurons besoin dun systme pour dclencher le chargement. Pour cela, nous ajoutons des liens auxquels nous associerons des gestionnaires dvnements :
<div class="letters"> <div class="letter" id="letter-a"> <h3><a href="#">A</a></h3> </div> <div class="letter" id="letter-b"> <h3><a href="#">B</a></h3> </div> <div class="letter" id="letter-c"> <h3><a href="#">C</a></h3> </div> <div class="letter" id="letter-d"> <h3><a href="#">D</a></h3> </div> </div>

INFO
Comme toujours, dans le cadre dune implmentation relle, il faut utiliser le principe damlioration progressive de manire que la page soit oprationnelle mme en labsence de JavaScript. Pour simplifier notre exemple, les liens nauront aucun effet tant que nous ne leur aurons pas attribu des comportements avec jQuery.

Chapitre 6

AJAX

111

Avec quelques rgles CSS, la page se prsente telle qu la Figure 6.1.


Figure 6.1
La page initiale du dictionnaire.

Nous pouvons prsent nous focaliser sur lobtention dun contenu pour la page. Ajouter du contenu HTML Les applications AJAX ne font souvent que demander au serveur des morceaux de contenu HTML. Cette technique, parfois dsigne sous lacronyme AHAH (Asynchronous HTTP and HTML), est trs simple mettre en uvre avec jQuery. Tout dabord, nous avons besoin du contenu HTML insrer. Il est enregistr dans le fichier a.html au ct de notre document principal. Voici le dbut de ce fichier HTML secondaire :
<div class="entry"> <h3 class="term">ACCOMPLI</h3> <div class="part">adj.</div> <div class="definition"> On critiquait devant un bon cur ses paroissiennes. Cependant, rpondit-il, je les vois presque toutes Complies. </div> </div> <div class="entry"> <h3 class="term">ACROSTICHE</h3> <div class="part">n.</div> <div class="definition"> Celui que nous citons ici offre un gracieux jeu de mots. <div class="quote"> <div class="quote-line"><strong>L</strong>ouis est un hros sans peur et sans reproche.</div> <div class="quote-line"><strong>O</strong>n dsire le voir. Aussitt qu'on l'approche,</div> <div class="quote-line"><strong>U</strong>n sentiment d'amour enflamme tous les curs.</div> <div class="quote-line"><strong>I</strong>l ne trouve chez nous que des adorateurs.</div>

112

jQuery

<div class="quote-line"><strong>S</strong>on image est partout, except dans ma poche.</div> </div> </div> </div> <div class="entry"> <h3 class="term">ADMIRATION</h3> <div class="part">n.</div> <div class="definition"> On demandait des soldats en campagne si on pouvait compter sur eux et quel tait le thermomtre de leur enthousiasme. L'un d'eux rpondit : Nous en sommes <em>la demi-ration</em>. </div> </div>

La page se poursuit avec dautres entres dans cette structure HTML. Sans mise en forme approprie, laspect de cette page est plutt brut1 (voir Figure 6.2).

Figure 6.2
Affichage rudimentaire dun contenu HTML.

1. N.d.T. : les caractres accentus ne sont pas affichs correctement car la structure HTML seule ne prcise pas lencodage des caractres adquat.

Chapitre 6

AJAX

113

Cette prsentation est due au fait que le fichier a.html ne constitue pas un vritable document HTML : il ne contient aucune des balises <html>, <head> ou <body> habituellement requises. Ce type de fichier est appel extrait ou fragment. Il nexiste que pour tre insr dans un autre document HTML :
$(document).ready(function() { $('#letter-a a').click(function() { $('#dictionary').load('a.html'); return false; }); });

La mthode .load() soccupe de tous les dtails notre place. Nous indiquons la cible du fragment HTML en utilisant un slecteur jQuery classique et passons en argument de cette mthode lURL du fichier charger. Un clic sur le premier lien charge le fichier et le place dans <div id="dictionary">. Le navigateur affiche le nouveau contenu HTML aprs son insertion (voir Figure 6.3).

Figure 6.3
Affichage du fragment HTML aprs insertion dans un document HTML.

Le fragment HTML nest plus brut, mais styl. En effet, les rgles CSS du document principal sont immdiatement appliques cet extrait ds quil est insr.

114

jQuery

Si vous testez cet exemple, les dfinitions du dictionnaire apparatront probablement instantanment aprs avoir cliqu sur le lien. Cest lun des dangers du test local des applications. Il est difficile de tenir compte des dlais de transmission des documents au travers du rseau. Supposons que nous ajoutions une bote dalerte qui saffiche aprs que les dfinitions sont charges :
$(document).ready(function() { $('#letter-a a').click(function() { $('#dictionary').load('a.html'); alert('Dfinitions charges !'); return false; }); });

La structure de ce code nous laisse supposer que le message est affich uniquement lorsque le chargement des dfinitions est termin. En effet, lexcution dun code JavaScript se fait gnralement de manire synchrone, cest--dire une tche aprs lautre. Cependant, lorsque ce code particulier est test sur un serveur web en production, le message dalerte peut tre affich et avoir disparu avant que le changement ne soit termin, en raison de la latence du rseau. En effet, tous les appels AJAX sont par dfaut asynchrones. Si ce ntait pas le cas, cette technologie se serait nomme SJAX. Un chargement asynchrone signifie quune fois que la requte HTTP dobtention du fragment HTML a t mise, lexcution du script reprend immdiatement sans attendre la rponse. Plus tard, le navigateur reoit la rponse du serveur et la prend en charge. Ce comportement est gnralement celui attendu ; il est peu convivial de bloquer le navigateur web en attendant larrive des donnes. Si des actions doivent tre reportes la fin du chargement, jQuery fournit pour cela une fonction de rappel. Nous en verrons un exemple plus loin. Manipuler des objets JavaScript Si recevoir la demande du HTML intgralement balis se rvle trs pratique, il se peut que nous voulions traiter les donnes avant quelles ne soient affiches. Dans ce cas, nous devons les obtenir sous forme dune structure manipulable avec du code JavaScript. Grce aux slecteurs de jQuery, nous pouvons parcourir le balisage HTML reu et le manipuler, mais il doit tout dabord tre insr dans le document. En utilisant un format de donnes JavaScript natif, le code ncessaire sera plus rduit. Obtenir un objet JavaScript Nous lavons vu, les objets JavaScript ne sont que des ensembles de couples cl-valeur qui peuvent tre dfinis succinctement laide daccolades ({}). Quant aux tableaux

Chapitre 6

AJAX

115

JavaScript, ils sont dfinis dynamiquement avec des crochets ([]). En combinant ces deux concepts, nous pouvons facilement exprimer des structures de donnes trs complexes et riches. Le terme JSON (JavaScript Object Notation) a t invent par Douglas Crockford pour dsigner cette syntaxe simple. Elle offre une alternative concise au format XML parfois volumineux :
{ "cle": "valeur", "cle2": [ "tableau", "d", "lments" ] }

INFO
Pour de plus amples informations concernant les avantages potentiels de JSON, ainsi que des exemples de mise en uvre dans diffrents langages de programmation, consultez le site http://json.org/.

Il existe diffrentes manires dencoder les donnes dans ce format. Dans notre exemple, nous plaons les entres du dictionnaire dans un fichier JSON nomm b.json, dont voici le dbut :
[ { "term": "BADAUD", "part": "n.", "definition": "L'expression de badaud qu'on applique aux Parisiens comme une injure, vient, dit-on, d'un mot celtique qui signifie batelier, parce que les Parisiens faisaient autrefois un grand commerce par eau. La ville de Paris porte mme un navire pour armoiries.", "quote": [ "De peur d'offenser sa patrie,", "Journel, mon imprimeur, digne enfant de Paris,", "Ne veut rien imprimer sur la badauderie.", "Journel est bien de son pays." ], "author": "Mnage" }, { "term": "BOITER", "part": "v. tr.", "definition": "Un homme qui venait d'acheter un cheval, la vue, reconnut qu'il tait boiteux et voulut rsilier son march. Mais le vendeur tmoigna qu'il l'avait prvenu de ce dfaut, en lui disant : Il boite et mange bien." },

116

jQuery

{ "term": "BONHEUR", "part": "n.", "definition": "Le moyen d'tre heureux en mnage, c'est de se marier au point du jour, parce qu'alors on est sr d'avoir fait un mariage de bonne heure." },

Pour obtenir ces donnes, nous utiliserons la mthode $.getJSON(). Elle rcupre le fichier, le traite et retourne au code appelant lobjet JavaScript rsultant. Fonctions jQuery globales Jusqu prsent, toutes les mthodes jQuery que nous avons employes sont associes un objet jQuery construit par la fonction $(). Les slecteurs nous permettent de prciser les nuds du DOM sur lesquels nous souhaitons travailler en invoquant des mthodes. En revanche, la fonction $.getJSON() est diffrente. Elle ne sapplique logiquement aucun lment du DOM ; lobjet rsultant est fourni au script, non inject dans la page. Cest pourquoi getJSON() est une mthode de lobjet jQuery global (un objet unique appel jQuery ou $ dfini une seule fois par la bibliothque jQuery), la place dune instance dobjet jQuery individuelle (les objets crs par la fonction $()). Si, comme dans dautres langages orients objet, la notion de classe existait en JavaScript, $.getJSON() serait une mthode de classe. Pour nous, ce type de mthode est une fonction globale, cest--dire une fonction qui utilise lespace de noms de jQuery pour ne pas entrer en conflit avec dautres noms de fonctions. Pour utiliser cette fonction, nous lui fournissons le nom du fichier :
$(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json'); return false; }); });

En cliquant sur le lien correspondant, ce code ne semble avoir aucun effet. Lappel de la fonction charge le fichier, mais les donnes rsultantes ne sont pas utilises. Pour cela, nous devons employer une fonction de rappel. La fonction $.getJSON() prend un second argument qui dsigne la fonction invoque lorsque le chargement est termin. Nous lavons mentionn prcdemment, les appels AJAX sont asynchrones et la fonction de rappel constitue une technique pour attendre les donnes transmises au lieu dexcuter le code directement. La fonction de rappel prend galement un argument, qui contient les donnes rsultantes. Nous pouvons donc crire le code suivant :
$(document).ready(function() { $('#letter-b a').click(function() {

Chapitre 6

AJAX

117

$.getJSON('b.json', function(data) { }); return false; }); });

Dans ce code, nous utilisons une fonction de rappel anonyme, comme nous en avons lhabitude pour un code jQuery concis. Toutefois, rien nempche de passer une fonction nomme. Dans la fonction, nous pouvons utiliser la variable data pour examiner la structure de donnes. Nous devons parcourir le tableau de premier niveau, en construisant le contenu HTML qui correspond chaque lment. Pour cela, une boucle for classique suffit, mais nous allons profiter de cette occasion pour prsenter une autre fonction globale utile : $.each(). Nous avons rencontr son homologue au Chapitre 5, la mthode .each(). Au lieu doprer sur un objet jQuery, cette fonction prend en premier argument un tableau ou une mappe, et une fonction de rappel en second argument. chaque tour de boucle, lindice ditration et llment courants du tableau ou de la mappe sont passs en paramtres la fonction de rappel :
$(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json', function(data) { $('#dictionary').empty(); $.each(data, function(entryIndex, entry) { var html = '<div class="entry">'; html += '<h3 class="term">' + entry['term'] + '</h3>'; html += '<div class="part">' + entry['part'] + '</div>'; html += '<div class="definition">'; html += entry['definition']; html += '</div>'; html += '</div>'; $('#dictionary').append(html); }); }); return false; }); });

Avant la boucle, nous vidons llment <div id="dictionary"> de manire le remplir avec notre nouveau contenu HTML construit. Ensuite, nous appelons $.each() pour examiner chaque lment et construire une structure HTML partir de son contenu. Enfin, nous convertissons ce balisage HTML en une arborescence DOM en lajoutant dans la balise <div>.
INFO
Cet exemple suppose que les donnes se prtent parfaitement un balisage HTML. Autrement dit, elles ne doivent pas contenir certains caractres spciaux, comme <.

118

jQuery

Il ne nous reste plus qu prendre en charge les entres contenant des citations, ce que nous faisons avec une autre boucle $.each() :
$(document).ready(function() { $('#letter-b a').click(function() { $.getJSON('b.json', function(data) { $('#dictionary').empty(); $.each(data, function(entryIndex, entry) { var html = '<div class="entry">'; html += '<h3 class="term">' + entry['term'] + '</h3>'; html += '<div class="part">' + entry['part'] + '</div>'; html += '<div class="definition">'; html += entry['definition']; if (entry['quote']) { html += '<div class="quote">'; $.each(entry['quote'], function(lineIndex, line) { html += '<div class="quote-line">' + line + '</div>'; }); if (entry['author']) { html += '<div class="quote-author">' + entry['author'] + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append(html); }); }); return false; }); });

Lorsque ce code est en place, nous cliquons sur le lien B et confirmons le bon fonctionnement de notre code (voir Figure 6.4).
INFO
Le format JSON est concis, mais peu indulgent. Chaque crochet, accolade, apostrophe et virgule doit tre prsent et justifi, ou le fichier ne pourra pas tre charg. Dans la plupart des navigateurs, vous ne recevrez aucun message derreur. Le script chouera en silence.

Excuter un script Parfois, nous ne souhaitons pas charger tout le code JavaScript ncessaire ds le chargement de la page. Il se peut mme que nous ne sachions pas quels sont les scripts utiles tant que lutilisateur na pas effectu certaines actions. Nous pourrions ajouter dynamiquement des balises <script> au fur et mesure des besoins, mais une solution plus lgante pour injecter le code supplmentaire consiste laisser jQuery charger directement le fichier .js.

Chapitre 6

AJAX

119

Figure 6.4
Chargement des entres enregistres dans une structure JSON.

Le chargement dun script est aussi simple que le chargement dun fragment HTML. Nous utilisons la fonction globale $.getScript(), qui, comme son homologue, prend en argument lURL du fichier du script :
$(document).ready(function() { $('#letter-c a').click(function() { $.getScript('c.js'); return false; }); });

Dans lexemple prcdent, nous devions traiter les donnes obtenues afin dexploiter le fichier charg. Avec un fichier de script, la procdure est automatique : le script est simplement excut. Les scripts obtenus de cette manire sont excuts dans le contexte global de la page en cours. Cela signifie quils ont accs toutes les fonctions et variables dfinies globalement, notamment celles de jQuery. Nous pouvons donc imiter lexemple JSON pour prparer et insrer du contenu HTML sur la page lors de lexcution du script et placer ce code dans c.js :

120

jQuery

var entries = [ { "term": "CHAIR", "part": "n.", "definition": "M. Ch. Monselet, aprs avoir entendu M. Saisset,..." }, { "term": "CHALEUR", "part": "n.", "definition": "Bautru se promenait le chapeau la main, par un soleil..." }, { "term": "CHANT", "part": "n.", "definition": "On dit que le rossignol ne chante plus lorsqu'il est..." }, { "term": "CHAUDRON", "part": "n.", "definition": "Brunet disait que le vase qu'on appelle chaudron..." }, { "term": "CHER", "part": "n.", "definition": "C'est un dpartement o l'on ne peut pas vivre..." }, { "term": "COMBL", "part": "adj.", "definition": "Un trs-gros homme, arrt au bord d'un foss, disait :..." } ]; var html = ''; $.each(entries, function() { html += '<div class="entry">'; html += '<h3 class="term">' + this['term'] + '</h3>'; html += '<div class="part">' + this['part'] + '</div>'; html += '<div class="definition">' + this['definition'] + '</div>'; html += '</div>'; }); $('#dictionary').html(html);

La Figure 6.5 montre le rsultat que lon obtient en cliquant sur le lien C. Charger un document XML XML fait partie de lacronyme AJAX et, pourtant, nous navons pas encore charg un document XML. Nanmoins, cette procdure est simple et reprend assez fidlement la technique JSON. Tout dabord, nous avons besoin dun fichier XML, d.xml, qui contient les donnes afficher :

Chapitre 6

AJAX

121

Figure 6.5
Le contenu est insr en chargeant un script. <?xml version="1.0" encoding="UTF-8"?> <entries> <entry term="DCRET" part="n."> <definition> Un savant prtend que les mots dcret et dcrter ont t invents par Minos roi et lgislateur de la Crte. </definition> </entry> <entry term="DSASTRE" part="n."> <definition> Dans les derniers jours du Directoire, on trouva un matin, sur la porte du Luxembourg, un magnifique soleil frachement peint, et portant au milieu de ses rayons ce seul mot<![CDATA[&nbsp;]]>: la Rpublique. Les Parisiens comprirent le rbus, et tout le monde le lisait. (La Rpublique dans le plus grand des astres.) </definition> </entry> <entry term="DESCARTES" part="n."> <definition> Le marquis de Saint-Aulaire, qui, la fin du XVIIe sicle, fit les dlices de la cour de la duchesse du Maine, fut pri un jour par cette princesse de lui expliquer le systme de Newton. La sachant zle cartsienne, le spirituel marquis luda la question en improvisant ce petit couplet sur l'air des fraises : </definition> <quote author="Marquis de Saint-Aulaire"> <line>Princesse, dtachons-nous</line> <line>De Newton, de Descartes ;</line> <line>Ces deux espces de fous</line> <line>N'ont jamais vu le dessous</line>

122

jQuery

<line>Des cartes,</line> <line>Des cartes,</line> <line>Des cartes.</line> </quote> </entry> <entry term="DTESTABLE" part="adj."> <definition> On s'est rjoui beaucoup en 1858 d'avoir une si grande suite de jours d't stables. </definition> </entry> </entries>

Bien videmment, ces donnes peuvent tre fournies de diffrentes manires, certaines tant plus proches de la structure tablie pour les exemples HTML et JSON prcdents. Toutefois, nous illustrons ici certaines caractristiques de XML qui permettent ce format dtre plus facile lire, par exemple en utilisant des attributs pour les termes la place des balises. Notre fonction dbute de manire semblable aux prcdentes :
$(document).ready(function() { $('#letter-d a').click(function() { $.get('d.xml', function(data) { }); return false; }); });

Cette fois-ci, le travail est ralis par la fonction $.get(). En gnral, elle rcupre simplement le fichier indiqu par lURL et passe le texte brut correspondant la fonction de rappel. Toutefois, si le type MIME fourni par le serveur indique que la rponse est au format XML, la fonction de rappel manipulera larborescence DOM correspondante. Par chance, jQuery offre de nombreuses fonctions de parcours du DOM. Nous pouvons employer les mthodes .find(), .filter() et autres sur le document XML comme nous le ferions sur du contenu HTML :
$(document).ready(function() { $('#letter-d a').click(function() { $.get('d.xml', function(data) { $('#dictionary').empty(); $(data).find('entry').each(function() { var $entry = $(this); var html = '<div class="entry">'; html += '<h3 class="term">' + $entry.attr('term') + '</h3>'; html += '<div class="part">' + $entry.attr('part') + '</div>'; html += '<div class="definition">'; html += $entry.find('definition').text(); var $quote = $entry.find('quote'); if ($quote.length) { html += '<div class="quote">'; $quote.find('line').each(function() {

Chapitre 6

AJAX

123

html += '<div class="quote-line">' + $(this).text() + '</div>'; }); if ($quote.attr('author')) { html += '<div class="quote-author">' + $quote.attr('author') + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append($(html)); }); }); return false; }); });

La Figure 6.6 montre quun clic sur le lien D produit le rsultat escompt.

Figure 6.6
Insertion dun contenu XML.

124

jQuery

Il sagit dune nouvelle utilisation des mthodes de parcours du DOM que nous avons dj employes, soulignant, si ctait encore ncessaire, la flexibilit de la prise en charge des slecteurs CSS par jQuery. La syntaxe CSS sert gnralement embellir les pages HTML, et les slecteurs dfinis dans les fichiers .css standard utilisent des noms de balises HTML comme div et body pour localiser du contenu. En revanche, avec jQuery, nous pouvons employer des noms de balises XML quelconques, comme entry et definition dans cet exemple, en plus des balises HTML standard. Le moteur de slection labor de jQuery facilite galement la localisation de parties du document XML pour des cas plus complexes. Par exemple, supposons que nous souhaitions limiter laffichage aux entres qui contiennent des citations et leur auteur. Pour ne slectionner que les entres qui comprennent des lments <quote>, nous remplaons entry par entry:has(quote). Pour retenir uniquement les entres dont les lments <quote> possdent un attribut author, nous crivons entry: has(quote[author]). La ligne contenant le slecteur initial devient alors :
$(data).find('entry:has(quote[author])').each(function() {

Cette nouvelle expression de slection restreint les entres slectionnes (voir Figure 6.7).

Figure 6.7
Uniquement les entres comprenant une citation avec son auteur.

Chapitre 6

AJAX

125

6.2

Choisir le format des donnes

Nous avons examin quatre formats pour les donnes externes, chacun pouvant tre trait par les fonctions AJAX natives de jQuery. Nous avons galement vrifi que ces quatre formats permettent de rpondre aux besoins, en chargeant des informations dans une page existante lorsque lutilisateur les demande, non avant. Se pose donc naturellement la question du choix du format retenir dans nos applications. Les fragments HTML demandent trs peu de travail. Les donnes externes peuvent tre charges et insres dans la page laide dune seule mthode simple, qui nimplique aucune fonction de rappel. Pour ajouter le nouveau contenu HTML la page existante, aucun parcours des donnes nest ncessaire. En revanche, la structure des donnes dune application ne convient pas ncessairement dautres applications. Le fichier externe est fortement coupl au conteneur cibl. Les fichiers JSON ont une structure qui facilite la rutilisation. Ils sont concis et faciles lire. Il faut parcourir la structure de donnes pour en extraire les informations et les prsenter sur la page, mais cette procdure peut tre mise en uvre avec des techniques JavaScript standard. Puisque les fichiers peuvent tre parss par un simple appel la fonction JavaScript eval(), la lecture dun fichier JSON est extrmement rapide. Toutefois, toute utilisation deval() prsente des risques. Des erreurs dans le fichier JSON conduisent un chec silencieux ou des effets secondaires sur la page. Par consquent, les donnes doivent tre prpares soigneusement par un tiers de confiance. Les fichiers JavaScript offrent la souplesse maximale, mais ne constituent pas un vritable mcanisme de stockage de donnes. Puisque les fichiers sont propres au langage, ils ne peuvent pas servir fournir des informations des systmes disparates. En revanche, grce cette possibilit de charger un fichier JavaScript, les comportements rarement ncessaires peuvent tre placs dans des fichiers spars pour ntre chargs quen cas de besoin. La taille du code sen trouve ainsi rduite. Les documents XML sont les rois de la portabilit. Puisque XML est devenu la lingua franca des services web, lutilisation de ce format permet de rutiliser les donnes ailleurs. Par exemple, Flickr (http://flickr.com/), del.icio.us (http://del.icio.us/) et Upcoming (http://upcoming.org/) exportent des reprsentations XML de leurs donnes, ce qui a permis de dvelopper de nombreuses applications tierces partir de ces informations. Cependant, le format XML est quelque peu volumineux et risque dtre plus lent analyser et manipuler que dautres solutions. Si lon considre toutes ces caractristiques, il est plus facile de fournir des donnes externes sous forme de fragments HTML, tant quelles nont pas tre employes dans dautres applications. Si les donnes doivent tre rutilises dans dautres applications dont le code est disponible, JSON se rvle souvent un bon choix en raison de ses per-

126

jQuery

formances et de sa taille. Lorsque lapplication distante est inconnue, XML constitue la meilleure garantie dinteroprabilit. Plus que toute autre considration, il faut savoir si les donnes sont dj disponibles. Dans laffirmative, il est probable quelles se trouvent dj dans lun de ces formats et le choix est alors tout fait.

6.3

Passer des donnes au serveur

Les exemples prcdents ont expliqu comment obtenir des fichiers de donnes statiques partir du serveur web. Cependant, la technique AJAX est encore plus intressante lorsque le serveur peut constituer dynamiquement les donnes en fonction dinformations fournies par le navigateur. Sur ce point, nous pouvons galement compter sur jQuery. Toutes les mthodes dcrites jusqu prsent peuvent tre modifies pour que le transfert des donnes soit bidirectionnel.
INFO
Puisque lillustration de cette technique implique une interaction avec le serveur web, nous allons devoir utiliser pour la premire fois du code ct serveur. Les exemples prsents se fondent sur PHP, un langage de script largement employ et disponible gratuitement. Nous nexpliquerons pas comment configurer le serveur web avec PHP. Pour cela, vous pouvez consulter les sites web dApache (http://apache.org/), de PHP (http://php.net/) ou de la socit qui hberge votre site.

Effectuer une requte GET Pour servir dexemple la communication entre un client et un serveur, nous allons dvelopper un script qui envoie une seule entre du dictionnaire au navigateur chaque requte. Lentre choisie dpendra dun paramtre transmis par le navigateur. Le script prend lentre correspondante partir dune structure de donnes semblable la suivante :
<?php $entries = array( 'CRIRE' => array( 'part' => 'v. tr.', 'definition' => "Un savant, connu par un nasillement extraordinaire, assistait la lecture d'un ouvrage historique. -- Cet ouvrage est mal crit, s'cria-t-il, un style prtentieux, plein d'affectation ! Il faut avant tout crire comme on parle. -- C'est fort bien, dit un ami de l'auteur, mais alors, vous qui parlez du nez, vous devez crire de mme.", ), 'EFFORT' => array( 'part' => 'n.', 'definition' => "Voyant un homme qui avait le nez trs-gros, Odry disait : -- En faisant cet homme-l la nature a fait un nez fort.", ),

Chapitre 6

AJAX

127

... 'T' => array( 'part' => 'n.', 'definition' => "On boit tant de th en hiver dans les soires de Londres, qu'on a dit que les Anglais faisaient de l'hiver la saison des ths.", ), 'EXCUTIF' => array( 'part' => 'n.', 'definition' => "Quand l'Assemble constituante eut restreint, comme on sait, l'autorit royale de Louis XVI, on fit cette pigramme :", 'quote' => array( "Entre savants, quelquefois on dispute.", "D'o vient ce nom : pouvoir excutif", "Que donne au roi le corps lgislatif ?", "Eh ! le voici : trop faible pour la lutte,", "C'est un pouvoir, hlas ! qui s'excute.", ), ), ); ?>

Dans une version relle de cet exemple, les donnes seraient enregistres dans une base de donnes et charges la demande. Puisque, dans notre version, les donnes font partie du script, le code pour les obtenir est relativement simple. Nous examinons le terme transmis et construisons le fragment HTML afficher :
<?php $term = mb_strtoupper($_REQUEST['term'], 'utf-8'); if (isset($entries[$term])) { $entry = $entries[$term]; $html = '<div class="entry">'; $html .= '<h3 class="term">'; $html .= $term; $html .= '</h3>'; $html .= '<div class="part">'; $html .= $entry['part']; $html .= '</div>'; $html .= '<div class="definition">'; $html .= $entry['definition']; if (isset($entry['quote'])) { $html .= '<div class="quote">'; foreach ($entry['quote'] as $line) { $html .= '<div class="quote-line">'. $line .'</div>'; } if (isset($entry['author'])) { $html .= '<div class="quote-author">'. $entry['author'].'</div>'; } $html .= '</div>'; } $html .= '</div>'; $html .= '</div>'; print($html); } ?>

128

jQuery

Les requtes sur ce script, nomm e.php, retournent le fragment HTML correspondant au terme indiqu dans les paramtres de GET. Par exemple, en accdant au script avec e.php?term=excutif, nous obtenons le contenu HTML illustr la Figure 6.82.
Figure 6.8
Entre du dictionnaire fournie par un script PHP.

Une fois encore, nous remarquons la prsentation quelque peu basique de ce fragment HTML. En effet, il nest pas insr dans un vritable document HTML et les rgles CSS ne sont pas appliques. Puisque nous sommes en train dexpliquer comment transmettre des donnes au serveur, nous allons utiliser une autre mthode pour demander des entres du dictionnaire. la place dun lien sur une lettre de lalphabet, nous proposerons une liste de liens pour chaque terme et ferons en sorte quun clic sur lun des liens charge la dfinition correspondante. Pour cela, nous ajoutons le balisage HTML suivant :
<div class="letter" id="letter-e"> <h3>E</h3> <ul> <li><a href="e.php?term=crire">crire</a></li> <li><a href="e.php?term=effort">Effort</a></li> <li><a href="e.php?term=emploi">Emploi</a></li> <li><a href="e.php?term=encorn">Encorn</a></li> <li><a href="e.php?term=encre">Encre</a></li> <li><a href="e.php?term=enseigner">Enseigner</a></li> <li><a href="e.php?term=envieux">Envieux</a></li> <li><a href="e.php?term=piciers">piciers</a></li> <li><a href="e.php?term=esprit">Esprit</a></li> <li><a href="e.php?term=t">t</a></li> <li><a href="e.php?term=excutif">Excutif</a></li> </ul> </div>

2. N.d.T : les caractres accentus ne sont pas affichs correctement car le fragment HTML reu nest pas inclus dans un document HTML qui fixe lencodage adquat.

Chapitre 6

AJAX

129

Nous devons modifier notre code JavaScript pour quil invoque le script PHP avec les paramtres adquats. Pour cela, nous pouvons employer le mcanisme .load() normal, en concatnant la chane de requte lURL et en demandant directement les donnes avec des adresses de la forme e.php?term=excutif. Toutefois, nous pouvons faire en sorte que jQuery construise la chane de requte partir dune mappe passe la fonction $.get() :
$(document).ready(function() { $('#letter-e a').click(function() { $.get('e.php', {'term': $(this).text()}, function(data) { $('#dictionary').html(data); }); return false; }); });

Puisque nous connaissons prsent les interfaces AJAX de jQuery, le fonctionnement de ce code nous est familier. La seule diffrence rside dans le second paramtre, qui nous permet de fournir une mappe de cls et de valeurs qui seront intgres la chane de requte. Dans notre cas, la cl est toujours term, mais la valeur est extraite du contenu de chaque lien. En cliquant sur le dernier lien de la liste, la dfinition correspondante saffiche (voir Figure 6.9).

Figure 6.9
La dfinition du terme transmis est affiche.

130

jQuery

Les adresses de tous les liens sont indiques dans le source HTML, mme si nous ne les utilisons pas dans le code. Ainsi, lorsque JavaScript est dsactiv ou indisponible, lutilisateur pourrait encore disposer dune mthode de navigation (toujours notre principe damlioration progressive) condition dadapter le code PHP pour quil renvoie dans ce cas une page HTML complte. Pour viter quun clic sur les liens ne dclenche laction par dfaut, le gestionnaire dvnements doit retourner false. Effectuer une requte POST Les requtes HTTP de type POST sont presque identiques celles de type GET. La diffrence notable rside dans le fait que la mthode GET place ses arguments dans la chane de requte, contrairement la mthode POST. Toutefois, dans les appels AJAX, cette distinction est invisible lutilisateur normal. En gnral, la seule raison de choisir une mthode par rapport lautre est une volont de se conformer aux standards du code ct serveur ou pour transmettre de grandes quantits de donnes ; la mthode GET impose des limites plus strictes. Notre script PHP est conu pour prendre en charge les deux mthodes. Par consquent, pour passer de GET POST, nous devons simplement modifier la fonction jQuery invoque :
$(document).ready(function() { $('#letter-e a').click(function() { $.post('e.php', {'term': $(this).text()}, function(data) { $('#dictionary').html(data); }); return false; }); });

Les arguments sont identiques, mais la requte est prsent de type POST. Nous pouvons simplifier le code en invoquant la mthode .load(), qui utilise POST par dfaut lorsquune mappe lui est passe en argument :
$(document).ready(function() { $('#letter-e a').click(function() { $('#dictionary').load('e.php', {'term': $(this).text()}); return false; }); });

Cette version plus concise fonctionne de manire identique (voir Figure 6.10). Srialiser un formulaire Pour envoyer des donnes au serveur, lutilisateur doit souvent remplir un formulaire. Au lieu demployer le mcanisme normal de soumission dun formulaire, qui charge la rponse dans la fentre du navigateur, nous pouvons utiliser la bote outils AJAX de jQuery pour soumettre le formulaire de manire asynchrone et placer la rponse lintrieur de la page en cours.

Chapitre 6

AJAX

131

Figure 6.10
Obtention dune dfinition par la mthode POST.

Pour illustrer cela, nous construisons un formulaire simple :


<div class="letter" id="letter-f"> <h3>F</h3> <form> <input type="text" name="term" value="" id="term" /> <input type="submit" name="search" value="rechercher" id="search" /> </form> </div>

Cette fois-ci, le script PHP retournera lensemble des entres du dictionnaire qui contiennent le terme recherch. La structure de donnes conserve le format prcdent, mais la logique diffre :
foreach ($entries as $term => $entry) { if (strpos($term, mb_strtoupper($_REQUEST['term'])) !== FALSE) { $html = '<div class="entry">'; $html .= '<h3 class="term">'; $html .= $term; $html .= '</h3>'; $html .= '<div class="part">'; $html .= $entry['part']; $html .= '</div>'; $html .= '<div class="definition">'; $html .= $entry['definition']; if (isset($entry['quote'])) { foreach ($entry['quote'] as $line) { $html .= '<div class="quote-line">'. $line .'</div>'; } if (isset($entry['author'])) { $html .= '<div class="quote-author">'.$entry['author'] .'</div>';

132

jQuery

} } $html .= '</div>'; $html .= '</div>'; print($html); } }

La fonction strpos() recherche le terme indiqu dans la chane passe en argument. Nous pouvons ragir la soumission du formulaire et construire les paramtres de requte appropris en parcourant larborescence du DOM :
$(document).ready(function() { $('#letter-f form').submit(function() { $('#dictionary').load('f.php', {'term': $('input[name="term"]').val()}); return false; }); });

Ce code produit leffet attendu, mais la recherche des champs de saisie en fonction de leur nom et leur ajout un par un une mappe se rvlent fastidieux. Par ailleurs, cette solution nest pas trs efficace lorsque le formulaire devient plus complexe. Heureusement, jQuery propose un raccourci pour cet idiome frquent. La mthode .serialize() opre sur un objet jQuery et convertit les lments du DOM slectionns en une chane de requte qui peut tre passe dans une requte AJAX. Voici comment gnraliser notre gestionnaire de soumission :
$(document).ready(function() { $('#letter-f form').submit(function() { $.get('f.php', $(this).serialize(), function(data) { $('#dictionary').html(data); }); return false; }); });

Dsormais, le mme script permet de prendre en charge des formulaires comprenant un grand nombre de champs. La Figure 6.11 montre les entres qui correspondent notre recherche.

6.4

Surveiller la requte

Jusqu prsent, nous nous sommes contents deffectuer un appel une mthode AJAX et dattendre patiemment la rponse. Cependant, il peut tre utile den savoir un peu plus sur la progression de la requte HTTP. Lorsque ce besoin se fait sentir, nous pouvons utiliser diffrentes mthodes jQuery pour enregistrer des fonctions de rappel qui seront invoques lors dvnements AJAX.

Chapitre 6

AJAX

133

Figure 6.11
Affichage des entres qui contiennent le terme recherch.

Les mthodes .ajaxStart() et .ajaxStop() sont deux exemples de ces fonctions dobservation et peuvent tre associes nimporte quel objet jQuery. Lorsquun appel AJAX dbute, sans autre transfert en cours, la fonction de rappel de .ajaxStart() est invoque. De mme, lorsque la dernire requte active se termine, la fonction de rappel dfinie par .ajaxStop() est excute. Tous les observateurs sont globaux, car ils sont invoqus pour tous les changes AJAX, quel que soit le code qui les a initis. Nous pouvons nous servir de ces mthodes pour fournir lutilisateur un retour en cas de connexion rseau lente. Un message de chargement appropri est ajout au contenu HTML de la page :
<div id="loading"> Chargement en cours... </div>

134

jQuery

Ce message nest quun fragment HTML quelconque. Il pourrait inclure une image GIF anime pour, par exemple, reprsenter une pulsation. Dans notre cas, nous dfinissons quelques rgles CSS afin dafficher le message comme illustr la Figure 6.12.
Figure 6.12
Lindicateur de chargement des dfinitions.

Toutefois, pour rester dans lesprit de lamlioration progressive, ce contenu HTML nest pas plac directement dans la page. Il nest pertinent que si JavaScript est disponible. Par consquent, nous lajoutons en utilisant jQuery :
$(document).ready(function() { $('<div id="loading">Chargement en cours...</div>') .insertBefore('#dictionary') });

Pour que le message soit initialement masqu, la feuille de style affecte la valeur none la proprit display de ce <div>. Pour quil apparaisse au moment opportun, nous lui associons un observateur avec .ajaxStart() :
$(document).ready(function() { $('<div id="loading">Chargement en cours...</div>') .insertBefore('#dictionary') .ajaxStart(function() { $(this).show(); }); });

La procdure de masquage est directement chane ce code :

Chapitre 6

AJAX

135

$(document).ready(function() { $('<div id="loading">Chargement en cours...</div>') .insertBefore('#dictionary') .ajaxStart(function() { $(this).show(); }).ajaxStop(function() { $(this).hide(); }); });

Et voil, laffichage du chargement en cours est oprationnel. Notez que ces mthodes nont aucun rapport avec la manire dont les communications AJAX se passent. La mthode .load() associe au lien A et la mthode .getJSON() associe au lien B conduisent toutes deux au dclenchement de ces actions. Dans notre cas, ce comportement global est adapt. En revanche, si nous devons tre plus spcifiques, nous avons quelques solutions. Certaines mthodes dobservation, comme .ajaxError(), passent leur fonction de rappel une rfrence lobjet XMLHttpRequest. Elle peut servir diffrencier les requtes et mettre en uvre des comportements diffrents. Nous pouvons galement effectuer un traitement plus spcifique en utilisant la fonction de bas niveau $.ajax(), sur laquelle nous reviendrons plus loin. Le plus souvent, les interactions avec la requte se fondent sur la fonction de rappel invoque en cas de russite. Nous lavons dj employe plusieurs fois dans nos exemples pour interprter les donnes transmises par le serveur et insrer les rsultats dans la page. Mais elle peut galement servir dautres formes de retour dinformations. Reprenons notre exemple .load() :
$(document).ready(function() { $('#letter-a a').click(function() { $('#dictionary').load('a.html'); return false; }); });

Nous pouvons lamliorer en faisant en sorte que le contenu saffiche en fondu au lieu dapparatre immdiatement. Pour cela, nous utilisons la fonction de rappel qui peut tre passe .load() et invoque la fin du changement :
$(document).ready(function() { $('#letter-a a').click(function() { $('#dictionary').hide().load('a.html', function() { $(this).fadeIn(); }); return false; }); });

136

jQuery

Tout dabord, nous masquons llment cible et dmarrons le chargement. Une fois celui-ci termin, la fonction de rappel est invoque pour afficher avec un effet de fondu llment rempli.

6.5

AJAX et les vnements

Supposons que nous souhaitions que chaque terme du dictionnaire puisse contrler laffichage de sa dfinition ; en cliquant sur le terme, la dfinition associe est affiche ou masque. laide des techniques dcrites jusqu prsent, la mise en uvre est relativement immdiate :
$(document).ready(function() { $('.term').click(function() { $(this).siblings('.definition').slideToggle(); }); });

Lors du clic sur un terme, ce code recherche les frres de llment dont la classe est definition. Ensuite, il les affiche ou les masque par un effet de glissement. Tout semble parfait, mais, en ralit, un clic ne dclenche aucune action. En effet, les termes nont pas encore t ajouts au document au moment de la liaison des gestionnaires de click. Mme si nous parvenions associer des gestionnaires de click ces lments, un clic sur une lettre diffrente ferait que les gestionnaires ne seraient plus lis. Il sagit dun problme classique lorsque le contenu dune partie de la page est obtenu par une requte AJAX. La solution, galement classique, consiste relier les gestionnaires chaque actualisation de cette partie de la page. Toutefois, cette mthode est fastidieuse car le code de liaison dun vnement doit tre invoqu ds que la structure DOM de la page volue. Au Chapitre 3, nous avons prsent une alternative qui se rvle souvent meilleure. Nous pouvons utiliser la dlgation dvnement, en liant lvnement un lment anctre qui ne change jamais. Dans ce cas, nous associons le gestionnaire de click llment document avec la mthode .live() et interceptons les clics de cette manire :
$(document).ready(function() { $('.term').live('click', function() { $(this).siblings('.definition').slideToggle(); }); });

La mthode .live() demande au navigateur dobserver tous les clics qui se produisent nimporte o sur la page. Si, et seulement si, llment cliqu correspond au slecteur .term, alors le gestionnaire est excut. prsent, le comportement dfini se produira sur nimporte quel terme, mme sil est ajout ultrieurement via une requte AJAX.

Chapitre 6

AJAX

137

6.6

Questions de scurit

Pour toutes ses utilisations dans la construction dapplications web dynamiques, lobjet XMLHttpRequest, qui se trouve au cur de lutilisation dAJAX dans jQuery, est soumis des limites strictes. Pour empcher les attaques de type XSS (cross-site scripting), il est normalement impossible de demander un document un serveur autre que celui do provient la page dorigine. Ce fonctionnement est gnralement adapt. Par exemple, certaines personnes soulignent que lanalyse dune structure JSON avec eval() est risque. Si du code malveillant est insr dans le fichier de donnes, il peut tre excut par eval(). Cependant, puisque le fichier de donnes doit rsider sur le mme serveur que la page web, il est aussi facile dinjecter du code dans ce fichier que de linjecter directement dans la page. Autrement dit, dans le cas dun chargement de fichiers JSON approuvs, eval() ne prsente pas de problmes particuliers quant la scurit. En revanche, il arrive souvent que des donnes doivent tre obtenues partir dune source tierce. Pour contourner les barrires de scurit et mettre en uvre cette opration, il existe plusieurs solutions. Une mthode consiste demander au serveur de charger les donnes distantes, puis de les retransmettre au client. Cette approche est trs puissante car le serveur peut effectuer un prtraitement des donnes si ncessaire. Par exemple, nous pouvons charger des fichiers XML contenant des flux RSS de nouvelles partir de plusieurs sources, les agrger en un seul flux sur le serveur et publier ce nouveau fichier au client qui le demande. Pour charger des donnes partir dun emplacement distant sans impliquer le serveur, il faut tre plus astucieux. Une mthode rpandue pour le chargement de fichiers JavaScript trangers consiste injecter des balises <script> la demande. Puisque jQuery nous aide insrer de nouveaux lments du DOM, le code est simple :
$(document.createElement('script')) .attr('src', 'http://example.com/exemple.js') .appendTo('head');

En ralit, la mthode $.getScript() met automatiquement en place cette technique si elle dtecte un hte distant dans lURL passe en argument. Ce cas est donc pris en charge pour nous. Le navigateur excutera le script charg, mais aucun mcanisme ne permet de rcuprer les rsultats gnrs. Cest pourquoi cette technique exige une coopration avec lhte distant. Le script charg doit effectuer une certaine action, comme fixer la valeur dune variable globale qui a un effet sur lenvironnement local. Les services qui publient des scripts excutables de cette manire fournissent galement une API pour interagir avec le script distant.

138

jQuery

Une autre solution fonde le chargement des donnes distantes sur la balise HTML <iframe>. Cet lment permet dutiliser nimporte quelle URL comme source des donnes, mme si elle ne correspond pas au serveur qui hberge la page. Les donnes peuvent tre facilement charges et affiches sur la page en cours. En revanche, leur manipulation exige une coopration quivalente celle de lapproche fonde sur la balise <script>. Les scripts placs dans llment <iframe> doivent fournir explicitement les donnes en utilisant des objets du document parent. Utiliser JSONP pour les donnes distantes La technique des balises <script> pour rcuprer des fichiers JavaScript depuis une source distante peut tre adapte lobtention de fichiers JSON depuis un autre serveur. Pour cela, nous devons modifier lgrement le fichier JSON sur le serveur. Il existe plusieurs manires de procder, dont lune est directement prise en charge par jQuery : JSONP (JSON with Padding). Un fichier au format JSONP est constitu dun fichier JSON standard plac entre des parenthses et commence par une chane de caractres. Cette chane de remplissage (padding) est dtermine par le client qui demande les donnes. En raison des parenthses et selon la chane fournie, le client peut dclencher linvocation dune fonction ou laffectation dune variable. Limplmentation PHP de la technique JSONP est plutt simple :
<?php print($_GET['callback'] .'('. $data .')'); ?>

Dans ce cas, la variable $data contient une chane de caractres qui reprsente un fichier JSON. Lorsque le script est invoqu, le paramtre callback de la chane de requte est ajout au fichier rsultant qui est envoy au client. Pour illustrer cette technique, nous devons simplement modifier lgrement notre exemple JSON prcdent afin quil invoque cette source de donnes distante. Pour cela, la fonction $.getJSON() utilise un caractre substituable particulier, ? :
$(document).ready(function() { var url = 'http://examples.learningjquery.com/jsonp/g.php'; $('#letter-g a').click(function() { $.getJSON(url + '?callback=?', function(data) { $('#dictionary').empty(); $.each(data, function(entryIndex, entry) { var html = '<div class="entry">'; html += '<h3 class="term">' + entry['term'] + '</h3>'; html += '<div class="part">' + entry['part'] + '</div>'; html += '<div class="definition">'; html += entry['definition']; if (entry['quote']) {

Chapitre 6

AJAX

139

html += '<div class="quote">'; $.each(entry['quote'], function(lineIndex, line) { html += '<div class="quote-line">' + line + '</div>'; }); if (entry['author']) { html += '<div class="quote-author">' + entry['author'] + '</div>'; } html += '</div>'; } html += '</div>'; html += '</div>'; $('#dictionary').append(html); }); }); return false; }); });

Normalement, nous ne sommes pas autoriss rcuprer les donnes JSON depuis un serveur distant (examples.learningjquery.com dans ce cas). Toutefois, puisque ce fichier est conu pour fournir des donnes au format JSONP, nous pouvons obtenir les donnes en ajoutant une chane de requtes lURL, en utilisant ? comme paramtre substituable pour la valeur de largument callback. Au moment de la requte, jQuery remplace ?, parse les rsultats et les passe la fonction de rappel sous forme de donnes comme sil sagissait dune requte JSON locale. Notez que les prcautions concernant la scurit restent valables. Tout ce que le serveur choisit de retourner au navigateur sera excut sur lordinateur de lutilisateur. La technique JSONP ne doit tre employe quavec des donnes issues dune source de confiance.

6.7

Autres solutions

La bote outils AJAX fournie par jQuery est bien remplie. Nous avons examin plusieurs possibilits, mais nous navons utilis que les outils du dessus. Si les variantes sont trop nombreuses pour tre toutes dcrites ici, nous allons prsenter les principales solutions de personnalisation des communications AJAX. La mthode AJAX de bas niveau Nous avons vu plusieurs mthodes qui dclenchent des transactions AJAX. En interne, jQuery associe chacune de ces mthodes des variantes de la fonction globale $.ajax(). Au lieu de supposer une opration AJAX particulire, cette fonction prend en argument une mappe doptions qui personnalise son fonctionnement. Notre premier exemple chargeait un fragment HTML en utilisant $('#dictionary'). load('a.html'). Cette opration peut tre accomplie laide de $.ajax() :

140

jQuery

$.ajax({ url: 'a.html', type: 'GET', dataType: 'html', success: function(data) { $('#dictionary').html(data); } });

Nous devons prciser explicitement la mthode de requte, le type des donnes retournes et le traitement des donnes obtenues. Il est vident que cette approche demande un travail plus important au programmeur, mais il bnficie alors dune plus grande flexibilit. Voici quelques fonctionnalits spciales permises par lutilisation de la mthode de bas niveau $.ajax() :
n

Empcher le navigateur de mettre en cache les rponses du serveur. Cela sera utile si le serveur gnre dynamiquement les donnes. Enregistrer diffrentes fonctions de rappel qui seront excutes suite la russite de la requte, sa terminaison sur une erreur ou dans tous les cas. Supprimer les gestionnaires globaux, comme ceux dfinis avec $.ajaxStart(), qui sont dclenchs normalement par toutes les interactions AJAX. Fournir un nom dutilisateur et un mot de passe pour une authentification auprs de lhte distant.

Pour plus de dtails sur lutilisation des options disponibles, consultez le guide de rfrence jQuery ou la rfrence de lAPI http://docs.jquery.com/Ajax/jQuery.ajax. Modifier les valeurs par dfaut La fonction $.ajaxSetup() nous permet de prciser les valeurs par dfaut de chaque option utilise dans les invocations des mthodes AJAX. Elle prend en argument une mappe doptions identique celle passe la mthode $.ajax(). Ces valeurs sont ensuite utilises par toutes les requtes AJAX ultrieures, sauf indication contraire :
$.ajaxSetup({ url: 'a.html', type: 'POST', dataType: 'html' }); $.ajax({ type: 'GET', success: function(data) { $('#dictionary').html(data); } });

Chapitre 6

AJAX

141

Le code se comporte de la mme manire que dans notre exemple $.ajax() prcdent. LURL de la requte devient une valeur par dfaut grce lappel $.ajaxSetup(). Il nest donc plus utile de la prciser lors de linvocation de la mthode $.ajax(). Si la valeur du paramtre type a t fixe par dfaut POST, rien ne nous empche de lui donner la valeur GET lors de lappel $.ajax(). Charger des parties dune page HTML La premire technique AJAX prsente, la plus simple, consistait obtenir un fragment HTML et linsrer dans la page. Cependant, il arrive parfois que le serveur dispose du contenu HTML dont nous avons besoin, mais quil se trouve dj lintrieur dune page HTML. Lorsquil nest pas facile dobtenir du serveur les donnes dans le format souhait, jQuery peut apporter son aide ct client. Prenons un cas semblable notre premier exemple, mais dans lequel le document qui contient les dfinitions du dictionnaire est une page HTML complte :
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Le dictionnaire des calembours : H</title> <link rel="stylesheet" href="dictionary.css" type="text/css" media="screen" /> </head> <body> <div id="container"> <div id="header"> <h2>Le dictionnaire des calembours : H</h2> <div class="author"> par Le Baron de la Pointe et Le Dr Eugne Le Gai </div> </div> <div id="dictionary"> <div class="entry"> <h3 class="term">HACHES</h3> <div class="part">adj.</div> <div class="definition"> Quelles sont les lettres les plus maltraites ? -- Les lettres H E. </div> </div> <div class="entry"> <h3 class="term">HACHER</h3> <div class="part">v. tr.</div> <div class="definition"> Madame de Svign disait des pendules secondes, qu'elle ne les aimait pas, parce qu'elles hachent la vie trop menu. </div> </div> </div>

142

jQuery

</div> </body> </html>

Nous pouvons charger lintgralit du document dans notre page en utilisant le code dvelopp prcdemment :
$(document).ready(function() { $('#letter-h a').click(function() { $('#dictionary').load('h.html'); return false; }); });

Leffet produit est assez trange (voir Figure 6.13). En effet, certains lments de la page HTML ne sont pas censs tre affichs.

Figure 6.13
La page HTML est incluse dans la page englobante.

Chapitre 6

AJAX

143

Pour retirer le balisage superflu, nous pouvons employer une autre caractristique de la mthode .load(). Lorsque nous indiquons lURL du document charger, nous pouvons galement prciser une expression de slection jQuery. Dans ce cas, elle sert localiser une partie du document charg, qui sera la seule tre insre dans la page. Nous employons cette technique pour extraire du document uniquement les entres du dictionnaire et les placer dans la page :
$(document).ready(function() { $('#letter-h a').click(function() { $('#dictionary').load('h.html .entry'); return false; }); });

Le contenu HTML non pertinent nest alors plus inclus dans la page (voir Figure 6.14).

Figure 6.14
Le balisage HTML superflu nest plus insr dans la page.

144

jQuery

6.8

En rsum

Nous avons appris que les mthodes AJAX fournies par jQuery peuvent nous aider charger des donnes dans diffrents formats partir du serveur sans actualiser la page. Nous pouvons excuter des scripts partir du serveur et lui renvoyer des donnes. Nous avons galement vu comment aborder les problmes poss par les techniques de chargement asynchrones, comme garder des gestionnaires lis aprs le changement et charger des donnes partir dun serveur tiers. Ce chapitre conclut la partie didacticiels de cet ouvrage. Nous matrisons prsent les principaux outils fournis par la bibliothque jQuery : slecteurs, vnements, effets, manipulation du DOM et requtes asynchrones sur le serveur. Toutefois, jQuery peut nous aider de bien dautres manires et nous en dcouvrirons quelques-unes proposes par des plugins. Toutefois, commenons par combiner ces techniques afin damliorer nos pages web de manire novatrice et intressante.

7
Manipulation des tables
Au sommaire de ce chapitre
U Tri et pagination U Modifier laspect de la table U En rsum

Les premiers chapitres ont prsent la bibliothque jQuery au travers de didacticiels qui se focalisaient sur chaque composant de jQuery et les illustraient partir dexemples. Dans les Chapitres 7 9, nous procdons de manire inverse : nous prenons des exemples de problmes rels et voyons comment les rsoudre avec des mthodes jQuery. Une librairie en ligne nous servira de modle de site web, mais les techniques dveloppes peuvent galement sappliquer une grande diversit de sites, allant des blogs aux portfolios en passant par les vitrines commerciales et les intranets dentreprise. Les Chapitres 7 et 8 se focalisent sur des constructions trs rpandues dans les sites web : les tables et les formulaires. Le Chapitre 9 prsente deux faons damliorer visuellement des informations : les carrousels et les prompteurs anims. Ces dernires annes, les standards web ont vu leurs spcifications se prciser et ont donc commenc tre mieux appliqus. Les mises en page fondes sur les tables ont ainsi peu peu t abandonnes au profit des conceptions base de CSS. Si les tables ont souvent servi de solution de dpannage dans les annes 1990 pour crer des prsentations multicolonnes et dautres agencements complexes compatibles avec la plupart des navigateurs, elles nont jamais t conues dans ce but. En revanche, CSS est une technologie cre expressment pour ces questions de prsentation. Toutefois, nous nallons pas dbattre ici du rle des tables. Dans ce chapitre, nous utiliserons jQuery pour appliquer des techniques permettant damliorer la lisibilit, lutilisabilit et lapparence visuelle de conteneurs dont le balisage smantique indique clairement quils contiennent des donnes tabulaires. Pour plus de dtails concernant lapplication dun balisage HTML smantique aux tables, vous pouvez consulter le

146

jQuery

billet "Bring on the Tables" publi par Roger Johansson ladresse http://www. 456bereastreet.com/archive/200410/bring_on_the_tables/. Certaines des techniques que nous appliquons aux tables sont disponibles dans des plugins, comme le module Table Sorter de Christian Bach. Pour de plus amples informations, rendez-vous sur le catalogue des plugins jQuery ladresse http://plugins. jquery.com/. Dans ce chapitre, nous aborderons le tri, la pagination, la mise en exergue des lignes, les info-bulles, la rduction et le dveloppement des lignes, ainsi que le filtrage.

7.1

Tri et pagination

Les deux oprations les plus frquentes effectues sur les donnes tabulaires sont le tri et la pagination. Lorsque la table est grande, les informations doivent pouvoir tre rorganises. Malheureusement, ces oprations ne sont pas les plus simples mettre en uvre. Nous allons commencer par examiner le tri dune table, en rordonnant des donnes pour quelles soient plus utiles la personne qui les consulte. Tri ct serveur Pour trier des donnes, une solution classique consiste effectuer cette opration sur le serveur. Les donnes prsentes dans des tables proviennent souvent dune base de donnes et le code qui les extrait de la base peut donc les demander dans un certain ordre, par exemple en utilisant la clause ORDER BY du langage SQL. Lorsque lon a accs au code ct serveur, il est trs facile de choisir un ordre de tri initial raisonnable. Toutefois, le tri est plus utile lorsque linternaute peut prciser lui-mme lordre. Une solution trs utilise se fonde sur des liens pour les en-ttes de la table (<th>) qui reprsentent des colonnes pouvant servir au tri. Ces liens ciblent la page courante, mais ajoutent une chane de requte indiquant la colonne qui sert de base au tri :
<table id="mes-donnees"> <thead> <tr> <th class="nom"> <a href="index.php?tri=nom">Nom</a> </th> <th class="date"> <a href="index.php?tri=date">Date</a> </th> </tr> </thead> <tbody> ... </tbody> </table>

Chapitre 7

Manipulation des tables

147

Le serveur analyse le paramtre de la chane de requte et lutilise pour retourner le contenu de la base de donnes dans lordre appropri. viter les actualisations de la page Cette solution est simple, mais la page doit tre actualise chaque opration de tri. Nous lavons vu, jQuery nous permet de supprimer ces actualisations en utilisant des mthodes AJAX. Si les en-ttes de colonnes sont des liens, nous pouvons ajouter du code jQuery pour remplacer ces liens par des requtes AJAX :
$(document).ready(function() { $('#mes-donnees th a').click(function() { $('#mes-donnees tbody').load($(this).attr('href')); return false; }); });

Suite au clic sur un lien, jQuery envoie une requte AJAX au serveur qui cible la mme page. Lors dune requte AJAX, jQuery fixe len-tte HTTP X-Requested-With la valeur XMLHttpRequest afin que le serveur puisse savoir que cette requte est de type AJAX. Le code du serveur peut alors tre crit de manire renvoyer uniquement le contenu de llment <tbody>, sans le balisage HTML qui lentoure. Nous pouvons ainsi rcuprer la rponse et linsrer la place de llment <tbody> existant. Il sagit l dun exemple damlioration progressive. Avec les liens pour le tri ct serveur, la page fonctionne parfaitement sans impliquer un quelconque code JavaScript. Toutefois, lorsque JavaScript est disponible, AJAX dtourne la requte de la page et permet deffectuer un tri sans avoir recharger lintgralit de la page. Tri en JavaScript Toutefois, il arrive que nous ne souhaitions pas attendre que le serveur retourne les donnes tries ou que nous ne disposions pas dun langage de script ct serveur. Dans ce cas, une alternative possible consiste raliser le tri dans le navigateur en utilisant un script JavaScript ct client. Par exemple, supposons quune table recense des livres, avec les noms des auteurs, les dates de publication et les prix (voir Figure 7.1) :
<table class="sortable"> <thead> <tr> <th></th> <th>Titre</th> <th>Auteur(s)</th> <th>Date</th> <th>Prix</th> </tr> </thead>

148

jQuery

<tbody> <tr> <td><img src="../images/covers/small/1847192386.png" width="49" height="61" alt="Building Websites with Joomla! 1.5 Beta 1" /> </td> <td>Building Websites with Joomla! 1.5 Beta 1</td> <td>Hagen Graf</td> <td>Fvr 2007</td> <td>40,49 </td> </tr> <tr> <td><img src="../images/covers/small/1904811620.png" width="49" height="61" alt="Learning Mambo: A Step-by-Step Tutorial to Building Your Website" /> </td> <td>Learning Mambo: A Step-by-Step Tutorial to Building Your Website </td> <td>Douglas Paterson</td> <td>Dc 2006</td> <td>40,49 </td> </tr> </tbody> </table>

Figure 7.1
Une liste de livres affiche dans une table.

Nous voudrions transformer les en-ttes du tableau en boutons de manire pouvoir trier les ouvrages en fonction des colonnes correspondantes. Voyons comment procder.

Chapitre 7

Manipulation des tables

149

Groupes de lignes Vous aurez remarqu que nous utilisons les balises <thead> et <tbody> pour sparer les donnes en groupes de lignes. De nombreux dveloppeurs HTML omettent ces balises, mais elles peuvent se rvler utiles pour constituer des slecteurs CSS plus pratiques. Par exemple, supposons que nous souhaitions appliquer alternativement des bandes la table, en vitant les lignes den-tte :
$(document).ready(function() { $('table.sortable tbody tr:odd').addClass('odd'); $('table.sortable tbody tr:even').addClass('even'); });

Ce code permet dalterner les couleurs appliques aux lignes de la table, sans toucher len-tte (voir Figure 7.2).

Figure 7.2
Application de bandes aux lignes de la table, sans toucher len-tte.

Grce ces balises de regroupement, nous pourrons facilement slectionner et manipuler les lignes de donnes sans affecter len-tte. Tri alphabtique simple Nous allons prsent effectuer un tri fond sur la colonne TITRE de la table. Nous avons besoin dune classe pour la cellule den-tte afin de pouvoir la slectionner correctement :

150

jQuery

<thead> <tr> <th></th> <th class="sort-alpha">Titre</th> <th>Auteur(s)</th> <th>Date</th> <th>Prix</th> </tr> </thead>

Tri des tableaux en JavaScript Pour le tri, nous pouvons employer la mthode JavaScript .sort(). Elle effectue un tri en place du tableau et accepte une fonction de comparaison en argument. Cette fonction compare deux lments du tableau et doit retourner une valeur positive ou ngative selon llment qui doit tre plac en premier dans le tableau tri. Prenons par exemple un simple tableau de nombres :
var arr = [52, 97, 3, 62, 10, 63, 64, 1, 9, 3, 4];

Nous pouvons le trier en invoquant arr.sort(). Les lments se trouvent ensuite dans lordre suivant :
[1, 10, 3, 3, 4, 52, 62, 63, 64, 9, 97]

Vous le constatez, les lments sont, par dfaut, tris dans lordre lexicographique (par ordre alphabtique). Dans cet exemple, il serait plus cens de les trier numriquement. Pour cela, nous pouvons fournir une fonction de comparaison la mthode .sort() :
arr.sort(function(a,b) { if (a < b) return -1; if (a > b) return 1; return 0; });

Elle retourne une valeur ngative si a doit tre plac en premier dans le tableau tri, une valeur positive si b doit tre plac en premier, et 0 si lordre des lments na pas dimportance. La mthode .sort() peut ainsi classer les lments dans lordre appropri :
[1, 3, 3, 4, 9, 10, 52, 62, 63, 64, 97]

Utiliser un comparateur pour trier les lignes de la table Voici notre programme de tri initial :
$(document).ready(function() { $('table.sortable').each(function() { var $table = $(this); $('th', $table).each(function(column) { var $header = $(this);

Chapitre 7

Manipulation des tables

151

if ($header.is('.sort-alpha')) { $header.addClass('clickable').hover(function() { $header.addClass('hover'); }, function() { $header.removeClass('hover'); }).click(function() { var rows = $table.find('tbody > tr').get(); rows.sort(function(a, b) { var keyA = $(a).children('td').eq(column).text() .toUpperCase(); var keyB = $(b).children('td').eq(column).text() .toUpperCase(); if (keyA < keyB) return -1; if (keyA > keyB) return 1; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); }); }); } }); }); });

Tout dabord, notez que nous utilisons la mthode .each() pour que litration soit explicite. Nous pouvons lier un gestionnaire de click toutes les cellules den-tte de classe sort-alpha en invoquant simplement $('table.sortable th.sort-alpha'). click(), mais cette solution nous fait manquer un lment dinformation essentiel : lindice de la colonne qui correspond la cellule den-tte sur laquelle lutilisateur a cliqu. Puisque .each() passe lindice de litration sa fonction de rappel, nous pourrons nous en servir pour dterminer la cellule approprie dans chaque ligne de donnes. Aprs avoir identifi la cellule den-tte, nous rcuprons un tableau de toutes les lignes de donnes. Cet exemple montre parfaitement comment .get() permet de transformer un objet jQuery en un tableau de nuds du DOM ; mme si, par de nombreux aspects, les objets jQuery ressemblent des tableaux, ils ne disposent pas des mthodes des tableaux natifs, comme .sort(). Puisque nous disposons prsent dun tableau de nuds du DOM, nous pouvons les trier, mais il nous faut pour cela crire la fonction de comparaison adquate. Nous souhaitons trier les lignes conformment au contenu textuel des cellules. La fonction de comparaison examine donc ces informations. Nous savons quelle cellule utiliser pour le tri car nous avons mmoris lindice de la colonne dans lappel .each() englobant. Nous convertissons le texte en majuscules car les comparaisons de chanes en JavaScript sont sensibles la casse et nous voulons que le tri soit insensible la casse. Nous enregistrons les valeurs cls dans des variables pour viter les rptitions de code, nous effectuons la comparaison et nous retournons une valeur positive ou ngative conformment la rgle dcrite prcdemment.

152

jQuery

Enfin, nous parcourons les lignes du tableau tri et les rinsrons dans la table. Puisque la mthode .append() ne clone pas les nuds, ils sont dplacs au lieu dtre copis. La table est prsent trie. Voil donc un exemple de dgradation lgante. Dans lexemple, nous disposions dune solution qui ncessitait lactualisation de la page et nous lavons amliore en utilisant AJAX lorsque JavaScript est disponible. Dans le cas prsent, la technique de tri ne peut pas fonctionner si JavaScript nest pas activ ; nous supposons que le serveur ne propose aucun langage de script. La classe clickable est donc ajoute par le code, ce qui permet dobtenir une interface qui signale la possibilit de tri (par une image darrire-plan) uniquement lorsque le script peut sexcuter. Dans le cas contraire, la page reste toujours oprationnelle, mais dans une version dgrade, sans la fonctionnalit de tri. Puisque nous avons dplac les lignes, les bandes de couleurs ne sont plus appropries, comme lillustre la Figure 7.3. Nous devons donc rappliquer les couleurs aprs le tri. Pour cela, nous plaons le code de construction des bandes dans une fonction que nous invoquons lorsque cest ncessaire :

Figure 7.3
Les lignes dplaces conservent leur couleur darrire-plan. $(document).ready(function() { var alternateRowColors = function($table) { $('tbody tr:odd', $table) .removeClass('even').addClass('odd'); $('tbody tr:even', $table) .removeClass('odd').addClass('even'); };

Chapitre 7

Manipulation des tables

153

$('table.sortable').each(function() { var $table = $(this); alternateRowColors($table); $('th', $table).each(function(column) { var $header = $(this); if ($header.is('.sort-alpha')) { $header.addClass('clickable').hover(function() { $header.addClass('hover'); }, function() { $header.removeClass('hover'); }).click(function() { var rows = $table.find('tbody > tr').get(); rows.sort(function(a, b) { var keyA = $(a).children('td').eq(column).text() .toUpperCase(); var keyB = $(b).children('td').eq(column).text() .toUpperCase(); if (keyA < keyB) return -1; if (keyA > keyB) return 1; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); }); alternateRowColors($table); }); } }); }); });

La Figure 7.4 montre que les bandes sont nouveau correctement appliques.

Figure 7.4
Les bandes sont reconstruites aprs le tri.

154

jQuery

Puissance des plugins La fonction alternateRowColors() que nous venons dcrire constitue un bon candidat une conversion en plugin. En ralit, toute opration que nous souhaitons appliquer une collection dlments du DOM peut facilement devenir un plugin. Pour cela, nous devons modifier lgrement la fonction existante :
jQuery.fn.alternateRowColors = function() { $('tbody tr:odd', this) .removeClass('even').addClass('odd'); $('tbody tr:even', this) .removeClass('odd').addClass('even'); return this; };

Nous avons ralis trois changements importants :


n

Elle est dfinie comme une nouvelle proprit de jQuery.fn la place dune fonction autonome. Cette opration enregistre la fonction en tant que mthode de plugin. Nous utilisons le mot cl this la place du paramtre $table. Dans une mthode de plugin, this fait rfrence lobjet jQuery sur lequel la mthode est invoque. Enfin, la fonction retourne this. Lorsque la valeur de retour est lobjet jQuery, la nouvelle mthode peut tre chane.
INFO

Pour de plus amples informations concernant le dveloppement de plugins jQuery, consultez le Chapitre 11. Il dcrit la cration dun plugin prt tre propos la communaut, contrairement au petit exemple donn ici qui nest destin qu notre propre code.

Avec notre nouveau plugin, nous pouvons remplacer alternateRowColors($table) par $table.alternateRowColors(), qui est une instruction jQuery plus naturelle. Performances Notre code fonctionne, mais il est relativement lent. Lorigine de ces performances mdiocres se trouve dans la fonction de comparaison, qui effectue une quantit de travail relativement importante et est invoque plusieurs fois au cours du tri. Autrement dit, le temps superflu pass dans le traitement est amplifi. Lalgorithme de tri utilis par JavaScript nest pas dfini par la norme. Il peut sagir dun tri simple, comme le tri par permutation (de complexit en O(n), au pire), ou dune mthode sophistique, comme le tri rapide (en O(n log n) en moyenne). Toutefois, nous pouvons affirmer que la multiplication par deux du nombre dlments dans

Chapitre 7

Manipulation des tables

155

un tableau fera plus que doubler la dure totale dexcution de la fonction de comparaison. Le remde la lenteur de notre comparateur rside dans un prcalcul des cls de la comparaison. Nous partons de notre fonction de tri actuelle :
rows.sort(function(a, b) { var keyA = $(a).children('td').eq(column).text() .toUpperCase(); var keyB = $(b).children('td').eq(column).text() .toUpperCase(); if (keyA < keyB) return -1; if (keyA > keyB) return 1; return 0; });

Nous extrayons le calcul des cls dans une boucle spare :


$.each(rows, function(index, row) { row.sortKey = $(row).children('td').eq(column) .text().toUpperCase(); }); rows.sort(function(a, b) { if (a.sortKey < b.sortKey) return -1; if (a.sortKey > b.sortKey) return 1; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); row.sortKey = null; });

Dans la nouvelle boucle, nous ralisons tout le travail coteux et enregistrons le rsultat dans une nouvelle proprit .sortKey. Ce type de proprit, associe un lment du DOM, sans tre un attribut normal du DOM, sappelle expando. Lemplacement denregistrement choisi est trs pratique, car nous avons besoin dune cl pour chaque ligne de la table. Nous pouvons ensuite examiner cet attribut dans la fonction de comparaison et obtenir un tri beaucoup plus rapide.
INFO
Nous fixons la proprit expando null aprs en avoir termin : nous faisons le mnage aprs notre passage. Si ce nest pas strictement ncessaire dans ce cas, il faut prendre cette bonne habitude car les proprits expando qui tranent peuvent provoquer des fuites de mmoire. Pour de plus amples informations, consultez lAnnexe C.

la place des proprits expando, nous pouvons opter pour le mcanisme de stockage des donnes propos par jQuery. La mthode .data() enregistre ou rcupre des informations quelconques associes des lments de la page, tandis que la mthode .removeData() supprime de telles informations stockes :

156

jQuery

$.each(rows, function(index, row) { $(row).data('sortKey', $(row).children('td') .eq(column).text().toUpperCase()); }); rows.sort(function(a, b) { if ($(a).data('sortKey') < $(b).data('sortKey')) return -1; if ($(a).data('sortKey') > $(b).data('sortKey')) return 1; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); $(row).removeData('sortKey'); });

Lutilisation de .data() la place des proprits expando peut, parfois, se rvler plus pratique, car nous manipulons plus souvent des objets jQuery que directement des nuds du DOM. Elle permet galement dviter les problmes potentiels lis aux fuites de mmoire dans Internet Explorer. Toutefois, pour la suite de ce chapitre, nous conservons les proprits expando de manire mettre en pratique le passage entre les oprations sur les nuds du DOM et celles sur les objets jQuery. Dterminer les cls de tri Nous voulons prsent appliquer le mme type de tri la colonne AUTEUR(S) de la table. En ajoutant la classe sort-alpha la cellule den-tte de cette colonne, nous pouvons utiliser notre code existant. Dans lidal, les auteurs doivent tre tris en fonction de leur nom de famille, non de leur prnom. Puisque certains des ouvrages sont rdigs par plusieurs auteurs et que quelques auteurs ont des deuximes prnoms ou des initiales, nous avons besoin dune aide extrieure pour dterminer la partie du texte qui servira de cl au tri. Nous pouvons fournir cette aide en plaant la partie pertinente de la cellule dans une balise :
<tr> <td><img src="../images/covers/small/1847192386.png" width="49" height="61" alt="Building Websites with Joomla! 1.5 Beta 1" /> </td> <td>Building Websites with Joomla! 1.5 Beta 1</td> <td>Hagen <span class="sort-key">Graf</span></td> <td>Fvr 2007</td> <td>40,49 </td> </tr> <tr> <td><img src="../images/covers/small/1904811620.png" width="49" height="61" alt="Learning Mambo: A Step-by-Step Tutorial to Building Your Website" /> </td> <td>Learning Mambo: A Step-by-Step Tutorial to Building Your Website</td> <td>Douglas <span class="sort-key">Paterson</span></td> <td>Dc 2006</td> <td>40,49 </td> </tr>

Chapitre 7

Manipulation des tables

157

Nous modifions ensuite notre code de tri pour prendre en compte cette balise, sans perturber le comportement existant pour la colonne TITRE qui est correct. En concatnant la cl de tri marque avant la cl qui a dj t calcule, nous pouvons effectuer le tri sur le nom de famille lorsquil est repr, ou sur lintgralit de la chane dans le cas contraire :
$.each(rows, function(index, row) { var $cell = $(row).children('td').eq(column); row.sortKey = $cell.find('.sort-key').text().toUpperCase() + ' ' + $cell.text().toUpperCase(); });

Le tri fond sur la colonne AUTEUR(S) utilise prsent la cl fournie et se fait sur le nom de famille (voir Figure 7.5). Si les noms de famille sont identiques, lintgralit de la chane permet de les dpartager.

Figure 7.5
Tri de la table selon le nom de famille de lauteur.

Trier dautres types de donnes Lutilisateur doit pouvoir trier la table non seulement selon les colonnes TITRE et AUTEUR(S), mais galement selon les colonnes DATE et PRIX. Puisque nous avons gnralis notre fonction de comparaison, elle peut prendre en charge dautres types de donnes, mais les cls calcules doivent tout dabord tre accordes ces types. Par exemple, dans le cas du prix, nous devons convertir la chane en valeur numrique, mais il faut tout dabord prparer la chane de caractres :

158

jQuery

var key = parseFloat($cell.text().replace(/[\D,]/g, '')); row.sortKey = isNaN(key) ? 0 : key;

Lexpression rgulire retire tous les caractres non numriques, ainsi que la virgule. Le rsultat est pass la fonction parseFloat(). La valeur retourne par parseFloat() doit ensuite tre vrifie, car, si aucun nombre na pu tre obtenu partir du texte, elle vaut NaN (non un nombre). Une telle valeur pourrait perturber le comportement de .sort(). Pour les cellules comportant une date, la procdure est plus complexe. Elle se fonde sur lobjet JavaScript Date, mais sa mthode parse() ne comprend que les dates au format IETF. Nous devons donc commencer par convertir les noms de mois en anglais, ce que nous faisons avec une mappe :
var monthFRtoEN = { 'janv': 'Jan', 'fvr': 'Feb', 'mars': 'Mar', 'avr': 'Apr', 'mai': 'May', 'juin': 'Jun', 'juil': 'Jul', 'aot': 'Aug', 'sept': 'Sep', 'oct': 'Oct', 'nov': 'Nov', 'dc': 'Dec' }; var monthFR = $cell.text().replace(/[^a-z]/gi, ''); var dateEN = $cell.text().replace(monthFR, monthFRtoEN[monthFR.toLowerCase()]); row.sortKey = Date.parse('1 ' + dateEN);

La premire expression rgulire permet dextraire uniquement le nom du mois du texte de la cellule. La deuxime expression rgulire remplace dans le texte de la cellule le nom du mois en franais par le nom du mois anglais, qui est obtenu en consultant la mappe de conversion. La variable dateEN contient ainsi la date en anglais. Dans la table, les dates prcisent uniquement le mois et lanne. Puisque Date.parse() a besoin dune date complte, nous ajoutons 1 au dbut de la chane. Nous compltons ainsi le mois et lanne par un jour, puis la combinaison est convertie en un nombre de millisecondes compatible avec notre fonction de comparaison. Nous pouvons distribuer ces expressions dans des fonctions spares et invoquer la fonction approprie selon la classe de len-tte de la table :
jQuery.fn.alternateRowColors = function() { $('tbody tr:odd', this) .removeClass('even').addClass('odd'); $('tbody tr:even', this) .removeClass('odd').addClass('even'); return this; }; $(document).ready(function() { $('table.sortable').each(function() { var $table = $(this); $table.alternateRowColors(); $('th', $table).each(function(column) { var $header = $(this); var findSortKey; if ($header.is('.sort-alpha')) {

Chapitre 7

Manipulation des tables

159

findSortKey = function($cell) { return $cell.find('.sort-key') .text().toUpperCase() + ' ' + $cell.text().toUpperCase(); }; } else if ($header.is('.sort-numeric')) { findSortKey = function($cell) { var key = $cell.text().replace(/[\D,]/g, ''); key = parseFloat(key); return isNaN(key) ? 0 : key; }; } else if ($header.is('.sort-date')) { findSortKey = function($cell) { var monthFRtoEN = { 'janv': 'Jan', 'fvr': 'Feb', 'mars': 'Mar', 'avr': 'Apr', 'mai': 'May', 'juin': 'Jun', 'juil': 'Jul', 'aot': 'Aug', 'sept': 'Sep', 'oct': 'Oct', 'nov': 'Nov', 'dc': 'Dec' }; var monthFR = $cell.text().replace(/[^a-z]/gi, ''); var dateEN = $cell.text().replace(monthFR, monthFRtoEN[monthFR.toLowerCase()]); return Date.parse('1 ' + dateEN); }; } if (findSortKey) { $header.addClass('clickable').hover(function() { $header.addClass('hover'); }, function() { $header.removeClass('hover'); }).click(function() { var rows = $table.find('tbody > tr').get(); $.each(rows, function(index, row) { var $cell = $(row).children('td').eq(column); row.sortKey = findSortKey($cell); }); rows.sort(function(a, b) { if (a.sortKey < b.sortKey) return -1; if (a.sortKey > b.sortKey) return 1; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); row.sortKey = null; }); $table.alternateRowColors(); }); } }); }); });

La variable findSortKey sert la fois de fonction de calcul de la cl et dindicateur signalant si len-tte de la colonne est marqu par une classe lui permettant dtre utilise pour le tri. Nous pouvons prsent trier la table en fonction de la date (voir Figure 7.6) ou du prix (voir Figure 7.7).

160

jQuery

Figure 7.6
Tri de la table en fonction de la date de publication.

Figure 7.7
Tri de la table en fonction du prix du livre.

Mettre en exergue une colonne Nous souhaitons amliorer linterface utilisateur en lui rappelant visuellement laction quil a effectue. En mettant en exergue la colonne qui a t utilise pour le tri, nous pouvons attirer son attention sur la partie de la table qui lintresse certainement. Puis-

Chapitre 7

Manipulation des tables

161

que nous savons dj comment slectionner les cellules de la colonne de la table, lapplication dune classe ces cellules ne pose aucune difficult :
$table.find('td').removeClass('sorted') .filter(':nth-child(' + (column + 1) + ')') .addClass('sorted');

Ce bout de code commence par retirer la classe sorted toutes les cellules, puis lajoute celles qui se trouvent dans la colonne utilise pour le tri. Nous devons ajouter 1 lindice de colonne obtenu prcdemment, car le slecteur :nth-child() compte partir de un, non de zro. Avec ce code en place, nous obtenons la mise en exergue de la colonne aprs une opration de tri (voir Figure 7.8).

Figure 7.8
La colonne qui a servi au tri est surligne.

Inverser le sens du tri Comme dernire amlioration, nous allons permettre le choix entre un tri croissant et un tri dcroissant. Lorsque lutilisateur clique sur une colonne qui est dj trie, nous voulons inverser le sens du tri. Pour cela, nous changeons simplement les valeurs retournes par la fonction de comparaison en utilisant une variable :
if (a.sortKey < b.sortKey) return -sortDirection; if (a.sortKey > b.sortKey) return sortDirection;

Lorsque sortDirection est gale 1, le tri se fait comme prcdemment. Lorsquelle est gale -1, le tri est invers. Nous pouvons utiliser des classes pour suivre la direction du tri dune colonne :

162

jQuery

jQuery.fn.alternateRowColors = function() { $('tbody tr:odd', this) .removeClass('even').addClass('odd'); $('tbody tr:even', this) .removeClass('odd').addClass('even'); return this; }; $(document).ready(function() { $('table.sortable').each(function() { var $table = $(this); $table.alternateRowColors(); $('th', $table).each(function(column) { var $header = $(this); var findSortKey; if ($header.is('.sort-alpha')) { findSortKey = function($cell) { return $cell.find('.sort-key') .text().toUpperCase() + ' ' + $cell.text().toUpperCase(); }; } else if ($header.is('.sort-numeric')) { findSortKey = function($cell) { var key = $cell.text().replace(/[\D,]/g, ''); key = parseFloat(key); return isNaN(key) ? 0 : key; }; } else if ($header.is('.sort-date')) { findSortKey = function($cell) { var monthFRtoEN = { 'janv': 'Jan', 'fvr': 'Feb', 'mars': 'Mar', 'avr': 'Apr', 'mai': 'May', 'juin': 'Jun', 'juil': 'Jul', 'aot': 'Aug', 'sept': 'Sep', 'oct': 'Oct', 'nov': 'Nov', 'dc': 'Dec' }; var monthFR = $cell.text().replace(/[^a-z]/gi, ''); var dateEN = $cell.text().replace(monthFR, monthFRtoEN[monthFR.toLowerCase()]); return Date.parse('1 ' + dateEN); }; } if (findSortKey) { $header.addClass('clickable').hover(function() { $header.addClass('hover'); }, function() { $header.removeClass('hover'); }).click(function() { var sortDirection = 1; if ($header.is('.sorted-asc')) { sortDirection = -1; } var rows = $table.find('tbody > tr').get(); $.each(rows, function(index, row) { var $cell = $(row).children('td').eq(column); row.sortKey = findSortKey($cell); }); rows.sort(function(a, b) {

Chapitre 7

Manipulation des tables

163

if (a.sortKey < b.sortKey) return -sortDirection; if (a.sortKey > b.sortKey) return sortDirection; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); row.sortKey = null; }); $table.find('th').removeClass('sorted-asc') .removeClass('sorted-desc'); if (sortDirection == 1) { $header.addClass('sorted-asc'); } else { $header.addClass('sorted-desc'); } $table.find('td').removeClass('sorted') .filter(':nth-child(' + (column + 1) + ')') .addClass('sorted'); $table.alternateRowColors(); }); } }); }); });

Puisque nous utilisons des classes pour mmoriser le sens du tri, nous pouvons en profiter pour appliquer un style len-tte de la colonne de manire indiquer ce sens (voir Figure 7.9).

Figure 7.9
Len-tte de la colonne indique le sens du tri, croissant ou dcroissant.

164

jQuery

Pagination ct serveur Le tri est une bonne mthode pour se frayer un chemin parmi de grandes quantits de donnes et rechercher des informations. Mais nous pouvons galement aider lutilisateur se focaliser sur une portion dun grand ensemble de donnes en utilisant la pagination. linstar du tri, la pagination seffectue souvent sur le serveur. Si les donnes afficher sont enregistres dans une base de donnes, il est trs facile dextraire une page dinformations la fois en utilisant la clause LIMIT de MySQL, ROWNUM dOracle ou toute mthode quivalente propose par le moteur de base de donnes. Notre premier exemple de tri se fondait sur des informations envoyes au serveur par lintermdiaire de la chane de requte. Cest une technique que nous pouvons galement employer pour demander au serveur deffectuer la pagination, par exemple avec index.php?page=52. De la mme manire que prcdemment, cette opration peut se faire avec un chargement total de la page ou en utilisant AJAX pour rcuprer uniquement une partie de la table. Cette stratgie ne dpend pas du navigateur et peut prendre en charge de grands ensembles de donnes. Combiner le tri et la pagination Les donnes qui sont suffisamment nombreuses pour bnficier du tri ont galement de grandes chances de pouvoir tirer profit de la pagination. Il nest pas inhabituel de penser combiner ces deux techniques pour afficher des donnes. Cependant, puisquelles affectent toutes deux la collection de donnes prsente sur la page, il est important dexaminer leurs interactions lors de la mise en uvre. Le tri et la pagination peuvent se faire sur le serveur ou dans le navigateur web. Toutefois, les stratgies mises en place pour ces deux oprations doivent tre synchronises ou nous risquons dobtenir un comportement droutant. Supposons, par exemple, que nous ayons une table de huit lignes et deux colonnes, trie initialement sur la premire colonne. Si le tri se fait ensuite sur la deuxime colonne, de nombreuses lignes peuvent changer de position (voir Figure 7.10). Voyons prsent ce qui se passe si nous ajoutons la pagination. Supposons que le serveur ne fournisse que les quatre premires lignes et que le navigateur trie les donnes. Si la pagination est ralise par le serveur et le tri, par le navigateur, la procdure de tri na pas accs lensemble des donnes et les rsultats sont incorrects (voir Figure 7.11). Seules les donnes prsentes sur la page peuvent tre manipules par le code JavaScript. Pour viter que cela ne devienne un problme, les deux oprations doivent tre ralises sur le serveur (demander au serveur les donnes appropries pour chaque page

Chapitre 7

Manipulation des tables

165

Figure 7.10
Selon la colonne de tri, les lignes changent de position.

A B C D E F G H Avant

4 5 2 7 1 8 3 6

E C G A B H D F Aprs

1 2 3 4 5 6 7 8

Figure 7.11
La pagination sur le serveur suivie du tri dans le navigateur donne des rsultats incorrects.

A B C D Avant

4 5 2 7

C A B D Aprs

2 4 5 7

Figure 7.12
Les deux oprations effectues au mme endroit donnent des rsultats corrects.

A B C D Avant

4 5 2 7

E C G A Aprs

1 2 3 4

ou chaque opration de tri) ou dans le navigateur (toutes les donnes doivent tre accessibles en permanence au code JavaScript) pour que les premiers rsultats affichs correspondent aux premires lignes du jeu de donnes (voir Figure 7.12). Pagination en JavaScript Voyons donc comment ajouter la pagination JavaScript la table qui peut dj tre trie dans le navigateur. Tout dabord, concentrons-nous sur laffichage dune page de donnes, sans tenir compte, pour le moment, de laction de lutilisateur :
$(document).ready(function() { $('table.paginated').each(function() { var currentPage = 0; var numPerPage = 10; var $table = $(this); $table.find('tbody tr').hide() .slice(currentPage * numPerPage, (currentPage + 1) * numPerPage) .show(); }); });

166

jQuery

Ce code affiche la premire page, avec dix lignes de donnes. Une fois encore, nous nous appuyons sur la prsence dun lment <tbody> pour distinguer les donnes et les en-ttes ; nous ne voulons pas que les informations den-tte ou de pied disparaissent lors du passage la page suivante. Pour slectionner les lignes qui contiennent des donnes, nous masquons toutes les premires lignes, puis slectionnons les lignes sur la page en cours et les affichons. La mthode .slice() assure la mme fonction que la mthode ponyme de lobjet JavaScript Array : elle rduit la slection aux lments qui se trouvent entre les deux positions indiques. Le point le plus complexe de ce code rside dans les expressions donnes au filtre .slice(). Nous devons dterminer les indices des lignes qui correspondent au dbut et la fin de la page en cours. Pour la premire ligne, nous multiplions simplement le numro de la page courante par le nombre de lignes sur chaque page. En multipliant le nombre de lignes par le numro de la page courante plus un, nous obtenons la premire ligne de la page suivante. Ces valeurs conviennent parfaitement car la mthode .slice() rcupre les lignes jusqu celle indique par le second paramtre, sans linclure. Afficher le slecteur de page Pour que lutilisateur puisse interagir avec le systme de pagination, nous devons placer un slecteur de page ct de la table. Il sagit dun ensemble de liens permettant de naviguer vers les diffrentes pages de donnes. Pour cela, nous pourrions simplement ajouter les liens correspondant aux pages dans le document HTML, mais cela irait lencontre du principe damlioration progressive que nous voulons respecter. la place, nous devons ajouter les liens en utilisant du code JavaScript, ce qui vitera aux utilisateurs qui nont pas activ les fonctionnalits de script dtre induits en erreur par des liens non oprationnels. Pour afficher les liens, nous calculons le nombre de pages et crons le nombre dlments du DOM quivalents :
var numRows = $table.find('tbody tr').length; var numPages = Math.ceil(numRows / numPerPage); var $pager = $('<div class="pager"></div>'); for (var page = 0; page < numPages; page++) { $('<span class="page-number">' + (page + 1) + '</span>') .appendTo($pager).addClass('clickable'); } $pager.insertBefore($table);

Le nombre de pages est obtenu en divisant le nombre de lignes de donnes par le nombre dlments affichs sur chaque page. Puisque la division peut ne pas tre entire, nous arrondissons le rsultat avec Math.ceil() de manire que la dernire page

Chapitre 7

Manipulation des tables

167

partielle soit accessible. Nous utilisons ensuite cette valeur pour crer des boutons pour chaque page et positionnons le nouveau slecteur de page au-dessus de la table (voir Figure 7.13).

Figure 7.13
Le slecteur de page est plac au-dessus de la table.

Activer les boutons du slecteur de page Pour que les nouveaux boutons soient oprationnels, nous devons mettre jour la variable currentPage et lancer ensuite notre procdure de pagination. premire vue, nous pouvons fixer currentPage la valeur de page, qui correspond la valeur courante de litrateur qui cre les boutons :
$(document).ready(function() { $('table.paginated').each(function() { var currentPage = 0; var numPerPage = 10; var $table = $(this); var repaginate = function() { $table.find('tbody tr').hide() .slice(currentPage * numPerPage, (currentPage + 1) * numPerPage) .show(); }; var numRows = $table.find('tbody tr').length; var numPages = Math.ceil(numRows / numPerPage); var $pager = $('<div class="pager"></div>'); for (var page = 0; page < numPages; page++) { $('<span class="page-number"></span>').text(page + 1) .click(function() { currentPage = page; repaginate(); }).appendTo($pager).addClass('clickable'); } $pager.insertBefore($table); }); });

168

jQuery

Cela fonctionne, car la nouvelle fonction repaginate() est invoque lors du chargement de la page et lors dun clic sur un lien de page. Cependant, tous les liens affichent une table vide (voir Figure 7.14).

Figure 7.14
Les liens du slecteur fonctionnent, mais affichent une table vide.

Le problme vient de la dfinition du gestionnaire de click, qui cre une fermeture. Ce gestionnaire fait rfrence la variable page dfinie en dehors de la fonction. Lors de la modification de la variable au tour de boucle suivant, les gestionnaires de click que nous avons dj configurs pour les boutons prcdents sont galement affects. Par consquent, si le slecteur prsente sept boutons, chacun cible la page 8, cest--dire la valeur finale de page la fin de la boucle.
INFO
Pour de plus amples informations concernant les fermetures, consultez lAnnexe C.

Pour rsoudre ce problme, nous allons employer lune des fonctionnalits les plus labores des mthodes jQuery de liaison des vnements. Nous pouvons ajouter des donnes dvnement personnalises au gestionnaire au moment de sa liaison et elles seront disponibles lors de son invocation :
$('<span class="page-number"></span>').text(page + 1) .bind('click', {newPage: page}, function(event) { currentPage = event.data['newPage']; repaginate(); }).appendTo($pager).addClass('clickable');

Le nouveau numro de page est pass au gestionnaire par lintermdiaire de la proprit data de lvnement. Ainsi, ce numro chappe aux hasards de la fermeture et conserve la valeur quil avait au moment de la liaison du gestionnaire. prsent, les liens du slecteur conduisent aux diffrentes pages (voir Figure 7.15).

Chapitre 7

Manipulation des tables

169

Figure 7.15
Le deuxime bouton mne la deuxime page.

Signaler la page actuelle Le slecteur de page serait plus convivial si le numro de la page en cours tait mis en exergue. Pour cela, il suffit de modifier la classe des boutons lors de chaque clic :
$(document).ready(function() { $('table.paginated').each(function() { var currentPage = 0; var numPerPage = 10; var $table = $(this); var repaginate = function() { $table.find('tbody tr').hide() .slice(currentPage * numPerPage, (currentPage + 1) * numPerPage) .show(); }; var numRows = $table.find('tbody tr').length; var numPages = Math.ceil(numRows / numPerPage); var $pager = $('<div class="pager"></div>'); for (var page = 0; page < numPages; page++) { $('<span class="page-number"></span>').text(page + 1) .bind('click', {newPage: page}, function(event) { currentPage = event.data['newPage']; repaginate(); $(this).addClass('active').siblings().removeClass('active'); }).appendTo($pager).addClass('clickable'); } $pager.insertBefore($table) .find('span.page-number:first').addClass('active'); }); });

170

jQuery

Comme le montre la Figure 7.16, la page actuellement slectionne est signale.

Figure 7.16
Le slecteur indique la page en cours.

Pagination avec tri Nous avons dbut cette section en indiquant que les procdures de tri et de pagination devaient tenir compte lune de lautre de manire viter un rsultat droutant. Puisque notre slecteur de page est oprationnel, nous devons faire en sorte que les oprations de tri respectent la slection courante de la page. Pour cela, il suffit dinvoquer la fonction repaginate() ds quun tri a t ralis. Toutefois, la porte de la fonction pose problme. Nous ne pouvons pas appeler repaginate() partir de notre procdure de tri car elle se trouve dans un autre gestionnaire $(document).ready(). Nous pourrions simplement runir les deux parties de code, mais nous allons tre plus malins. Nous dcouplons les comportements de manire que la procdure de tri demande une pagination si ce comportement existe, et lignore sinon. Pour y parvenir, nous utiliserons un gestionnaire dvnements personnalis. Lors de notre tude prcdente de la gestion des vnements, nous nous sommes borns aux vnements qui sont dclenchs par le navigateur web, comme click et mouseup. Cependant, les mthodes .bind() et .trigger() peuvent galement prendre en charge dautres vnements : nous pouvons prciser nimporte quelle chane de caractres comme nom dvnement. Grce cette possibilit, nous dfinissons un nouvel vnement nomm repaginate pour remplacer la fonction que nous appelions :

Chapitre 7

Manipulation des tables

171

$table.bind('repaginate', function() { $table.find('tbody tr').hide() .slice(currentPage * numPerPage, (currentPage + 1) * numPerPage).show(); });

Linvocation de repaginate() est ensuite remplace par lappel suivant :


$table.trigger('repaginate');

Nous pouvons galement utiliser ce type dappel pour le tri. Il naura aucun effet si la table ne propose pas de slecteur de page, ce qui nous permet dassocier les deux possibilits comme bon nous semble. Version finale du code Voici la version finale du code qui met en uvre le tri et la pagination :
jQuery.fn.alternateRowColors = function() { $('tbody tr:odd', this).removeClass('even').addClass('odd'); $('tbody tr:even', this).removeClass('odd').addClass('even'); return this; }; $(document).ready(function() { $('table.sortable').each(function() { var $table = $(this); $table.alternateRowColors(); $('th', $table).each(function(column) { var $header = $(this); var findSortKey; if ($header.is('.sort-alpha')) { findSortKey = function($cell) { return $cell.find('.sort-key').text().toUpperCase() + ' ' + $cell.text().toUpperCase(); }; } else if ($header.is('.sort-numeric')) { findSortKey = function($cell) { var key = $cell.text().replace(/[\D,]/g, ''); key = parseFloat(key); return isNaN(key) ? 0 : key; }; } else if ($header.is('.sort-date')) { findSortKey = function($cell) { var monthFRtoEN = { 'janv': 'Jan', 'fvr': 'Feb', 'mars': 'Mar', 'avr': 'Apr', 'mai': 'May', 'juin': 'Jun', 'juil': 'Jul', 'aot': 'Aug', 'sept': 'Sep', 'oct': 'Oct', 'nov': 'Nov', 'dc': 'Dec' }; var monthFR = $cell.text().replace(/[^a-z]/gi, ''); var dateEN = $cell.text().replace(monthFR, monthFRtoEN[monthFR.toLowerCase()]); return Date.parse('1 ' + dateEN); }; }

172

jQuery

if (findSortKey) { $header.addClass('clickable').hover(function() { $header.addClass('hover'); }, function() { $header.removeClass('hover'); }).click(function() { var sortDirection = 1; if ($header.is('.sorted-asc')) { sortDirection = -1; } var rows = $table.find('tbody > tr').get(); $.each(rows, function(index, row) { var $cell = $(row).children('td').eq(column); row.sortKey = findSortKey($cell); }); rows.sort(function(a, b) { if (a.sortKey < b.sortKey) return -sortDirection; if (a.sortKey > b.sortKey) return sortDirection; return 0; }); $.each(rows, function(index, row) { $table.children('tbody').append(row); row.sortKey = null; }); $table.find('th').removeClass('sorted-asc') .removeClass('sorted-desc'); if (sortDirection == 1) { $header.addClass('sorted-asc'); } else { $header.addClass('sorted-desc'); } $table.find('td').removeClass('sorted') .filter(':nth-child(' + (column + 1) + ')') .addClass('sorted'); $table.alternateRowColors(); $table.trigger('repaginate'); }); } }); }); }); $(document).ready(function() { $('table.paginated').each(function() { var currentPage = 0; var numPerPage = 10; var $table = $(this); $table.bind('repaginate', function() { $table.find('tbody tr').hide() .slice(currentPage * numPerPage, (currentPage + 1) * numPerPage) .show(); }); var numRows = $table.find('tbody tr').length; var numPages = Math.ceil(numRows / numPerPage);

Chapitre 7

Manipulation des tables

173

var $pager = $('<div class="pager"></div>'); for (var page = 0; page < numPages; page++) { $('<span class="page-number"></span>').text(page + 1) .bind('click', {newPage: page}, function(event) { currentPage = event.data['newPage']; $table.trigger('repaginate'); $(this).addClass('active') .siblings().removeClass('active'); }).appendTo($pager).addClass('clickable'); } $pager.insertBefore($table) .find('span.page-number:first').addClass('active'); }); });

7.2

Modifier laspect de la table

Nous avons examin plusieurs manires dordonner les lignes de donnes dune table pour que lutilisateur puisse trouver facilement les informations quil recherche. Toutefois, il arrive souvent que mme aprs un tri ou une pagination la quantit de donnes examiner soit encore trs importante. Nous pouvons aider lutilisateur en manipulant non seulement lordre et la quantit de lignes affiches, mais galement leur prsentation. Mettre une ligne en exergue Pour orienter le regard de lutilisateur, une solution pratique consiste mettre en exergue des lignes, ce qui donne une indication visuelle sur limportance des donnes. Afin dexaminer quelques stratgies de mise en exergue, nous avons besoin dune table adapte. Cette fois, nous prenons une table contenant des lments dactualit. Elle est un peu plus complexe que la prcdente, car certaines lignes correspondent des soustitres, en plus des titres principaux. Voici la structure HTML correspondante :
<table> <thead> <tr> <th>Date</th> <th>Intitul</th> <th>Auteur</th> <th>Sujet</th> </tr> </thead> <tbody> <tr> <th colspan="4">2008</th> </tr> <tr> <td>28 sept</td> <td>jQuery, Microsoft, and Nokia</td> <td>John Resig</td> <td>tierce partie</td> </tr> ...

174

jQuery

<tr> <td>15 janv</td> <td>jQuery 1.2.2: 2nd Birthday Present</td> <td>John Resig</td> <td>sortie</td> </tr> </tbody> <tbody> <tr> <th colspan="4">2007</th> </tr> <tr> <td>8 dc</td> <td>jQuery Plugins site updated</td> <td>Mike Hostetler</td> <td>annonce</td> </tr> ... <tr> <td>11 janv</td> <td>Selector Speeds</td> <td>John Resig</td> <td>source</td> </tr> </tbody> ... </table>

Notez les multiples sections <tbody>. Ce balisage HTML valide permet de regrouper des lignes. Nous avons plac les sous-titres de section dans ces groupes, en utilisant des lments <th> pour les sparer. La Figure 7.17 montre laspect de cette table, aprs avoir dfini quelques styles CSS de base. Effet de bande sur les lignes Nous avons dj vu un exemple simple de mise en exergue des lignes dans ce chapitre, ainsi quau Chapitre 2. Leffet de bande est souvent utilis pour orienter prcisment le regard de lutilisateur sur diffrentes colonnes. Pour mettre en uvre cet effet, il suffit de quelques lignes de code qui ajoutent des classes aux lignes paires et impaires :
$(document).ready(function() { $('table.striped tr:odd').addClass('odd'); $('table.striped tr:even').addClass('even'); });

Si ce code fonctionne parfaitement avec les structures de table simples, le schma pairimpair nest plus satisfaisant en cas dajout de lignes qui ne doivent pas tre prises en compte dans leffet, comme les lignes de sous-titres utilises pour les annes dans notre table. Par exemple, la ligne 2006 correspond une ligne paire et celles qui viennent avant et aprs sont toutes deux impaires. La Figure 7.18 montre que le rsultat obtenu nest pas celui attendu.

Chapitre 7

Manipulation des tables

175

Figure 7.17
Aspect initial de la table.

Figure 7.18
Le schma pair-impair nest pas satisfaisant.

En utilisant la pseudo-classe :nth-child() prsente au Chapitre 2, nous pouvons faire en sorte que lalternance du motif reprenne au dbut aprs chaque ligne de sous-titre (voir Figure 7.19) :

176

jQuery

$(document).ready(function() { $('table.striped tr:nth-child(odd)').addClass('odd'); $('table.striped tr:nth-child(even)').addClass('even'); });

Figure 7.19
Lalternance des bandes reprend au dbut aprs une ligne de sous-titre.

Chaque groupe de lignes commence prsent par une ligne impaire, mais les lignes de sous-titre font partie du calcul. Pour ne plus prendre en compte ces lignes, nous utilisons la pseudo-classe :has() (voir Figure 7.20) :
$(document).ready(function() { $('table.striped tr:not(:has(th)):odd').addClass('odd'); $('table.striped tr:not(:has(th)):even').addClass('even'); });

Figure 7.20
Les lignes de sous-titre ne sont plus prises en compte.

Les sous-titres sont prsent exclus, mais les groupes commencent par une ligne paire ou impaire en fonction de la classe applique la ligne de donnes prcdente. Pour concilier ces deux comportements, la solution risque dtre difficile trouver. Une approche simple se fonde sur une itration explicite avec la mthode .each() :

Chapitre 7

Manipulation des tables

177

$(document).ready(function() { $('table.striped tbody').each(function() { $(this).find('tr:not(:has(th)):odd').addClass('odd'); $(this).find('tr:not(:has(th)):even').addClass('even'); }); });

Dans ce cas, les bandes sont appliques chaque groupe de manire indpendante et les lignes de sous-titre sont exclues du traitement (voir Figure 7.21).

Figure 7.21
Traitement indpendant des groupes de lignes.

Effet de bande labor Ces manipulations des lignes paires et impaires nous ont conduits mettre en uvre des techniques complexes. Dans les tables particulirement denses, lalternance de la couleur des lignes peut perturber le regard et il pourrait tre prfrable dalterner les couleurs sur un intervalle plus long. Pour prendre un exemple, nous modifierons leffet de bande sur notre table en changeant de couleur toutes les trois lignes. Au Chapitre 2, nous avons prsent la mthode .filter(), qui permet de slectionner des lments de la page de manire trs souple. En se rappelant que .filter() peut prendre non seulement une expression de slection, mais galement une fonction de filtre, nous crivons le code suivant :
$(document).ready(function() { $('table.striped tbody').each(function() { $(this).find('tr:not(:has(th))').filter(function(index) { return (index % 6) < 3; }).addClass('odd'); }); });

En peu de lignes, ce code ralise beaucoup de choses. Nous allons donc ltudier morceau par morceau.

178

jQuery

Comme prcdemment, nous utilisons la mthode .each() pour traiter sparment les groupes de lignes. Puisque les bandes de trois lignes doivent reprendre aprs chaque sous-titre, cette technique nous permet de travailler une section la fois. Ensuite, nous invoquons .find(), comme dans notre dernier exemple, pour localiser toutes les lignes qui ne contiennent pas dlments <th>, et qui ne sont donc pas des sous-titres. Ensuite, nous devons slectionner les trois premiers lments de la collection obtenue, sauter trois lments, et ainsi de suite. Cest l o la mthode .filter() entre en scne. La fonction de filtre prend un argument qui contient lindice de llment dans la collection obtenue, cest--dire le numro de la ligne dans la section de la table en cours de manipulation. Si, et uniquement si, notre fonction de filtre retourne true, llment reste dans la collection. Loprateur modulo (%) nous fournit linformation dont nous avons besoin. Lexpression index % 6 donne le reste de la division du numro de la ligne par six. Si ce reste est gal 0, 1 ou 2, nous marquons la ligne comme tant impaire. Sil vaut 3, 4 ou 5, la ligne est paire. Tel que prsent, le code marque uniquement les ensembles impairs de ligne. Pour appliquer galement la classe even, nous pouvons crire un autre filtre oprant de manire inverse ou tre un peu plus astucieux :
$(document).ready(function() { $('table.striped tbody').each(function() { $(this).find('tr:not(:has(th))').addClass('even') .filter(function(index) { return (index % 6) < 3; }).removeClass('even').addClass('odd'); }); });

Ce code applique la classe even toutes les lignes, puis la retire lorsque nous ajoutons la classe odd. Les bandes sont ainsi appliques par groupes de lignes, avec une reprise au dbut de chaque section de la table (voir Figure 7.22). Mise en exergue interactive Nous pouvons apporter une autre amlioration visuelle notre table darticles dactualit en fondant la mise en exergue des lignes sur les actions de lutilisateur. Dans notre exemple, nous rpondrons au clic sur le nom dun auteur en mettant en exergue toutes les lignes qui contiennent ce nom dans la cellule AUTEUR. Comme nous lavons fait pour les bandes, nous pouvons modifier laspect de ces lignes mises en exergue en ajoutant une classe :
#content tr.highlight { background: #ff6; }

Chapitre 7

Manipulation des tables

179

Figure 7.22
Le changement de couleur intervient toutes les trois lignes.

Puisquil est important que cette nouvelle classe permette de distinguer les lignes mises en exergue, nous choisissons de changer la couleur darrire-plan par rapport celle des classes even et odd. Nous devons prsent slectionner la cellule approprie et lui associer un comportement laide de la mthode .click() :
$(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); $authorCells.click(function() { // Effectuer la mise en exergue. }); });

Nous utilisons la pseudo-classe :nth-child(n) dans lexpression de slection. Cela nous permet de dsigner la troisime colonne qui contient linformation concernant lauteur. Si la structure de la table venait changer, il faudrait mettre cette constante 3

180

jQuery

en un seul endroit afin quelle soit facile modifier. Cest pourquoi, ainsi que pour une question defficacit, nous enregistrons le rsultat du slecteur dans la variable $authorCells au lieu de le rpter chaque fois quil est requis.
ATTENTION
Noubliez pas que, contrairement aux indices JavaScript, la pseudo-classe :nth-child(n) de CSS commence compter partir de un, non de zro.

Lorsque lutilisateur clique sur une cellule de la troisime colonne, nous voulons comparer le texte de cette cellule celui de la cellule correspondante dans chaque autre ligne. Sils sont identiques, la prsence de la classe highlight est inverse. Autrement dit, la classe est ajoute si elle nest pas dj prsente et retire dans le cas contraire. Nous pouvons ainsi cliquer sur une cellule dauteur pour retirer la mise en exergue de la ligne si nous avons dj cliqu sur cette cellule ou une autre contenant le mme auteur.
$(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); $authorCells.click(function() { var authorName = $(this).text(); $authorCells.each(function(index) { if (authorName == $(this).text()) { $(this).parent().toggleClass('highlight'); } }); }); });

Le code fonctionne parfaitement, except lorsque lutilisateur clique la suite sur deux noms dauteurs diffrents. Au lieu de basculer la mise en exergue des lignes qui correspondent au premier auteur vers celles qui correspondent au suivant, les deux groupes de lignes finissent avec la classe highlight. Pour viter ce comportement, nous ajoutons une instruction else dans laquelle nous supprimons la classe highlight sur toutes les lignes qui ne contiennent pas le nom de lauteur slectionn :
$(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); $authorCells.click(function() { var authorName = $(this).text(); $authorCells.each(function(index) { if (authorName == $(this).text()) { $(this).parent().toggleClass('highlight'); } else { $(this).parent().removeClass('highlight'); } }); }); });

Chapitre 7

Manipulation des tables

181

prsent, si nous cliquons sur Rey Bango, par exemple, tous les articles contenant cet auteur sont beaucoup plus faciles reprer (voir Figure 7.23).

Figure 7.23
Les articles du mme auteur (Rey Bango) sont mis en exergue.

Si nous cliquons ensuite sur John Resig dans lune des cellules, les lignes qui correspondent aux articles de Rey Bango ne sont plus mises en exergue, contrairement celles de John Resig. Info-bulles Bien que la mise en exergue des lignes soit une fonctionnalit utile, rien nindique lutilisateur quelle existe. Nous pouvons remdier cette situation en attribuant toutes les cellules AUTEUR la classe clickable, dont la rgle de style modifie lapparence du pointeur de la souris lorsquil se trouve sur la cellule :
$(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); $authorCells .addClass('clickable')

182

jQuery

.click(function() { var authorName = $(this).text(); $authorCells.each(function(index) { if (authorName == $(this).text()) { $(this).parent().toggleClass('highlight'); } else { $(this).parent().removeClass('highlight'); } }); }); });

La classe clickable va dans le bon sens, mais lutilisateur ne sait toujours pas ce qui se produira sil clique sur la cellule. Pour autant quil puisse limaginer, ce clic dclenchera certainement un autre comportement, comme le conduire vers une autre page. Il faut donc lui fournir des indications sur les effets de ses actions. Les info-bulles sont utilises par bon nombre dapplications logicielles, y compris les navigateurs web. Nous pouvons rpondre notre problme dutilisabilit en affichant une info-bulle lorsque le pointeur de la souris survole une cellule AUTEUR. Le texte de linfo-bulle peut dcrire aux utilisateurs les rsultats de laction, avec un message du type "Mettre en exergue tous les articles rdigs par Rey Bango.". Ce message sera plac dans un <div>, lui-mme ajout llment <body>. La variable $tooltip sera utilise tout au long du script pour faire rfrence ce nouvel lment cr :
var $tooltip = $('<div id="tooltip"></div>').appendTo('body');

Voici les trois oprations de base que nous effectuerons plusieurs reprises sur notre info-bulle : 1. Afficher linfo-bulle lorsque le pointeur de la souris se trouve au-dessus de llment interactif. 2. Masquer linfo-bulle lorsque le pointeur de la souris quitte la zone. 3. Repositionner linfo-bulle lorsque le pointeur de la souris se dplace. Nous allons crire des fonctions pour chacune de ces tches, puis nous les associerons aux vnements du navigateur laide de jQuery. Commenons par positionTooltip(), qui sera invoque lorsque le pointeur de la souris se dplace au-dessus dune cellule AUTEUR :
var positionTooltip = function(event) { var tPosX = event.pageX; var tPosY = event.pageY + 20; $tooltip.css({top: tPosY, left: tPosX}); };

Chapitre 7

Manipulation des tables

183

Nous examinons les proprits pageX et pageY de lobjet event pour fixer le positionnement haut et gauche de linfo-bulle. Lorsque cette fonction est invoque en rponse un vnement de la souris, comme mousemove, event.pageX et event.pageY contiennent les coordonnes du pointeur de la souris. Par consquent, tPosX et tPosY dsignent un emplacement sur lcran qui se trouve vingt pixels sous le pointeur de la souris. Nous passons ensuite la fonction showTooltip(), qui affiche linfo-bulle lcran :
var showTooltip = function(event) { var authorName = $(this).text(); $tooltip .text('Mettre en exergue tous les articles rdigs par ' + authorName) .show(); positionTooltip(event); };

Cette fonction est relativement simple. Nous remplissons le texte de linfo-bulle en utilisant une chane de caractres construite partir du contenu de la cellule (cest--dire le nom de lauteur) et laffichons. La fonction positionTooltip() positionne ensuite linfo-bulle lemplacement appropri sur la page. Puisque linfo-bulle a t ajoute llment <body>, nous devons dfinir une rgle CSS pour quelle flotte au-dessus de la page lemplacement calcul :
#tooltip { position: absolute; z-index: 2; background: #efd; border: 1px solid #ccc; padding: 3px; }

Enfin, nous crivons une fonction hideTooltip() simple :


var hideTooltip = function() { $tooltip.hide(); };

Puisque nous disposons prsent des fonctions permettant dafficher, de masquer et de positionner linfo-bulle, nous pouvons les intgrer notre code :
$(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); var $tooltip = $('<div id="tooltip"></div>').appendTo('body'); var positionTooltip = function(event) { var tPosX = event.pageX; var tPosY = event.pageY + 20; $tooltip.css({top: tPosY, left: tPosX}); }; var showTooltip = function(event) { var authorName = $(this).text(); $tooltip .text('Mettre en exergue tous les articles rdigs par ' + authorName) .show();

184

jQuery

positionTooltip(event); }; var hideTooltip = function() { $tooltip.hide(); }; $authorCells .addClass('clickable') .hover(showTooltip, hideTooltip) .mousemove(positionTooltip) .click(function(event) { var authorName = $(this).text(); $authorCells.each(function(index) { if (authorName == $(this).text()) { $(this).parent().toggleClass('highlight'); } else { $(this).parent().removeClass('highlight'); } }); }); });

Notez que les arguments des mthodes .hover() et .mousemove() font rfrence des fonctions qui sont dfinies ailleurs. Cest pourquoi nous omettons les parenthses, qui sinon dclencheraient linvocation des fonctions. Linfo-bulle apparat ds que le pointeur de la souris passe au-dessus dune cellule AUTEUR, suit le dplacement de la souris et disparat lorsque le pointeur quitte la cellule (voir Figure 7.24).

Figure 7.24
Une info-bulle dexplication apparat sous le pointeur de la souris.

Limplmentation actuelle fonctionne, mais linfo-bulle suggre de cliquer sur une cellule pour mettre en exergue les articles, mme sils le sont dj (voir Figure 7.25). Nous devons modifier le texte de linfo-bulle lorsque la ligne pointe possde la classe highlight. Pour cela, nous ajoutons une instruction conditionnelle dans la fonction

Chapitre 7

Manipulation des tables

185

Figure 7.25
Lindication donne par linfo-bulle nest pas correcte.

showTooltip() de manire vrifier la prsence de la classe. Selon que llment <tr> parent de la cellule pointe possde ou non la classe highlight, nous changeons le texte de linfo-bulle :
var action = 'Mettre en exergue'; if ($(this).parent().is('.highlight')) { action = 'Ne plus mettre en exergue'; } $tooltip .text(action + ' tous les articles rdigs par ' + authorName) .show();

Le texte de linfo-bulle est corrig lorsque le pointeur de la souris entre dans une cellule, mais nous devons galement ladapter chaque fois que lutilisateur clique. Pour cela, nous invoquons la fonction showTooltip() depuis le gestionnaire de lvnement click :
$(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); var $tooltip = $('<div id="tooltip"></div>').appendTo('body'); var positionTooltip = function(event) { var tPosX = event.pageX; var tPosY = event.pageY + 20; $tooltip.css({top: tPosY, left: tPosX}); }; var showTooltip = function(event) { var authorName = $(this).text(); var action = 'Mettre en exergue'; if ($(this).parent().is('.highlight')) { action = 'Ne plus mettre en exergue'; } $tooltip .text(action + ' tous les articles rdigs par ' + authorName) .show(); positionTooltip(event); };

186

jQuery

var hideTooltip = function() { $tooltip.hide(); }; $authorCells .addClass('clickable') .hover(showTooltip, hideTooltip) .mousemove(positionTooltip) .click(function(event) { var authorName = $(this).text(); $authorCells.each(function(index) { if (authorName == $(this).text()) { $(this).parent().toggleClass('highlight'); } else { $(this).parent().removeClass('highlight'); } }); showTooltip.call(this, event); }); });

Avec la fonction JavaScript call(), nous pouvons invoquer showTooltip() comme si elle sexcutait dans la porte du gestionnaire de click associe la cellule contenant le nom de lauteur. Lorsquune fonction est appele, le mot cl this correspond lobjet JavaScript qui la appele. La mthode JavaScript call() permet de contourner cette rgle en prcisant en premier argument la valeur souhaite de this, dans ce cas la cellule de la table. Linfo-bulle affiche une indication plus correcte lorsque le pointeur de la souris survole une ligne qui est dj mise en exergue (voir Figure 7.26).

Figure 7.26
Lindication donne par linfo-bulle est adapte la situation.

Chapitre 7

Manipulation des tables

187

Rduire et dvelopper des sections Lorsquun jeu de donnes volumineux est divis en sections, il peut tre utile de masquer les informations qui ne prsentent pas dintrt un moment donn. Dans notre table des articles dactualit, les lignes sont regroupes par anne. Rduire, ou masquer, les articles dune anne peut tre une bonne manire dobtenir une vue densemble de toutes les donnes de la table sans avoir les faire dfiler. Pour que les sections de la table soient escamotables, nous devons tout dabord crer un lment qui servira dclencher ce comportement. La possibilit de rduction dune section est souvent reprsente par un signe moins, tandis que la fonctionnalit de dveloppement est signale par un signe plus. Nous insrons les icnes correspondantes en JavaScript, en suivant les techniques damlioration progressive standard :
$(document).ready(function() { var collapseIcon = '../images/bullet_toggle_minus.png'; var collapseText = 'Rduire cette section'; var expandIcon = '../images/bullet_toggle_plus.png'; var expandText = 'Dvelopper cette section'; $('table.collapsible tbody').each(function() { var $section = $(this); $('<img />').attr('src', collapseIcon) .attr('alt', collapseText) .prependTo($section.find('th')); }); });

Au dbut de la fonction, nous enregistrons dans des variables lemplacement des icnes, ainsi que leur texte de remplacement. Nous pouvons ainsi les modifier et y faire rfrence trs facilement. Linjection de limage se fait dans une boucle .each(), qui nous sera utile plus tard lorsque nous devrons nouveau faire rfrence llment <tbody> englobant ; il sera accessible au travers de la variable $section. Ensuite, nous devons faire en sorte que les icnes dclenchent la rduction et le dveloppement des sections. Lajout dune classe clickable permet dapporter le retour visuel ncessaire lutilisateur, et une classe applique llment <tbody> nous permet de suivre ltat de visibilit des lignes :
$(document).ready(function() { var collapseIcon = '../images/bullet_toggle_minus.png'; var collapseText = 'Rduire cette section'; var expandIcon = '../images/bullet_toggle_plus.png'; var expandText = 'Dvelopper cette section'; $('table.collapsible tbody').each(function() { var $section = $(this); $('<img />').attr('src', collapseIcon) .attr('alt', collapseText) .prependTo($section.find('th')) .addClass('clickable') .click(function() {

188

jQuery

if ($section.is('.collapsed')) { $section.removeClass('collapsed') .find('tr:not(:has(th))').fadeIn('fast'); $(this).attr('src', collapseIcon) .attr('alt', collapseText); } else { $section.addClass('collapsed') .find('tr:not(:has(th))').fadeOut('fast'); $(this).attr('src', expandIcon) .attr('alt', expandText); } }); }); });

Voici les oprations effectues lors dun clic : 1. Ajouter ou retirer la classe collapsed sur llment <tbody>, pour le suivi de ltat actuel de la section correspondante de la table. 2. Localiser toutes les lignes de la section qui ne contiennent pas un intitul et les afficher ou les masquer avec un effet de fondu. 3. Inverser ltat actuel de licne, en changeant ses attributs src et alt de manire reflter le comportement quelle dclenchera dsormais lors dun clic. Une fois que ce code est en place, un clic sur licne RDUIRE CETTE SECTION affiche ct de 2007 permet dobtenir la prsentation de la table illustre la Figure 7.27.

Figure 7.27
Rduction des articles de lanne 2007.

Les articles de lanne 2007 ne sont pas supprims. Ils sont simplement masqus, jusqu ce que lutilisateur clique sur licne DVELOPPER CETTE SECTION qui apparat dsormais sur cette ligne de sous-titre.

Chapitre 7

Manipulation des tables

189

ATTENTION
Les lignes dune table posent un problme particulier pour lanimation, car les navigateurs utilisent des valeurs diffrentes (table-row et block) pour la proprit de visibilit display. Les mthodes .hide() et .show() sans animation ont toujours un comportement cohrent pour les lignes dune table. Si vous souhaitez ajouter une animation, vous pouvez galement utiliser .fadeIn() et .fadeOut().

Appliquer un filtre Nous avons vu que le tri et la pagination sont des techniques qui permettent aux utilisateurs de se focaliser sur les donnes pertinentes dune table. Elles peuvent toutes deux tre mises en uvre ct serveur ou en JavaScript. Le filtrage complte notre arsenal de stratgies de prsentation des donnes. En affichant lutilisateur uniquement les lignes de la table qui correspondent un critre donn, nous lui vitons dtre distrait par des informations inutiles. Nous avons dj tudi la ralisation dun type de filtre : la mise en exergue dun ensemble de lignes. Nous allons prsent dvelopper cette ide en masquant les lignes qui ne correspondent pas au filtre. Nous commenons par crer un emplacement pour les liens de filtrage. Conformment au principe damlioration progressive, nous ajoutons ces contrles en utilisant du code JavaScript afin quils ne soient proposs quaux internautes ayant activ la prise en charge des scripts :
$(document).ready(function() { $('table.filterable').each(function() { var $table = $(this); $table.find('th').each(function(column) { if ($(this).is('.filter-column')) { var $filters = $('<div class="filters"></div>'); $('<h3></h3>') .text('Filtrer par ' + $(this).text() + ' :') .appendTo($filters); $filters.insertBefore($table); } }); }); });

Pour pouvoir rutiliser ce code avec dautres tables, nous rcuprons le libell du filtre partir des en-ttes de colonnes. Nous disposons prsent dun titre qui attend quelques boutons (voir Figure 7.28).

190

jQuery

Figure 7.28
Les dbuts du filtrage.

Options de filtrage Nous pouvons prsent passer la mise en uvre dun filtre. Pour commencer, nous allons ajouter des filtres pour deux sujets connus. Le code correspondant ressemble lexemple de mise en exergue des articles crits par un auteur :
$(document).ready(function() { $('table.filterable').each(function() { var $table = $(this); $table.find('th').each(function(column) { if ($(this).is('.filter-column')) { var $filters = $('<div class="filters"></div>'); $('<h3></h3>') .text('Filtrer par ' + $(this).text() + ' :') .appendTo($filters); var keywords = ['confrence', 'sortie']; $.each(keywords, function(index, keyword) { $('<div class="filter"></div>').text(keyword) .bind('click', {key: keyword}, function(event) { $('tr:not(:has(th))', $table).each(function() { var value = $('td', this).eq(column).text(); if (value == event.data['key']) { $(this).show(); } else { $(this).hide(); } }); $(this).addClass('active') .siblings().removeClass('active'); }).addClass('clickable').appendTo($filters); }); $filters.insertBefore($table); } }); }); });

Chapitre 7

Manipulation des tables

191

Nous partons dun tableau statique de mots cls qui serviront de critres de tri. Nous le parcourons et crons un lien de filtre pour chacun des mots cls. Comme dans lexemple de pagination, nous utilisons le paramtre data de la mthode .bind() pour viter les problmes lis aux fermetures. Ensuite, dans le gestionnaire de click, nous comparons le contenu de chaque cellule et le mot cl, et masquons la ligne sils ne correspondent pas. Puisque notre slecteur de lignes exclut celles qui contiennent un lment <th>, nous navons pas besoin de nous proccuper des sous-titres. La Figure 7.29 montre les liens qui correspondent aux mots cls dfinis.

Figure 7.29
Application dun filtre sur un critre prdfini.

Obtenir les options de filtrage partir du contenu Nous devons tendre les options de filtrage de manire couvrir lensemble des sujets contenus dans la table. Au lieu de figer tous les sujets dans le code, nous pouvons les obtenir partir du texte affich par la table. Nous modifions donc la dfinition de keywords :
var keywords = {}; $table.find('td:nth-child(' + (column + 1) + ')') .each(function() { keywords[$(this).text()] = $(this).text(); });

Ce code se fonde sur deux astuces : 1. En utilisant une mappe la place dun tableau pour stocker les mots cls trouvs, nous supprimons automatiquement les doublons. Chaque cl ne peut avoir quune seule valeur, et les cls sont toujours uniques. 2. La fonction $.each() de jQuery nous permet de manipuler de la mme manire des tableaux et des mappes. Le code na donc pas besoin dtre modifi.

192

jQuery

Toutes les options de filtrage sont prsent disponibles (voir Figure 7.30).

Figure 7.30
Les critres du filtre sont obtenus depuis le contenu de la table.

Retirer le filtre Nous voulons galement proposer un moyen de revenir la liste complte aprs lavoir filtre. Pour cela, nous ajoutons une option qui correspond tous les sujets :
$('<div class="filter">Tous</div>').click(function() { $table.find('tbody tr').show(); $(this).addClass('active') .siblings().removeClass('active'); }).addClass('clickable active').appendTo($filters);

Ce lien permet simplement dafficher toutes les lignes de la table. Pour faire bonne mesure, il est initialement actif (voir Figure 7.31). Interagir avec un autre code Avec notre code pour le tri et la pagination, nous avons vu que nous ne pouvions pas dvelopper isolment les diffrentes fonctionnalits. Les comportements peuvent parfois interagir de manire surprenante. Par consquent, il est prfrable de revenir sur notre travail prcdent pour examiner sa coexistence avec les nouvelles possibilits de filtrage ajoutes.

Chapitre 7

Manipulation des tables

193

Figure 7.31
Un lien permet dafficher toutes les lignes de la table.

Effet de bande Leffet de bande labor appliqu aux lignes est perturb par la fonctionnalit de filtrage. Puisquil nest pas rappliqu aprs le filtrage de la table, les lignes conservent leur couleur comme si les lignes masques taient toujours prsentes. Pour tenir compte des lignes filtres, le code dapplication des bandes doit tre en mesure de les identifier. La pseudo-classe jQuery :visible peut nous aider obtenir les lignes concernes par les bandes. Nous profitons de cette modification pour prparer le code de cration des bandes tre invoqu depuis dautres endroits du script. Pour cela, nous crons un type dvnement personnalis, comme nous lavons fait pour la combinaison du tri et de la pagination.
$(document).ready(function() { $('table.striped').bind('stripe', function() { $('tbody', this).each(function() { $(this).find('tr:visible:not(:has(th))') .removeClass('odd').addClass('even') .filter(function(index) { return (index % 6) < 3; }).removeClass('even').addClass('odd'); }); }).trigger('stripe'); });

Ce code fond sur la pseudo-classe :visible fonctionne parfaitement dans les versions de jQuery antrieures la 1.3.2. partir de cette version, la visibilit dun lment est dtecte diffremment et notre code nest plus oprationnel. Nous remplaons donc la ligne :
$(this).find('tr:visible:not(:has(th))')

194

jQuery

par :
$(this).find('tr:not(:has(th))') .filter(function() { return this.style.display != 'none'; })

Dans le code de filtrage, nous pouvons prsent invoquer $table.trigger('stripe') aprs chaque application dun filtre. Avec le nouveau gestionnaire dvnements et ses dclenchements en place, leffet de bande est respect mme aprs le filtrage de la table (voir Figure 7.32).

Figure 7.32
Application de leffet de bande aprs filtrage.

Rduction et dveloppement La rduction et le dveloppement des sections ajouts prcdemment entrent galement en conflit avec les filtres. Lorsquune section est rduite et quun nouveau filtre est slectionn, tous les lments correspondants sont affichs mme ceux qui se trouvent dans la section ferme. De mme, lorsque la table est filtre et quune section est dveloppe, tous les lments de cette section sont affichs, quils correspondent ou non au filtre. Pour rpondre ce dernier cas, une solution consiste modifier laffichage et le masquage des lignes. Si nous utilisons une classe pour indiquer quune ligne doit tre masque, il nest plus ncessaire dinvoquer explicitement .hide() et .show(). En remplaant .hide() par .addClass('filtered') et .show() par .removeClass('fil-

Chapitre 7

Manipulation des tables

195

tered'), et en dfinissant une rgle CSS pour la classe, nous pouvons obtenir un masquage et un affichage des lignes qui sintgrent mieux au code de rduction. Si la classe est retire et si la ligne est rduite, elle nest plus affiche par mgarde.

Lajout de cette nouvelle classe filtered nous facilite galement la rsolution du problme inverse. Nous pouvons tester la prsence de filtered au moment du dveloppement dune section et sauter les lignes correspondantes au lieu de les afficher. Pour tester cette classe, il suffit dajouter :not(.filtered) lexpression de slection utilise pour le dveloppement des sections. Les fonctionnalits cooprent parfaitement, chacune pouvant afficher et masquer les lignes indpendamment. Version finale du code Notre deuxime exemple a illustr lapplication dun effet de bande une table, la mise en exergue de lignes, les info-bulles, la rduction et le dveloppement de sections, ainsi que le filtrage. Voici lintgralit du code JavaScript qui correspond cet exemple :
$(document).ready(function() { $('table.striped').bind('stripe', function() { $('tbody', this).each(function() { $(this).find('tr:not(:has(th))') .filter(function() { return this.style.display != 'none'; }) .removeClass('odd').addClass('even') .filter(function(index) { return (index % 6) < 3; }).removeClass('even').addClass('odd'); }); }).trigger('stripe'); }); $(document).ready(function() { var $authorCells = $('table.striped td:nth-child(3)'); var $tooltip = $('<div id="tooltip"></div>').appendTo('body'); var positionTooltip = function(event) { var tPosX = event.pageX; var tPosY = event.pageY + 20; $tooltip.css({top: tPosY, left: tPosX}); }; var showTooltip = function(event) { var authorName = $(this).text(); var action = 'Mettre en exergue'; if ($(this).parent().is('.highlight')) { action = 'Ne plus mettre en exergue'; } $tooltip .text(action + ' tous les articles rdigs par ' + authorName) .show();

196

jQuery

positionTooltip(event); }; var hideTooltip = function() { $tooltip.hide(); }; $authorCells .addClass('clickable') .hover(showTooltip, hideTooltip) .mousemove(positionTooltip) .click(function(event) { var authorName = $(this).text(); $authorCells.each(function(index) { if (authorName == $(this).text()) { $(this).parent().toggleClass('highlight'); } else { $(this).parent().removeClass('highlight'); } }); showTooltip.call(this, event); }); }); $(document).ready(function() { var collapseIcon = '../images/bullet_toggle_minus.png'; var collapseText = 'Rduire cette section'; var expandIcon = '../images/bullet_toggle_plus.png'; var expandText = 'Dvelopper cette section'; $('table.collapsible tbody').each(function() { var $section = $(this); $('<img />').attr('src', collapseIcon) .attr('alt', collapseText) .prependTo($section.find('th')) .addClass('clickable') .click(function() { if ($section.is('.collapsed')) { $section.removeClass('collapsed') .find('tr:not(:has(th)):not(.filtered)') .fadeIn('fast'); $(this).attr('src', collapseIcon) .attr('alt', collapseText); } else { $section.addClass('collapsed') .find('tr:not(:has(th))') .fadeOut('fast', function() { $(this).css('display', 'none'); }); $(this).attr('src', expandIcon) .attr('alt', expandText); } $section.parent().trigger('stripe'); }); }); });

Chapitre 7

Manipulation des tables

197

$(document).ready(function() { $('table.filterable').each(function() { var $table = $(this); $table.find('th').each(function(column) { if ($(this).is('.filter-column')) { var $filters = $('<div class="filters"></div>'); $('<h3></h3>') .text('Filtrer par ' + $(this).text() + ' :') .appendTo($filters); $('<div class="filter">all</div>').click(function() { $table.find('tbody tr').removeClass('filtered'); $(this).addClass('active') .siblings().removeClass('active'); $table.trigger('stripe'); }).addClass('clickable active').appendTo($filters); var keywords = {}; $table.find('td:nth-child(' + (column + 1) + ')') .each(function() { keywords[$(this).text()] = $(this).text(); }); $.each(keywords, function(index, keyword) { $('<div class="filter"></div>').text(keyword) .bind('click', {key: keyword}, function(event) { $('tr:not(:has(th))', $table).each(function() { var value = $('td', this).eq(column).text(); if (value == event.data['key']) { $(this).removeClass('filtered'); } else { $(this).addClass('filtered'); } }); $(this).addClass('active') .siblings().removeClass('active'); $table.trigger('stripe'); }).addClass('clickable').appendTo($filters); }); $filters.insertBefore($table); } }); }); });

7.3

En rsum

Dans ce chapitre, nous avons explor plusieurs manires de manipuler les tables de nos sites, pour les transformer en conteneurs lgants et fonctionnels de nos donnes. Nous avons vu comment trier les donnes dune table, en utilisant des cls de diffrents types (mots, nombres, dates), et comment la paginer en portions plus faciles consulter. Nous

198

jQuery

avons appris des techniques JavaScript sophistiques permettant dajouter des bandes aux lignes et dafficher des info-bulles. Nous avons galement tudi la rduction et le dveloppement de sections de contenu, ainsi que le filtrage et la mise en exergue des lignes qui correspondent un critre donn. Nous avons mme abord brivement quelques sujets avancs, comme le tri et la pagination ct serveur avec des techniques AJAX, le calcul dynamique de la position des lments sur la page et lcriture dun plugin jQuery. Nous lavons vu, les tables HTML dont la smantique est correcte cachent un haut niveau de subtilit et de complexit. Heureusement, jQuery nous aide matriser ces constructions, de manire rvler tout lintrt des donnes tabulaires.

8
Manipulation des formulaires
Au sommaire de ce chapitre
U U U U

Amliorer un formulaire de base Formulaires compacts Manipuler des donnes numriques En rsum

Il est difficile de trouver un site web qui nutilise pas un formulaire pour obtenir un retour de la part de lutilisateur. Depuis les dbuts dInternet, les formulaires ont servi transmettre des informations entre lutilisateur final et lditeur du site web, certes de manire fiable, mais souvent avec peu dlgance ou de style. Ce manque de classe tait sans doute li au voyage rptitif et ardu entre le serveur et le navigateur, ou bien provenait-il des lments intransigeants imposs aux formulaires et de leur rticence suivre la dernire mode. Quelle que soit la raison, ce nest que rcemment, avec le regain dintrt pour les scripts ct client, que les formulaires ont retrouv un second souffle, un objectif et du style. Dans ce chapitre, nous verrons comment ressusciter les formulaires. Nous allons amliorer leur aspect, crer des procdures de validation, les employer pour des calculs et envoyer discrtement les rsultats au serveur.

8.1

Amliorer un formulaire de base

Lorsquon utilise jQuery pour des sites web, il faut toujours sinterroger sur laspect et le fonctionnement des pages en cas dindisponibilit de JavaScript (sauf, bien sr, si lon sait prcisment qui sont les visiteurs et comment sont configurs leurs navigateurs). Attention, cela ne signifie pas que lindisponibilit de JavaScript doive empcher la cration dun site plus joli et aux fonctionnalits compltes. Le principe damlioration progressive est trs rpandu chez les dveloppeurs JavaScript, car il respecte le besoin de tous les utilisateurs tout en proposant des fonctions supplmentaires

200

jQuery

la plupart. Pour illustrer ce principe dans le contexte des formulaires, nous allons crer un formulaire de contact, que nous amliorerons avec jQuery, tant au niveau de son aspect que de son fonctionnement. Amlioration progressive de laspect Tout dabord, apportons quelques modifications esthtiques notre formulaire. Lorsque JavaScript nest pas activ, le premier jeu de champs saffiche comme lillustre la Figure 8.1.

Figure 8.1
Prsentation par dfaut des champs du formulaire.

Si elle semble rpondre aux besoins et explique aux utilisateurs comment remplir les champs, cette partie du formulaire mriterait dtre amliore. Nous allons intervenir progressivement sur trois aspects : 1. Modifier le DOM pour que lapplication dun style aux balises <legend> soit plus flexible. 2. Remplacer le message (OBLIGATOIRE) sur les champs obligatoires par un astrisque (*) et le message (OBLIGATOIRE SI LA CASE CORRESPONDANTE EST COCHE) sur les champs spciaux par deux astrisques (**). Afficher en gras le libell de chaque champ obligatoire et placer une lgende au dbut du formulaire pour expliquer la signification de lastrisque et de lastrisque double. 3. Masquer le champ de saisie correspondant chaque case cocher lors du chargement de la page, puis les afficher ou les masquer selon que lutilisateur coche ou dcoche les cases. Nous commenons par le contenu HTML du <fieldset> :

Chapitre 8

Manipulation des formulaires

201

<fieldset> <legend>Informations personnelles</legend> <ol> <li> <label for="first-name">Prnom</label> <input class="required" type="text" name="first-name" id="first-name" /> <span>(obligatoire)</span> </li> <li> <label for="last-name">Nom</label> <input class="required" type="text" name="last-name" id="last-name" /> <span>(obligatoire)</span> </li> <li>Comment souhaitez-vous tre contact ? (choisissez au moins une mthode) <ul> <li> <label for="by-email"> <input type="checkbox" name="by-contact-type" value="E-mail" id="by-email" /> par courriel </label> <input class="conditional" type="text" name="email" id="email" /> <span>(obligatoire si la case correspondante est coche)</span> </li> <li> <label for="by-phone"> <input type="checkbox" name="by-contact-type" value="Phone" id="by-phone" /> par tlphone </label> <input class="conditional" type="text" name="phone" id="phone" /> <span>(obligatoire si la case correspondante est coche)</span> </li> <li> <label for="by-fax"> <input type="checkbox" name="by-contact-type" value="Fax" id="by-fax" /> par tlcopie </label> <input class="conditional" type="text" name="fax" id="fax" /> <span>(obligatoire si la case correspondante est coche)</span> </li> </ul> </li> </ol> </fieldset>

Vous remarquerez que chaque lment dinformation ou couple dlments est considr comme un lment de liste (<li>). Tous les lments sont placs dans une liste ordonne (<ol>) et les cases cocher, avec leur champ de texte, sont places dans une liste non ordonne imbrique (<ul>). Par ailleurs, nous utilisons <label> pour prciser le nom des champs. Pour les champs de texte, <label> prcde llment <input> ; pour les cases cocher, il englobe <input>. Mme sil nexiste aucune structure stan-

202

jQuery

dard pour les lments dun jeu de champs, la liste ordonne semble adapte la reprsentation smantique des lments dinformation dans un formulaire de contact. Avec ce contenu HTML disponible, nous sommes prts utiliser jQuery pour procder une amlioration progressive. La lgende La lgende du formulaire est depuis tout temps un lment difficile styler avec CSS. Les diffrences dinterprtation par les navigateurs et les limites sur le positionnement rendent cet exercice particulirement prilleux. Nanmoins, si nous voulons utiliser des lments de page bien structurs et vhiculant un sens, la lgende est un choix sduisant, si ce nest attrayant visuellement, pour ajouter un titre notre lment <fieldset> du formulaire. Avec uniquement un balisage HTML et des styles CSS, nous sommes forcs un compromis entre un balisage smantique et une conception flexible. Toutefois, rien ne nous empche de modifier le balisage HTML lors du chargement de la page, de manire convertir chaque <legend> en <h3> pour ceux qui consultent la page, tandis que les systmes de lecture de la page, ainsi que les navigateurs sans JavaScript, continuent voir llment <legend>. Grce la mthode jQuery .replaceWith(), cette opration est trs simple :
$(document).ready(function() { $('legend').each(function(index) { $(this).replaceWith('<h3>' + $(this).text() + '</h3>'); }); });

Dans ce cas, nous ne pouvons pas nous fonder sur litration implicite de jQuery. Pour chaque lment que nous remplaons, nous devons insrer son contenu textuel correspondant. Cest pourquoi nous utilisons la mthode .each(), qui nous permet dobtenir ce texte partir de $(this). prsent, si nous modifions la feuille de style pour appliquer un fond bleu et une couleur blanche au texte des lments <h3>, le premier jeu de champs du formulaire apparat tel quillustr la Figure 8.2. Une autre solution permet de conserver les lments <legend>, mais enveloppe leur contenu dans une balise <span> :
$(document).ready(function() { $('legend').wrapInner('<span></span>'); });

Cette approche prsente au moins deux avantages par rapport au remplacement de <legend> par <h3> : elle garde la signification smantique de <legend> pour les lec-

Chapitre 8

Manipulation des formulaires

203

Figure 8.2
Remplacement de la lgende par un niveau de titre.

teurs dcran qui prennent en charge JavaScript et ncessite un travail moins important de la part du script. Elle a pour inconvnient de complexifier lapplication du style lintitul. Nous devons au moins fixer la proprit position des lments <fieldset> et <span>, ainsi que les proprits padding-top du <fieldset> et width du <span> :
fieldset { position: relative; padding-top: 1.5em; } legend span { position: absolute; width: 100%; }

Que nous choisissions de remplacer les lments <legend> du formulaire ou dinsrer un <span> lintrieur, ils sont prsent suffisamment amliors pour nous satisfaire. Il est temps de passer au message des champs obligatoires. Message des champs obligatoires Dans le formulaire de contact, les champs obligatoires possdent la classe required, ce qui nous permet de leur appliquer un style et danalyser la saisie de lutilisateur ; la classe conditional est applique aux champs de saisie qui correspondent une mthode de contact. Nous allons utiliser ces classes pour modifier les instructions affiches entre parenthses droite des champs. Nous commenons par initialiser les variables requiredFlag et conditionalFlag, puis nous remplissons llment <span> plac ct de chaque champ obligatoire en utilisant le contenu de ces variables :

204

jQuery

$(document).ready(function() { var requiredFlag = ' * '; var conditionalFlag = ' ** '; $('form :input') .filter('.required') .next('span').text(requiredFlag).end() .end() .filter('.conditional') .next('span').text(conditionalFlag); });

En utilisant .end(), nous pouvons tendre la chane des mthodes de manire continuer travailler sur la mme collection dlments et minimiser le nombre dobjets crs et les traverses du DOM. Chaque appel la mthode .end() ramne la slection un pas en arrire et retourne les lments qui taient slectionns avant la dernire mthode de parcours. Dans le script, nous en utilisons deux la suite : le premier appel .end() retourne la collection obtenue par .filter('.required'), le second, celle fournie par $('form :input'). Ainsi, lorsque .filter('.conditional') slectionne les lments dont la classe est conditional, il examine lensemble des champs de saisie du formulaire. Puisquun seul astrisque (*) ne suffira sans doute pas attirer lattention de lutilisateur, nous ajoutons galement la classe req-label llment <label> de chaque champ obligatoire et appliquons font-weight:bold cette classe, tout cela en poursuivant la chane :
$(document).ready(function() { var requiredFlag = ' * '; var conditionalFlag = ' ** '; $('form :input') .filter('.required') .next('span').text(requiredFlag).end() .prev('label').addClass('req-label').end() .end() .filter('.conditional') .next('span').text(conditionalFlag); });

Une telle chane dinvocations risque dtre difficile lire. Cest pourquoi une utilisation cohrente des passages la ligne et de lindentation est essentielle. La Figure 8.3 prsente le jeu de champs aprs modification du texte et ajout de la classe. Pas mal pour un dbut. Nanmoins, les messages des champs obligatoires ntaient pas inutiles, ils taient simplement trop rptitifs. Reprenons la premire instance de chaque message et affichons-la au-dessus du formulaire, ct du symbole qui le reprsente.

Chapitre 8

Manipulation des formulaires

205

Figure 8.3
Traitement des messages des deux types de champs obligatoires.

Avant de remplir les lments <span> laide du message correspondant, nous devons enregistrer les messages initiaux dans deux variables. Ensuite, nous pouvons retirer les parenthses laide dune expression rgulire :
$(document).ready(function() { var requiredFlag = ' * '; var conditionalFlag = ' ** '; var requiredKey = $('input.required:first').next('span').text(); var conditionalKey = $('input.conditional:first').next('span').text(); requiredKey = requiredFlag + requiredKey.replace(/^\((.+)\)$/,'$1'); conditionalKey = conditionalFlag + conditionalKey.replace(/^\((.+)\)$/,'$1'); // ... suite du code. });

Les deux premires lignes supplmentaires dclarent requiredKey et conditionalKey, les variables dans lesquelles sont enregistrs les textes des messages. Les deux lignes suivantes modifient le texte contenu dans ces variables, en concatnant le symbole de rfrence et le texte associ, sans les parenthses. Lexpression rgulire et la mthode .replace() mritent sans doute quelques explications. Expression rgulire Lexpression rgulire est place entre les deux barres obliques et prend la forme suivante : /^\((.+)\)$/. Le premier caractre, ^, indique que ce qui suit doit apparatre au dbut de la chane. Il est suivi de deux caractres, \(, qui recherchent une parenthse ouvrante. La barre oblique inverse applique lchappement au caractre suivant pour indiquer au parseur de lexpression rgulire quil doit le traiter littralement. Cet chappement est ncessaire car les parenthses font partie des caractres ayant une signification particulire dans les expressions rgulires. Les quatre caractres suivants,

206

jQuery

(.+), recherchent un ou plusieurs (la signification du +) caractres quelconques la suite (reprsents par le .) et les placent dans un groupe de capture grce aux parenthses. Les trois derniers caractres, \)$, recherchent une parenthse fermante la fin de la chane. En rsum, lexpression rgulire correspond une chane qui commence par une parenthse ouvrante, suivie dun groupe de caractres et termine par une parenthse fermante.

La mthode .replace() recherche, dans le contexte indiqu, une chane de caractres reprsente par une expression rgulire et la remplace par une autre chane. Voici sa syntaxe :
'contexte'.replace(/expression-rgulire/, 'remplacement')

Dans nos deux mthodes .replace(), les chanes sont les variables requiredKey et conditionalKey. La signification des expressions rgulires a t donne prcdemment. Une virgule spare lexpression rgulire de la chane de remplacement, qui, dans notre cas, est '$1'. Le paramtre $1 reprsente le premier groupe de capture de lexpression rgulire. Puisque notre expression rgulire possde un groupe de un ou de plusieurs caractres entour de parenthses, la chane de remplacement correspondra au contenu de ce groupe, sans les parenthses. Insrer la lgende des messages Nous disposons prsent des messages sans les parenthses et pouvons donc les insrer au-dessus du formulaire avec les indicateurs correspondants :
$(document).ready(function() { var requiredFlag = ' * '; var conditionalFlag = ' ** '; var requiredKey = $('input.required:first').next('span').text(); var conditionalKey = $('input.conditional:first').next('span').text(); requiredKey = requiredFlag + requiredKey.replace(/^\((.+)\)$/,'$1'); conditionalKey = conditionalFlag + conditionalKey.replace(/^\((.+)\)$/,'$1'); $('<p></p>') .addClass('field-keys') .append(requiredKey + '<br />') .append(conditionalKey) .insertBefore('#contact'); });

Les cinq nouvelles lignes doivent dsormais vous sembler relativement familires. Voici leur fonction : 1. Crer un nouvel lment de paragraphe. 2. Affecter la classe field-keys au paragraphe.

Chapitre 8

Manipulation des formulaires

207

3. Ajouter le contenu de requiredKey et un passage la ligne au paragraphe. 4. Ajouter le contenu de conditionalKey au paragraphe. 5. Insrer le paragraphe et tout ce que nous avons ajout avant le formulaire de contact. Lorsquon utilise .append() avec une chane HTML, comme cest le cas ici, il faut vrifier que lchappement est appliqu aux caractres HTML spciaux. Dans notre exemple, la mthode .text() utilise lors de la dclaration des variables a procd cette vrification pour nous. En dfinissant quelques styles pour la classe .field-keys, nous obtenons le rsultat illustr la Figure 8.4.
Figure 8.4
Dplacement des messages avant le formulaire.

Notre intervention jQuery sur le premier jeu de champs est quasiment termine. Affichage conditionnel des champs Amliorons prsent le groupe de champs qui demande linternaute par quels moyens il souhaite tre contact. Puisque les champs de saisie ne doivent tre remplis que si les cases correspondantes sont coches, nous pouvons les masquer, ainsi que les doubles astrisques, lors du chargement initial du document :
$(document).ready(function() { $('input.conditional').next('span').andSelf().hide(); });

La Figure 8.5 montre que le jeu de champs est prsent plus organis.

208

jQuery

Figure 8.5
Les champs de saisie des mthodes de contact sont initialement masqus.

Pour que ces champs et ces astrisques apparaissent au moment opportun, nous associons la mthode .click() chaque case cocher. Cette opration se fait dans le contexte de chaque champ de saisie conditionnel afin que nous puissions fixer la valeur de deux variables qui seront rutilises par la suite :
$(document).ready(function() { $('input.conditional').next('span').andSelf().hide() .end().end() .each(function() { var $thisInput = $(this); var $thisFlag = $thisInput.next('span'); $thisInput.prev('label').find(':checkbox') .click(function() { // suite du code... }); }); });

Nous appelons nouveau la mthode .end(), cette fois-ci pour que la mthode .each() sapplique au slecteur $('input.conditional') dorigine. Nous disposons prsent dune variable pour le champ de saisie et la rfrence de message en cours. Lorsque lutilisateur clique sur la case cocher, nous vrifions si elle est coche. Dans laffirmative, nous affichons le champ de saisie, la rfrence au message et ajoutons la classe req-label llment <label> parent :
$(document).ready(function() { $('input.conditional').next('span').andSelf().hide() .end().end() .each(function() { var $thisInput = $(this); var $thisFlag = $thisInput.next('span'); $thisInput.prev('label').find(':checkbox') .click(function() { if (this.checked) { $thisInput.show();

Chapitre 8

Manipulation des formulaires

209

$thisFlag.show(); $(this).parent('label').addClass('req-label'); } }); }); });

Dans notre exemple, nous vrifions si une case est coche en utilisant this.checked, car nous avons un accs direct au nud du DOM via le mot cl this. Lorsque le nud du DOM est inaccessible, nous pouvons utiliser $('selector').is(':checked'), car .is() retourne une valeur boolenne (true ou false). Il reste deux points rsoudre : 1. Faire en sorte que les cases soient dcoches lors du chargement initial de la page, car certains navigateurs conservent ltat des lments du formulaire aprs actualisation de la page. 2. Ajouter une clause else qui masque les lments conditionnels et retire la classe req-label lorsque la case est dcoche.
$(document).ready(function() { $('input.conditional').next('span').andSelf().hide() .end().end() .each(function() { var $thisInput = $(this); var $thisFlag = $thisInput.next('span'); $thisInput.prev('label').find(':checkbox') .attr('checked', false) .click(function() { if (this.checked) { $thisInput.show(); $thisFlag.show(); $(this).parent('label').addClass('req-label'); } else { $thisInput.hide(); $thisFlag.hide(); $(this).parent('label').removeClass('req-label'); } }); }); });

Nous concluons ainsi cette partie de la mise en forme du formulaire. Nous allons passer la validation du formulaire ct client. Valider le formulaire Avant dutiliser jQuery pour ajouter la validation dun formulaire, il ne faut pas oublier une rgle importante : la validation ct client ne remplace pas la validation ct serveur. Une fois encore, nous ne pouvons pas supposer que les utilisateurs aient activ JavaScript. Sil faut absolument vrifier que certains champs ont t saisis ou que cette saisie doit se faire dans un format particulier, JavaScript seul ne peut pas garantir le

210

jQuery

rsultat. En effet, certains internautes prfrent dsactiver JavaScript, certains priphriques ne le prennent tout simplement pas en charge et quelques utilisateurs peuvent envoyer intentionnellement des donnes malveillantes en contournant les protections poses par JavaScript. Dans ce cas, pourquoi vouloir mettre en uvre une validation avec jQuery ? La validation dun formulaire sur le client prsente un avantage par rapport la validation ct serveur : un retour immdiat. Le code ct serveur, quil sagisse dASP, de PHP ou de tout autre acronyme, impose un rechargement de la page ( moins que laccs ne soit asynchrone, ce qui, de toute manire, exige JavaScript). Avec jQuery, nous pouvons tirer parti de la rponse rapide du code ct client en effectuant une validation sur chaque champ obligatoire lorsquil perd le focus (vnement blur) ou lors de lappui sur une touche (vnement keyup). Champs obligatoires Pour notre formulaire de contact, nous allons tester la prsence de la classe required sur un champ de saisie lorsque linternaute utilise la touche Tab ou clique en dehors du champ. Avant dexaminer le code, nous devons revenir rapidement aux champs de saisie conditionnels. Pour simplifier notre procdure de validation, nous pouvons ajouter la classe required llment <input> lorsquil est affich, et la retirer lorsquil est masqu. Voici la nouvelle version de cette partie du code :
$thisInput.prev('label').find(':checkbox') .attr('checked', false) .click(function() { if (this.checked) { $thisInput.show().addClass('required'); $thisFlag.show(); $(this).parent('label').addClass('req-label'); } else { $thisInput.hide().removeClass('required'); $thisFlag.hide(); $(this).parent('label').removeClass('req-label'); } });

Toutes les classes required tant en place, nous sommes prts signaler lutilisateur que lun des champs requis est vide. Un message est affich aprs lindicateur de champ obligatoire et un style est appliqu llment <li> du champ au travers de la classe warning de manire avertir lutilisateur :
$(document).ready(function() { $('form :input').blur(function() { if ($(this).hasClass('required')) { var $listItem = $(this).parents('li:first'); if (this.value == '') { var errorMessage = 'Ce champ doit tre rempli'; $('<span></span>') .addClass('error-message')

Chapitre 8

Manipulation des formulaires

211

.text(errorMessage) .appendTo($listItem); $listItem.addClass('warning'); } } }); });

Le code comprend deux instructions if pour chaque vnement blur sur un champ du formulaire. La premire vrifie lexistence de la classe required, tandis que la seconde teste si la valeur du champ est une chane vide. Lorsque les deux conditions sont satisfaites, nous construisons un message derreur, le plaons dans <span class="errormessage"> et lajoutons au <li> parent. Lorsque le champ vide est lun des champs conditionnels (il nest obligatoire que si la case correspondante est coche), nous souhaitons modifier lgrement le message. Nous allons concatner un message de prcision au message derreur standard. Pour cela, nous imbriquons une instruction if supplmentaire qui vrifie la prsence de la classe conditional lorsque les deux conditions prcdentes sont satisfaites :
$(document).ready(function() { $('form :input').blur(function() { if ($(this).hasClass('required')) { var $listItem = $(this).parents('li:first'); if (this.value == '') { var errorMessage = 'Ce champ doit tre rempli'; if ($(this).hasClass('conditional')) { errorMessage += ', lorsque la case correspondante est coche'; } $('<span></span>') .addClass('error-message') .text(errorMessage) .appendTo($listItem); $listItem.addClass('warning'); } } }); });

Notre code fonctionne parfaitement la premire fois que lutilisateur quitte un champ vide. Cependant, deux problmes apparaissent lorsquil entre et quitte nouveau le champ (voir Figure 8.6). Si le champ reste vide, le message derreur est rpt autant de fois que lutilisateur quitte le champ. Si du texte est saisi dans le champ, la classe warning nest pas retire. Bien videmment, nous voulons afficher un seul message par champ et le retirer lorsque lutilisateur corrige lerreur. Nous rsolvons les deux problmes en retirant la classe warning de llment <li> parent du champ en cours et tout <span class="errormessage"> du mme <li> lors du dclenchement de lvnement blur sur le champ, avant dexcuter la validation :

212

jQuery

Figure 8.6
Problme de rptition du message derreur. $(document).ready(function() { $('form :input').blur(function() { $(this).parents('li:first').removeClass('warning') .find('span.error-message').remove(); if ($(this).hasClass('required')) { var $listItem = $(this).parents('li:first'); if (this.value == '') { var errorMessage = 'Ce champ doit tre rempli'; if ($(this).hasClass('conditional')) { errorMessage += ', lorsque la case correspondante est coche'; } $('<span></span>') .addClass('error-message') .text(errorMessage) .appendTo($listItem); $listItem.addClass('warning'); } } }); });

Notre script de validation des champs obligatoires et des champs obligatoires conditionnels est enfin oprationnel. En entrant et en quittant plusieurs fois des champs obligatoires, les messages derreur saffichent prsent correctement (voir Figure 8.7). Cependant, nous devons encore retirer la classe warning de llment <li> et ses lments <span class="error-message"> lorsque lutilisateur dcoche une case. Pour cela, nous revenons au code prcdent de traitement des cases cocher et ajoutons le dclenchement de lvnement blur sur le champ de texte correspondant lorsque la case est dcoche :

Chapitre 8

Manipulation des formulaires

213

Figure 8.7
Laffichage des messages derreur se fait correctement. if (this.checked) { $thisInput.show().addClass('required'); $thisFlag.show(); $(this).parent('label').addClass('req-label'); } else { $thisInput.hide().removeClass('required').blur(); $thisFlag.hide(); $(this).parent('label').removeClass('req-label'); }

Dsormais, lorsquune case est dcoche, les styles davertissement et les messages derreur disparaissent. Formats obligatoires Nous devons encore mettre en uvre un autre type de validation dans notre formulaire de contact. Il concerne les formats de saisie. Il est parfois utile dafficher un avertissement lorsque le texte indiqu dans un champ nest pas correct (au lieu quil soit simplement vide). Les champs de saisie dune adresse lectronique, dun numro de tlphone ou dun numro de carte bancaire font de parfaits candidats ce type davertissement. titre dexemple, nous allons mettre en place une expression rgulire relativement simple pour tester ladresse lectronique saisie. Avant dexaminer en dtail cette expression rgulire, voici lintgralit du code de validation :
$(document).ready(function() { // ... suite du code ... if (this.id == 'email') { var $listItem = $(this).parents('li:first'); if ($(this).is(':hidden')) { this.value = ''; }

214

jQuery

if (this.value != '' && !/.+@.+\.[a-zA-Z]{2,4}$/.test(this.value)) { var errorMessage = "Veuillez utiliser un format dadresse valide" + ' (par exemple paul@example.com)'; $('<span></span>') .addClass('error-message') .text(errorMessage) .appendTo($listItem); $listItem.addClass('warning'); } } // ... suite du code ... });

Ce code ralise les oprations suivantes :


n

Tester si lidentifiant du champ correspond celui de ladresse lectronique. Dans laffirmative :


n n

Affecter llment de liste parent une variable. Vrifier si le champ dadresse est masqu. Dans laffirmative (ce qui se produit lorsque la case correspondante est dcoche), fixer sa valeur une chane vide. Ainsi, le code de suppression de la classe davertissement et du message derreur fonctionne correctement pour ce champ. Vrifier que le champ ne contient pas une chane vide et que sa valeur ne correspond pas lexpression rgulire. En cas de russite des deux tests :
n n n

Crer un message derreur. Insrer le message dans un lment <span class="error-message">. Ajouter le <span class="error-message"> et son contenu llment de liste parent. Appliquer la classe warning llment de liste parent.

Examinons prsent lexpression rgulire seule :


!/.+@.+\.[a-zA-Z]{2,4}$/.test(this.value)

Bien quelle ressemble celle cre prcdemment dans ce chapitre, elle utilise la mthode .test() la place de .replace(). En effet, nous souhaitons simplement quelle retourne true ou false, sans procder un remplacement. Lexpression rgulire est place entre les deux barres obliques. Elle est ensuite compare la chane qui se trouve entre les parenthses de .test() ; dans ce cas, la valeur du champ de saisie est ladresse lectronique. Avec cette expression rgulire, nous recherchons un groupe de un ou de plusieurs caractres (.+), suivi dun symbole @, suivi dun autre groupe de un ou de plusieurs caractres. Jusque-l, une chane comme lucie@example passe le test avec succs,

Chapitre 8

Manipulation des formulaires

215

comme des millions dautres variantes, mme sil ne sagit pas dune adresse lectronique valide. Nous pouvons rendre le test plus prcis, en recherchant un caractre ., suivi de deux quatre lettres (de a z) la fin de la chane. Cest prcisment ce que fait la partie restante de lexpression rgulire. Elle commence par rechercher un caractre entre a et z ou entre A et Z ([a-zA-Z]). Elle prcise ensuite quune lettre dans cette plage peut apparatre entre deux et quatre fois uniquement ({2,4}). Enfin, elle insiste pour que ces deux quatre lettres apparaissent la fin de la chane ($). prsent, une chane comme lucie@example.com retournera true, tandis que lucie@example, lucie@example.2fn, lucie@example.example ou lucia-example.com retournera false. Cependant, nous voulons que true soit retourn (et le code de traitement correspondant, excut) uniquement si ladresse saisie nest pas dans le bon format. Cest pourquoi nous faisons prcder lexpression de test par un point dexclamation (oprateur non) :
!/.+@.+\.[a-zA-Z]{2,4}$/.test(this.value)

Vrification finale Le code de validation du formulaire de contact est presque termin. Nous pouvons vrifier une nouvelle fois les champs du formulaire lorsque lutilisateur tente de le soumettre, mais la vrification est cette fois-ci globale. partir du gestionnaire dvnements .submit() associ au formulaire, non au bouton ENVOYER, nous dclenchons lvnement blur sur tous les champs obligatoires :
$(document).ready(function() { $('form').submit(function() { $('#submit-message').remove(); $(':input.required').trigger('blur'); }); });

Vous remarquerez que nous avons ajout une ligne pour retirer un lment <div id="submit-message"> qui nexiste pas encore. Nous lajouterons ltape suivante. Nous le supprimons ici car nous savons dj que nous devrons le faire, en raison des problmes de cration de multiples messages derreur rencontrs prcdemment. Aprs avoir dclench lvnement blur, nous connaissons le nombre total de classes warning prsentes dans le formulaire. Sil est positif, nous crons un nouvel lment <div id="submit-message"> et linsrons avant le bouton ENVOYER, l o lutilisateur le remarquera. Nous arrtons galement la soumission du formulaire :
$(document).ready(function() { $('form').submit(function() { $('#submit-message').remove(); $(':input.required').trigger('blur');

216

jQuery

var numWarnings = $('.warning', this).length; if (numWarnings) { $('<div></div>') .attr({ 'id': 'submit-message', 'class': 'warning' }) .append('Veuillez corriger les erreurs avec les ' + numWarnings + ' champs') .insertBefore('#send'); return false; } }); });

Outre la demande gnrique de correction des erreurs, le message indique le nombre de champs concerns (voir Figure 8.8).
Figure 8.8
Linternaute doit corriger ses erreurs de saisie dans trois champs.

Nous pouvons nanmoins faire mieux. Au lieu dindiquer simplement le nombre derreurs, nous pouvons afficher la liste des champs concerns :
$(document).ready(function() { $('form').submit(function() { $('#submit-message').remove(); $(':input.required').trigger('blur'); var numWarnings = $('.warning', this).length; if (numWarnings) { var list = []; $('.warning label').each(function() { list.push($(this).text()); }); $('<div></div>') .attr({ 'id': 'submit-message', 'class': 'warning' }) .append('Veuillez corriger les erreurs dans les ' + numWarnings + ' champs suivants :<br />') .append('&bull; ' + list.join('<br />&bull; ')) .insertBefore('#send'); return false; }; }); });

Chapitre 8

Manipulation des formulaires

217

La premire modification du code se trouve dans la variable list, qui contient initialement un tableau vide. Ensuite, nous prenons chaque libell qui est un descendant dun lment possdant la classe warning et plaons le texte quil contient dans le tableau list (avec la fonction JavaScript native push). Le texte de chaque libell est un lment distinct dans le tableau list. Nous modifions lgrement le message du <div id="submit-message"> et y ajoutons le tableau list. En utilisant la fonction JavaScript native join() pour convertir le tableau en chane de caractres, nous insrons chaque lment du tableau, avec un passage la ligne et une puce (voir Figure 8.9).
Figure 8.9
La liste des champs corriger.

Nous ladmettons, le balisage HTML de la liste des champs a un objectif de prsentation, non de smantique. Toutefois, pour une liste phmre, qui est gnre par le code JavaScript lors de la dernire tape et qui sera dtruite ds que possible, nous excuserons ce code dvelopp rapidement, dans un souci de concision et de facilit. Manipuler des cases cocher Notre formulaire de contact comprend galement une partie DIVERS constitue dune liste de cases cocher (voir Figure 8.10). Pour complter nos amliorations du formulaire de contact, nous allons aider lutilisateur grer cette liste. Une suite de dix cases risque de le dcourager, en particulier sil veut en cocher la majorit, voire toutes. Dans une telle situation, une option qui permet de cocher ou de dcocher toutes les cases sera utile. Nous allons donc lajouter. Nous crons tout dabord un nouvel lment <li>, y plaons un <label>, dans lequel nous insrons <input type="checkbox" id="discover-all"> et du texte. Nous ajoutons le tout llment <ul> dans <li class="discover"> :
$(document).ready(function() { $('<li></li>') .html('<label><input type="checkbox" id="discover-all" />' + ' <em>cocher tout</em></label>') .prependTo('li.discover > ul'); });

218

jQuery

Figure 8.10
La section des cases cocher.

Nous disposons prsent dune nouvelle case cocher avec le libell COCHER TOUT. Cependant, pour quelle fasse quelque chose, nous devons lui associer une mthode .click() :
$(document).ready(function() { $('<li></li>') .html('<label><input type="checkbox" id="discover-all" />' + ' <em>cocher tout</em></label>') .prependTo('li.discover > ul'); $('#discover-all').click(function() { var $checkboxes = $(this).parents('ul:first').find(':checkbox'); if (this.checked) { $checkboxes.attr('checked', true); } else { $checkboxes.attr('checked', ''); } }); });

Dans ce gestionnaire dvnements, nous fixons tout dabord la valeur de la variable $checkboxes, qui est constitue dun objet jQuery contenant chaque case cocher de la liste. Grce cette affectation, nous devons simplement cocher les cases si COCHER TOUT est coche et les dcocher si COCHER TOUT est dcoche. La touche finale consiste ajouter une classe checkall au libell de la case COCHER TOUT et modifier son texte DCOCHER TOUT aprs quelle a t coche par lutilisateur :

Chapitre 8

Manipulation des formulaires

219

$(document).ready(function() { $('<li></li>') .html('<label><input type="checkbox" id="discover-all" />' + ' <em>cocher tout</em></label>') .prependTo('li.discover > ul'); $('#discover-all').click(function() { var $checkboxes = $(this) .parents('ul:first').find(':checkbox'); if (this.checked) { $(this).next().text(' dcocher tout'); $checkboxes.attr('checked', true); } else { $(this).next().text(' cocher tout'); $checkboxes.attr('checked', ''); }; }) .parent('label').addClass('checkall'); });

La Figure 8.11 prsente le groupe de cases cocher, avec la case COCHER TOUT.
Figure 8.11
Une nouvelle case permet de cocher toutes les autres cases en un clic.

Aprs avoir coch la case Figure 8.12.

COCHER TOUT,

le groupe se prsente tel quillustr la

220

jQuery

Figure 8.12
La nouvelle case permet galement de dcocher toutes les autres cases.

Version finale du code Voici la version finale du code pour le formulaire de contact :
$(document).ready(function() { // Amliorer le style des lments de formulaire. $('legend').each(function(index) { $(this).replaceWith('<h3>' + $(this).text() + '</h3>'); }); var var var var requiredFlag = ' * '; conditionalFlag = ' ** '; requiredKey = $('input.required:first').next('span').text(); conditionalKey = $('input.conditional:first').next('span').text();

requiredKey = requiredFlag + requiredKey.replace(/^\((.+)\)$/,'$1'); conditionalKey = conditionalFlag + conditionalKey.replace(/^\((.+)\)$/,'$1'); $('<p></p>') .addClass('field-keys') .append(requiredKey + '<br />') .append(conditionalKey) .insertBefore('#contact'); $('form :input') .filter('.required') .next('span').text(requiredFlag).end() .prev('label').addClass('req-label').end() .end() .filter('.conditional') .next('span').text(conditionalFlag); // Cases cocher : champs de saisie conditionnels. $('input.conditional').next('span').andSelf().hide() .end().end() .each(function() { var $thisInput = $(this); var $thisFlag = $thisInput.next('span'); $thisInput.prev('label').find(':checkbox') .attr('checked', false) .click(function() {

Chapitre 8

Manipulation des formulaires

221

if (this.checked) { $thisInput.show().addClass('required'); $thisFlag.show(); $(this).parent('label').addClass('req-label'); } else { $thisInput.hide().removeClass('required').blur(); $thisFlag.hide(); $(this).parent('label').removeClass('req-label'); } }); }); // Valider les champs sur lvnement. $('form :input').blur(function() { $(this).parents('li:first').removeClass('warning') .find('span.error-message').remove(); if ($(this).hasClass('required')) { var $listItem = $(this).parents('li:first'); if (this.value == '') { var errorMessage = 'Ce champ doit tre rempli'; if ($(this).is('.conditional')) { errorMessage += ', lorsque la case correspondante est coche'; } $('<span></span>') .addClass('error-message') .text(errorMessage) .appendTo($listItem); $listItem.addClass('warning'); } } if (this.id == 'email') { var $listItem = $(this).parents('li:first'); if ($(this).is(':hidden')) { this.value = ''; } if (this.value != '' && !/.+@.+\.[a-zA-Z]{2,4}$/.test(this.value)) { var errorMessage = "Veuillez utiliser un format dadresse valide" + ' (par exemple paul@example.com)'; $('<span></span>') .addClass('error-message') .text(errorMessage) .appendTo($listItem); $listItem.addClass('warning'); } } }); // Valider le formulaire la soumission. $('form').submit(function() { $('#submit-message').remove(); $(':input.required').trigger('blur'); var numWarnings = $('.warning', this).length;

222

jQuery

if (numWarnings) { var fieldList = []; $('.warning label').each(function() { fieldList.push($(this).text()); }); $('<div></div>') .attr({ 'id': 'submit-message', 'class': 'warning' }) .append('Veuillez corriger les erreurs dans les ' + numWarnings + ' champs suivants :<br />') .append('&bull; ' + fieldList.join('<br />&bull; ')) .insertBefore('#send'); return false; }; }); // Cases cocher. $('form :checkbox').removeAttr('checked'); // Cases cocher avec (d)cocher tout. $('<li></li>') .html('<label><input type="checkbox" id="discover-all" />' + ' <em>cocher tout</em></label>') .prependTo('li.discover > ul'); $('#discover-all').click(function() { var $checkboxes = $(this) .parents('ul:first').find(':checkbox'); if (this.checked) { $(this).next().text(' dcocher tout'); $checkboxes.attr('checked', true); } else { $(this).next().text(' cocher tout'); $checkboxes.attr('checked', ''); }; }) .parent('label').addClass('checkall'); });

Nous avons apport de nombreuses amliorations au formulaire de contact, mais nous pouvons aller beaucoup plus loin, notamment en ce qui concerne la validation. Vous trouverez un plugin de validation flexible ladresse http://plugins.jquery.com/project/validate/.

8.2

Formulaires compacts

Certains formulaires sont beaucoup plus simples que des formulaires de contact. En ralit, de nombreux sites ajoutent souvent chaque page un formulaire comprenant un seul champ : une fonction de recherche sur le site. Les habituelles informations prsentes dans un formulaire, comme les libells des champs, les boutons de soumission et le texte, sont superflues dans une portion de la page aussi petite. Nous pouvons utiliser jQuery pour rduire la taille du formulaire tout en conservant ses fonctionnalits et

Chapitre 8

Manipulation des formulaires

223

mme amliorer son comportement pour quil soit plus facile utiliser que son quivalent pleine page. Texte substituable pour les champs Llment <label> dun champ de formulaire est un composant indispensable pour laccessibilit des sites web. Chaque champ doit tre libell afin que les lecteurs dcran et les autres dispositifs dassistance puissent identifier le champ utilis et son rle. Par ailleurs, dans le code source HTML, le libell permet de dcrire le champ :
<form id="search" action="search/index.php" method="get"> <label for="search-text">rechercher sur le site</label> <input type="text" name="search-text" id="search-text" /> </form>

Sans style particulier, le libell est plac avant le champ (voir Figure 8.13).
Figure 8.13
Le libell dun champ.

Mme si la place occupe par le libell est rduite, cette seule ligne de texte peut tre de trop avec la mise en page de certains sites. Nous pourrions masquer le texte avec un style CSS, mais lutilisateur ne sera plus inform du rle du champ. la place, nous allons ajouter une classe au formulaire de recherche et utiliser CSS pour positionner le libell au-dessus du champ, uniquement lorsque JavaScript est activ :
$(document).ready(function() { var $search = $('#search').addClass('overlabel'); });

Grce cette seule ligne, nous ajoutons une classe au formulaire de recherche et enregistrons le slecteur dans une variable pour pouvoir y faire rfrence ultrieurement. La feuille de style dfinit des rgles pour la classe overlabel :
.overlabel { position: relative; } .overlabel label { position: absolute; top: 6px; left: 3px; color: #999; cursor: text; }

224

jQuery

La classe ajoute positionne correctement le libell et affiche le texte en gris afin dindiquer quil pourra tre remplac (voir Figure 8.14).
Figure 8.14
Le libell est superpos au champ.

Leffet obtenu est intressant, mais il prsente deux problmes (voir Figure 8.15) : 1. Le libell masque le texte que lutilisateur saisit dans le champ. 2. Pour entrer dans le champ de saisie, il faut utiliser la touche Tab. En effet, le libell recouvre le champ, ce qui empche lutilisateur de cliquer dans la zone de saisie.
Figure 8.15
Le libell masque la saisie.

Pour rsoudre le premier problme, nous devons masquer le texte du libell lorsque le champ reoit le focus, et lafficher nouveau lorsquil le perd et que lutilisateur na rien saisi dans le champ.
INFO
Pour de plus amples informations concernant le focus du clavier, consultez le Chapitre 3.

Le code qui masque le texte du libell est relativement simple :


$(document).ready(function() { var $search = $('#search').addClass('overlabel'); var $searchInput = $search.find('input'); var $searchLabel = $search.find('label'); $searchInput .focus(function() { $searchLabel.hide(); }) .blur(function() { if (this.value == '') {

Chapitre 8

Manipulation des formulaires

225

$searchLabel.show(); } }); });

Le libell est dsormais masqu lorsque lutilisateur saisit du texte dans le champ (voir Figure 8.16).
Figure 8.16
Lors de la saisie, le libell disparat.

Le second problme est prsent relativement facile rsoudre. Nous pouvons masquer le texte du libell et donner lutilisateur accs au champ de saisie en faisant en sorte quun clic sur le libell dclenche lvnement focus sur le champ :
$(document).ready(function() { var $search = $('#search').addClass('overlabel'); var $searchInput = $search.find('input'); var $searchLabel = $search.find('label'); $searchInput .focus(function() { $searchLabel.hide(); }) .blur(function() { if (this.value == '') { $searchLabel.show(); } }); $searchLabel.click(function() { $searchInput.trigger('focus'); }); });

Enfin, nous devons traiter le cas o du texte est conserv dans le champ de saisie aprs lactualisation de la page il sagit dun cas semblable celui rencontr prcdemment avec les champs de saisie conditionnels pour la validation du formulaire. Lorsque le champ de saisie contient une valeur, le libell est masqu :
$(document).ready(function() { var $search = $('#search').addClass('overlabel'); var $searchInput = $search.find('input'); var $searchLabel = $search.find('label'); if ($searchInput.val()) { $searchLabel.hide(); }

226

jQuery

$searchInput .focus(function() { $searchLabel.hide(); }) .blur(function() { if (this.value == '') { $searchLabel.show(); } }); $searchLabel.click(function() { $searchInput.trigger('focus'); }); });

Nous avons choisi dutiliser un libell au lieu dinsrer une valeur par dfaut directement dans le champ de saisie. En effet, cette technique a pour avantage de pouvoir tre adapte nimporte quel champ de texte, sans avoir sinquiter dun ventuel conflit avec un script de validation. Auto-compltion AJAX Pour dvelopper le champ de recherche, nous allons lui offrir une auto-compltion de son contenu. Cette fonctionnalit permettra aux utilisateurs de commencer la saisie du terme recherch et de se voir prsenter tous les termes possibles qui commencent par les caractres dj saisis. Puisque la liste des termes peut tre extraite dune base de donnes associe au site, lutilisateur sait que la recherche du terme propos produira un rsultat. Par ailleurs, si la base de donnes propose les termes par ordre de popularit ou par nombre de rsultats, lutilisateur sera guid dans ses recherches. Lauto-compltion est un sujet trs complexe, avec des subtilits introduites par les diffrentes actions de lutilisateur. Nous allons mettre en uvre un exemple oprationnel, mais nous navons pas la place dexplorer tous les concepts labors, comme limiter le dbit des requtes ou la compltion multiterme. Le widget dauto-compltion disponible dans la bibliothque jQuery UI peut tre utilis pour les implmentations relles simples et peut servir de point de dpart pour des mises en uvre plus complexes. Vous le trouverez sur le site http://ui.jquery.com/. Pour crire une procdure dauto-compltion, lide est de ragir aux appuis sur les touches du clavier et denvoyer une requte AJAX au serveur en passant le contenu du champ. La rponse contiendra une liste des compltions possibles pour le champ. Le script prsente ensuite cette liste dans une fentre affiche sous le champ. Ct serveur Nous avons besoin dun code ct serveur pour traiter les requtes. Dans le cas dune mise en uvre relle, nous utiliserions une base de donnes pour produire la liste des compltions possibles. Dans notre exemple, un simple script PHP fournit une rponse fige :

Chapitre 8

Manipulation des formulaires

227

<?php if (strlen($_REQUEST['search-text']) < 1) { print '[]'; exit; } $terms = array( 'accs', 'action', // suite de la liste... 'xaml', 'xoops', ); $possibilities = array(); foreach ($terms as $term) { if (strpos($term, strtolower($_REQUEST['search-text'])) === 0) { $possibilities[] = "'". str_replace("'", "\\'", $term)."'"; } } print ('['. implode(', ', $possibilities) .']');

La page compare la chane fournie avec le dbut de chaque terme et construit un tableau JSON avec les correspondances. Les manipulations effectues sur la chane, avec str_replace() et implode(), sassurent que la sortie du script est une structure JSON correctement formate afin dviter les erreurs JavaScript pendant lanalyse. Ct navigateur Nous pouvons prsent effectuer une requte sur ce script PHP partir de notre code JavaScript :
$(document).ready(function() { var $autocomplete = $('<ul class="autocomplete"></ul>') .hide() .insertAfter('#search-text'); $('#search-text').keyup(function() { $.ajax({ 'url': '../search/autocomplete.php', 'data': {'search-text': $('#search-text').val()}, 'dataType': 'json', 'type': 'GET', 'success': function(data) { if (data.length) { $autocomplete.empty(); $.each(data, function(index, term) { $('<li></li>').text(term).appendTo($autocomplete); }); $autocomplete.show(); } } }); }); });

228

jQuery

Nous devons utiliser lvnement keyup, non keydown ou keypress, pour le dclenchement de la requte AJAX. En effet, keydown ou keypress se produisent pendant la procdure dappui sur la touche, avant que le caractre ne soit entr dans le champ. Si nous ragissions ces vnements et mettions la requte, la liste des suggestions serait des lieues du texte recherch. Aprs la saisie du troisime caractre, par exemple, la requte AJAX serait effectue avec les deux premiers uniquement. En traitant lvnement keyup, nous vitons ce problme. Dans notre feuille de style, nous positionnons la liste des compltions de manire absolue pour quelle chevauche le texte qui se trouve en dessous. Lors de la saisie dans le champ de recherche, nous obtenons les termes reconnus (voir Figure 8.17).
Figure 8.17
Affichage des compltions possibles.

Pour afficher correctement la liste des suggestions, nous devons tenir compte du mcanisme dauto-compltion intgr certains navigateurs web. Les navigateurs mmorisent souvent ce que les utilisateurs ont saisi dans un champ et affichent ces propositions lors de lutilisation suivante du formulaire. Cela risque dtre perturbant lorsque notre mcanisme dauto-compltion est galement en place (voir Figure 8.18). Heureusement, il est possible de dsactiver cette fonctionnalit dans les navigateurs en fixant off lattribut autocomplete du champ du formulaire. Nous pouvons le faire directement dans le contenu HTML, mais nous remettrions alors en cause le principe damlioration progressive car nous dsactiverions la fonctionnalit dauto-compltion du navigateur sans proposer la ntre. Nous ajoutons donc cet attribut depuis le script :
$('#search-text').attr('autocomplete', 'off')

Remplir le champ de recherche Lintrt de notre liste de suggestions est limit si nous ne pouvons pas placer lun des termes proposs dans le champ de recherche. Tout dabord, nous autorisons la confirmation dune suggestion par un clic de souris :

Chapitre 8

Manipulation des formulaires

229

Figure 8.18
Deux mcanismes dauto-compltion en concurrence.

'success': function(data) { if (data.length) { $autocomplete.empty(); $.each(data, function(index, term) { $('<li></li>').text(term) .appendTo($autocomplete) .click(function() { $('#search-text').val(term); $autocomplete.hide(); }); }); $autocomplete.show(); } }

Grce cette modification, llment de la liste sur lequel lutilisateur clique est ajout dans le champ de recherche. Nous masquons galement la fentre des suggestions aprs avoir slectionn une proposition. Navigation au clavier Puisque lutilisateur a dj les mains sur le clavier pour saisir le terme rechercher, pourquoi ne pas lui permettre deffectuer la slection partir du clavier. Pour cela, nous devons conserver une trace de llment slectionn. Tout dabord, nous ajoutons une fonction daide qui enregistre lindice de llment et met en place les effets visuels qui indiquent llment actuellement slectionn :
var selectedItem = null; var setSelectedItem = function(item) { selectedItem = item; if (selectedItem === null) { $autocomplete.hide(); return; } if (selectedItem < 0) { selectedItem = 0; }

230

jQuery

if (selectedItem >= $autocomplete.find('li').length) { selectedItem = $autocomplete.find('li').length - 1; } $autocomplete.find('li').removeClass('selected') .eq(selectedItem).addClass('selected'); $autocomplete.show(); };

La variable selectedItem sera fixe null si aucun lment nest slectionn. Puisque nous invoquons toujours setSelectedItem() pour modifier la valeur de la variable, nous pouvons tre certains que la liste des suggestions est seulement visible lorsquun lment est slectionn. Les deux tests numriques sur la valeur de selectedItem permettent de rester dans la plage de slection valide. Sans eux, selectedItem pourrait prendre nimporte quelle valeur, y compris ngative. La fonction sassure que la valeur actuelle de selectedItem correspond toujours un indice valide dans la liste des suggestions. Voici la version du code fonde sur la nouvelle fonction :
$('#search-text').attr('autocomplete', 'off').keyup(function() { $.ajax({ 'url': '../search/autocomplete.php', 'data': {'search-text': $('#search-text').val()}, 'dataType': 'json', 'type': 'GET', 'success': function(data) { if (data.length) { $autocomplete.empty(); $.each(data, function(index, term) { $('<li></li>').text(term) .appendTo($autocomplete) .mouseover(function() { setSelectedItem(index); }) .click(function() { $('#search-text').val(term); $autocomplete.hide(); }); }); setSelectedItem(0); } else { setSelectedItem(null); } } }); });

Cette nouvelle version prsente des bnfices immdiats. Tout dabord, la liste des suggestions est masque lorsquune recherche ne produit aucun rsultat. Ensuite, nous avons ajout un gestionnaire de mouseover qui met en exergue llment sous le pointeur de la souris. Enfin, la premire proposition est surligne ds laffichage de la liste des suggestions (voir Figure 8.19).

Chapitre 8

Manipulation des formulaires

231

Figure 8.19
Lentre slectionne est repre.

Nous devons prsent faire en sorte que la slection dune proposition puisse se faire laide des touches du clavier. Prise en charge des touches de direction Lattribut keyCode de lobjet event permet de dterminer la touche qui a t enfonce. Nous pouvons ainsi surveiller les codes 38 et 40, qui correspondent aux touches de direction haut et bas, et ragir de faon approprie :
$('#search-text').attr('autocomplete', 'off').keyup(function(event) { if (event.keyCode > 40 || event.keyCode == 8) { // Les touches ayant les codes 40 et infrieurs sont particulires // (Entre, touches de direction, chap, etc.). // Le code 8 correspond la touche Retour arrire. $.ajax({ 'url': '../search/autocomplete.php', 'data': {'search-text': $('#search-text').val()}, 'dataType': 'json', 'type': 'GET', 'success': function(data) { if (data.length) { $autocomplete.empty(); $.each(data, function(index, term) { $('<li></li>').text(term) .appendTo($autocomplete) .mouseover(function() { setSelectedItem(index); }) .click(function() { $('#search-text').val(term); $autocomplete.hide(); }); }); setSelectedItem(0); } else { setSelectedItem(null); }

232

jQuery

} }); } else if (event.keyCode == 38 && selectedItem !== null) { // Appui sur la touche Flche vers le haut. setSelectedItem(selectedItem - 1); event.preventDefault(); } else if (event.keyCode == 40 && selectedItem !== null) { // Appui sur la touche Flche vers le bas. setSelectedItem(selectedItem + 1); event.preventDefault(); } });

Notre gestionnaire de keyup vrifie le keyCode qui a t envoy et agit en consquence. Les requtes AJAX ne sont pas effectues si la touche enfonce est une touche spciale, comme une touche de direction ou la touche chap. Lorsquune touche de direction est dtecte et que la liste de suggestions est affiche, le gestionnaire slectionne llment prcdent (-1) ou suivant (+1) en fonction de la touche. Puisque nous utilisons setSelectedItem() pour garder les valeurs dans la plage dindices valides pour la liste, lutilisateur ne peut pas dpasser les extrmits de la liste. Insrer une suggestion dans le champ Nous devons prsent grer la touche Entre. Lorsque la liste des suggestions est affiche, un appui sur cette touche doit remplir le champ laide de llment slectionn. Puisque nous effectuons cette opration en deux endroits, nous extrayons la procdure de remplissage du champ (crite prcdemment pour un clic de souris) et crons une fonction spare :
var populateSearchField = function() { $('#search-text').val($autocomplete .find('li').eq(selectedItem).text()); setSelectedItem(null); };

Le gestionnaire de click appelle alors cette fonction. Cest galement le cas dans le traitement de la touche Entre :
$('#search-text').keypress(function(event) { if (event.keyCode == 13 && selectedItem !== null) { // Appui sur la touche Entre. populateSearchField(); event.preventDefault(); } });

Ce gestionnaire nest plus associ lvnement keyup, mais keypress. Cette modification est ncessaire pour empcher que lappui sur la touche ne soumette le formulaire. Si nous attendions le dclenchement de lvnement keyup, la soumission serait dj en cours.

Chapitre 8

Manipulation des formulaires

233

Retirer la liste des suggestions Il nous reste un dernier point traiter. La liste des suggestions doit tre masque lorsque lutilisateur choisit de faire autre chose sur la page. Nous devons commencer par prendre en compte la touche chap dans le gestionnaire de keyup pour que lutilisateur puisse fermer la liste de cette manire :
else if (event.keyCode == 27 && selectedItem !== null) { // Appui sur la touche chap. setSelectedItem(null); }

Plus important encore, nous devons masquer la liste lorsque le champ de recherche perd le focus. Voici une premire tentative simple :
$('#search-text').blur(function(event) { setSelectedItem(null); });

Elle a cependant un effet secondaire inattendu. Puisquun clic de souris sur la liste retire le focus du champ, ce gestionnaire est invoqu et la liste est masque. Autrement dit, notre gestionnaire de click dfini prcdemment nest jamais appel et il est impossible dutiliser la souris pour slectionner une proposition dans la liste. La solution ce problme nest pas simple. Le gestionnaire de blur sera toujours appel avant le gestionnaire de click. Une possibilit consiste masquer la liste lors de la perte du focus, mais en commenant par attendre une fraction de seconde :
$('#search-text').blur(function(event) { setTimeout(function() { setSelectedItem(null); }, 250); });

Ainsi, lvnement click a une chance dtre dclench sur llment de la liste avant quelle ne soit masque. Auto-compltion contre recherche dynamique Lexemple prcdent sest focalis sur lauto-compltion du champ de texte car cette technique sapplique de nombreux formulaires. Toutefois, il est parfois prfrable dutiliser une autre approche appele recherche dynamique. Dans ce cas, la recherche du contenu se fait au cours de la saisie de lutilisateur. Dun point de vue fonctionnel, lauto-compltion et la recherche dynamique se ressemblent normment. Dans les deux cas, lappui sur une touche dclenche une requte AJAX vers le serveur, en passant le contenu actuel du champ. Les rsultats sont ensuite placs dans une liste droulante sous le champ. Dans le cas de lauto-compltion, les rsultats correspondent aux termes de recherche possibles. Avec la recherche dynamique, ils correspondent aux pages relles qui contiennent les termes de recherche saisis.

234

jQuery

Du ct JavaScript, le code de mise en uvre de ces deux fonctionnalits est quasiment identique. Nous nentrerons donc pas dans les dtails. Le choix de la technique utiliser est une question de compromis. La recherche dynamique apporte plus dinformations lutilisateur, mais elle demande gnralement plus de ressources. Mentionnons galement que, pour des sites francophones, la recherche peut se faire sans tenir compte des accents, des cdilles et autres caractres spciaux comme les ligatures. Pour cela, il suffit deffectuer dans le script PHP les remplacements appropris laide dexpressions rgulires. Version finale du code Voici la version finale du code pour la prsentation du champ de recherche et la mise en uvre de lauto-compltion :
$(document).ready(function() { var $search = $('#search').addClass('overlabel'); var $searchInput = $search.find('input'); var $searchLabel = $search.find('label'); if ($searchInput.val()) { $searchLabel.hide(); } $searchInput .focus(function() { $searchLabel.hide(); }) .blur(function() { if (this.value == '') { $searchLabel.show(); } }); $searchLabel.click(function() { $searchInput.trigger('focus'); }); var $autocomplete = $('<ul class="autocomplete"></ul>') .hide() .insertAfter('#search-text'); var selectedItem = null; var setSelectedItem = function(item) { selectedItem = item; if (selectedItem === null) { $autocomplete.hide(); return; } if (selectedItem < 0) { selectedItem = 0; } if (selectedItem >= $autocomplete.find('li').length) { selectedItem = $autocomplete.find('li').length - 1; }

Chapitre 8

Manipulation des formulaires

235

$autocomplete.find('li').removeClass('selected') .eq(selectedItem).addClass('selected'); $autocomplete.show(); }; var populateSearchField = function() { $('#search-text').val($autocomplete.find('li').eq(selectedItem).text()); setSelectedItem(null); }; $('#search-text') .attr('autocomplete', 'off') .keyup(function(event) { if (event.keyCode > 40 || event.keyCode == 8) { // Les touches ayant les codes 40 et infrieurs sont // (Entre, touches de direction, chap, etc.). // Le code 8 correspond la touche Espace arrire. $.ajax({ 'url': '../search/autocomplete.php', 'data': {'search-text': $('#search-text').val()}, 'dataType': 'json', 'type': 'GET', 'success': function(data) { if (data.length) { $autocomplete.empty(); $.each(data, function(index, term) { $('<li></li>').text(term) .appendTo($autocomplete) .mouseover(function() { setSelectedItem(index); }).click(populateSearchField); }); setSelectedItem(0); } else { setSelectedItem(null); } } }); } else if (event.keyCode == 38 && selectedItem !== null) // Appui sur la touche Flche vers le haut. setSelectedItem(selectedItem - 1); event.preventDefault(); } else if (event.keyCode == 40 && selectedItem !== null) // Appui sur la touche Flche vers le bas. setSelectedItem(selectedItem + 1); event.preventDefault(); } else if (event.keyCode == 27 && selectedItem !== null) // Appui sur la touche chap. setSelectedItem(null); } }).keypress(function(event) { if (event.keyCode == 13 && selectedItem !== null) {

particulires

236

jQuery

// Appui sur la touche Entre. populateSearchField(); event.preventDefault(); } }).blur(function(event) { setTimeout(function() { setSelectedItem(null); }, 250); }); });

8.3

Manipuler des donnes numriques

Les fonctionnalits des formulaires que nous venons dtudier sappliquent aux saisies textuelles effectues par lutilisateur. Toutefois, les donnes contenues dans les formulaires peuvent tre numriques. Lorsque les valeurs manipules sont des nombres, nous pouvons amliorer les formulaires de bien dautres manires. Dans le cadre de notre librairie, le panier dachat constitue un parfait exemple de formulaire numrique. Lutilisateur doit pouvoir actualiser les quantits de produits achets et nous devons lui prsenter des donnes numriques pour les prix et les montants totaux. Structure de la table du panier dachat Le balisage HTML du panier dachat dcrit une structure de table parmi les plus complexes que nous ayons rencontres jusque-l :
<form action="checkout.php" method="post"> <table id="cart"> <thead> <tr> <th class="item">Produit</th> <th class="quantity">Quantit</th> <th class="price">Prix</th> <th class="cost">Total</th> </tr> </thead> <tfoot> <tr class="subtotal"> <td class="item">Sous-total</td> <td class="quantity"></td> <td class="price"></td> <td class="cost">152,95 </td> </tr> <tr class="tax"> <td class="item">TVA</td> <td class="quantity"></td> <td class="price">5,5 %</td> <td class="cost">8,41 </td> </tr> <tr class="shipping"> <td class="item">Livraison</td>

Chapitre 8

Manipulation des formulaires

237

<td class="quantity">5</td> <td class="price">2 par produit</td> <td class="cost">10,00 </td> </tr> <tr class="total"> <td class="item">Total</td> <td class="quantity"></td> <td class="price"></td> <td class="cost">171,36 </td> </tr> <tr class="actions"> <td></td> <td> <input type="button" name="recalculate" value="Recalculer" id="recalculate" /> </td> <td></td> <td> <input type="submit" name="submit" value="Commander" id="submit" /> </td> </tr> </tfoot> <tbody> <tr> <td class="item"> Building Telephony Systems With Asterisk </td> <td class="quantity"> <input type="text" name="quantity-2" value="1" id="quantity-2" maxlength="3" /> </td> <td class="price">26,99 </td> <td class="cost">26,99 </td> </tr> <tr> <td class="item"> Smarty PHP Template Programming and Applications </td> <td class="quantity"> <input type="text" name="quantity-1" value="2" id="quantity-1" maxlength="3" /> </td> <td class="price">35,99 </td> <td class="cost">71,98 </td> </tr> <tr> <td class="item"> Creating your MySQL Database </td> <td class="quantity"> <input type="text" name="quantity-3" value="1" id="quantity-3" maxlength="3" /> </td> <td class="price">17,99 </td> <td class="cost">17,99 </td> </tr>

238

jQuery

<tr> <td class="item"> Drupal: Creating Blogs, Forums, Portals, and Community Websites </td> <td class="quantity"> <input type="text" name="quantity-4" value="1" id="quantity-4" maxlength="3" /> </td> <td class="price">35,99 </td> <td class="cost">35,99 </td> </tr> </tbody> </table> </form>

La table inclut un lment <tfoot> dont lutilisation est plutt rare. linstar de <thead>, il regroupe un ensemble de lignes de la table. Bien que cet lment soit plac avant le corps de la table, son affichage sur la page se fait aprs (voir Figure 8.20).

Figure 8.20
Llment <tfoot> est affich aprs le corps de la table.

Cette organisation du code source, si elle nest pas intuitive aux concepteurs qui rflchissent en termes de prsentation de la table, est utile aux personnes prsentant un handicap visuel. En effet, lorsque la table est lue par des dispositifs dassistance, le pied est lu avant le corps potentiellement long, ce qui permet lutilisateur davoir un rsum du contenu venir. Nous avons galement ajout une classe chaque cellule de la table de manire identifier la colonne qui la contient. Au chapitre prcdent, nous avons expliqu comment retrouver la colonne dune cellule en examinant son indice dans sa ligne. Ici, nous faisons un compromis qui nous permet de simplifier le code JavaScript, au prix dun bali-

Chapitre 8

Manipulation des formulaires

239

sage HTML lgrement plus complexe. Grce la classe qui identifie la colonne de chaque cellule, nos slecteurs seront un peu plus simples. Avant de passer la manipulation des champs du formulaire, nous allons appliquer un effet de bande la table avec notre code habituel :
$(document).ready(function() { $('#cart tbody tr:nth-child(even)').addClass('alt'); });

nouveau, nous faisons en sorte dappliquer les couleurs uniquement aux lignes qui se trouvent dans le corps de la table (voir Figure 8.21).

Figure 8.21
Application des bandes aux lignes des produits.

Rejeter toute saisie non numrique Au cours de lamlioration du formulaire de contact, nous avons prsent quelques techniques de validation des donnes saisies. Nous avons vrifi que le texte entr par lutilisateur correspondait ce que nous attendions, ce qui nous a permis de lui fournir des instructions avant que le formulaire ne soit envoy au serveur. Nous allons prsent examiner le pendant de la validation, appel masque de saisie. La validation confronte les donnes saisies par lutilisateur certains critres dfinissant leur validit. Un masque de saisie applique les critres pendant que lutilisateur entre les donnes et interdit simplement les touches invalides. Par exemple, dans notre formulaire de panier dachat, les champs de saisie ne doivent contenir que des nombres. Nous pouvons crire du code qui annule les effets des appuis sur des touches non numriques :

240

jQuery

$('td.quantity input').keypress(function(event) { if (event.which && (event.which < 48 || event.which > 57)) { event.preventDefault(); } });

Dans la fonction dauto-compltion pour le champ de recherche, nous avons intercept lvnement keyup. Nous avons pu ainsi examiner la proprit .keyCode de lobjet event afin de connatre la touche enfonce. prsent, nous observons la place lvnement keypress. Cet vnement ne dispose pas dune proprit .keyCode, mais propose la place .which. Elle indique le caractre ASCII qui correspond la squence de touches. Si la frappe produit un caractre (autrement dit, il ne sagit pas dune touche de direction, de Suppr ou dune autre touche ddition) et que ce caractre se trouve en dehors de la plage des codes ASCII reprsentant des nombres, nous invoquons la mthode .preventDefault() sur lobjet event. Nous lavons vu prcdemment, cela interrompt le traitement de lvnement par le navigateur. Dans ce cas, cela signifie que le caractre nest pas insr dans le champ. Les champs de quantit acceptent prsent uniquement des nombres. Calculs numriques Passons la manipulation des nombres saisis par lutilisateur dans le formulaire de panier dachat. Ce formulaire comprend un bouton RECALCULER, qui doit dclencher la soumission du formulaire au serveur, o les nouveaux totaux sont calculs, puis le formulaire est nouveau prsent lutilisateur. Toutefois, cet change nest pas ncessaire. Tout ce travail peut tre ralis dans le navigateur avec jQuery. Dans le formulaire, le calcul le plus simple se situe au niveau de la ligne L IVRAISON, qui affiche la quantit totale des produits commands. Lorsque lutilisateur modifie une quantit dans lune des lignes des produits, nous devons additionner toutes les valeurs saisies pour obtenir le nouveau total et lafficher dans la cellule correspondante :
var $quantities = $('td.quantity input'); $quantities.change(function() { var totalQuantity = 0; $quantities.each(function() { var quantity = parseInt(this.value, 10); totalQuantity += quantity; }); $('tr.shipping td.quantity').text(String(totalQuantity)); });

Plusieurs possibilits soffrent nous quant lvnement dclenchant cette opration de calcul. Nous pouvons observer lvnement keypress et effectuer le calcul chaque appui sur une touche. Nous pouvons galement observer lvnement blur, qui est dclench chaque fois que lutilisateur quitte le champ. Toutefois, nous allons tre un peu plus conomes en temps processeur et effectuer les calculs uniquement lors du

Chapitre 8

Manipulation des formulaires

241

dclenchement de lvnement change. Ainsi, nous calculons le nouveau total uniquement si lutilisateur quitte le champ aprs avoir saisi une valeur diffrente. La quantit totale est obtenue laide dune simple boucle .each(). La proprit .value dun champ permet dobtenir sa valeur sous forme dune chane de caractres. Nous utilisons donc la fonction intgre parseInt() pour convertir cette valeur en un entier. Cette approche permet dviter des situations tranges dans lesquelles laddition est interprte comme une concatnation de chanes, puisque ces deux oprations utilisent le mme symbole pour loprateur. linverse, nous devons passer une chane la mthode jQuery .text() pour afficher le rsultat du calcul. Nous utilisons donc la fonction String() pour convertir la quantit totale calcule en une chane de caractres. Lorsque lutilisateur change une quantit, le total est dsormais actualis automatiquement (voir Figure 8.22).

Figure 8.22
Actualisation automatique de la quantit totale.

Parser et mettre en forme la monnaie Nous pouvons prsent passer aux totaux affichs dans la colonne de droite. Chaque cot total dune ligne est calcul en multipliant la quantit saisie par le prix du produit. Puisque chaque ligne fait lobjet de plusieurs oprations, nous commenons par remanier le code de calcul des quantits, en le fondant sur la ligne la place du champ :
$('#cart tbody tr').each(function() { var quantity = parseInt($('td.quantity input', this).val(), 10); totalQuantity += quantity; });

242

jQuery

Nous obtenons le mme rsultat, mais disposons dsormais dun endroit pratique o ajouter le calcul du cot total de chaque ligne :
$('td.quantity input').change(function() { var totalQuantity = 0; $('#cart tbody tr').each(function() { var price = parseFloat($('td.price', this).text() .replace(',', '.').replace(/[^\d\.]/g, '')); price = isNaN(price) ? 0 : price; var quantity = parseInt($('td.quantity input', this).val()); var cost = quantity * price; var costFR = cost.toString().replace('.', ','); $('td.cost', this).text(costFR + ' '); totalQuantity += quantity; }); $('tr.shipping td.quantity').text(String(totalQuantity)); });

Nous rcuprons le prix de chaque produit dans la table en employant la technique dj utilise prcdemment pour trier les lignes en fonction du prix. Lexpression rgulire remplace la virgule par un point et retire tout caractre qui nest ni un chiffre ni un point, puis la chane rsultante est passe parseFloat(), qui interprte la valeur comme un nombre rel. Puisque le calcul se fera avec ce rsultat, nous vrifions quun nombre a t trouv et fixons le prix 0 si ce nest pas le cas. Enfin, nous multiplions le prix par la quantit pour obtenir le cot total. Cette valeur relle est convertie en une chane de caractres, dans laquelle nous remplaons le point par une virgule et que nous plaons ensuite dans la colonne de total en lui ajoutant le symbole . La Figure 8.23 illustre notre calcul des totaux.

Figure 8.23
Calcul du montant total de chaque ligne dun produit.

Chapitre 8

Manipulation des formulaires

243

Grer les chiffres aprs la virgule Mme si nous avons plac le symbole de leuro aprs le montant total, JavaScript ne sait pas que nous manipulons des valeurs montaires. Pour lordinateur, il sagit simplement de nombres, qui doivent tre affichs comme tels. Autrement dit, si les centimes du total se terminent par un ou deux zros, ceux-ci ne sont pas affichs (voir Figure 8.24).

Figure 8.24
Les zros de fin ne sont pas affichs.

Vous le constatez, le total 1079,70 saffiche sous la forme 1079,7 . Pire encore, les limites sur la prcision des calculs peuvent parfois conduire des erreurs darrondi et laisser croire que le rsultat est faux (voir Figure 8.25). Heureusement, la solution ces deux problmes est simple. La classe JavaScript Number offre plusieurs mthodes pour prendre en charge ce genre de cas, et .toFixed() convient parfaitement ici. Cette mthode prend en argument un nombre de chiffres aprs la virgule et retourne une chane qui reprsente le nombre rel arrondi cette prcision :
$('#cart tbody tr').each(function() { var price = parseFloat($('td.price', this).text() .replace(',', '.').replace(/[^\d\.]/g, '')); price = isNaN(price) ? 0 : price; var quantity = parseInt($('td.quantity input', this).val()); var cost = quantity * price; var costFR = cost.toFixed(2).replace('.', ','); $('td.cost', this).text(costFR + ' '); totalQuantity += quantity; });

La Figure 8.26 montre que les totaux ont prsent laspect dune valeur montaire normale.

244

jQuery

Figure 8.25
Effet des limites sur la prcision des calculs.

Figure 8.26
Mise en forme des valeurs montaires.

INFO
Aprs une longue suite doprations arithmtiques, larrondi des nombres rels peut cumuler tellement derreurs que la mthode .toFixed() ne puisse pas les masquer. Dans les applications, la meilleure manire de grer les valeurs montaires consiste les enregistrer et les manipuler en centimes, sous forme dentiers. La virgule est ajoute uniquement lors de laffichage.

Chapitre 8

Manipulation des formulaires

245

Autres calculs Les autres calculs ncessaires sur la page suivent un schma semblable. Pour le soustotal, nous pouvons additionner les totaux de chaque ligne lorsquils sont calculs et afficher le rsultat en utilisant la mise en forme prcdente (voir Figure 8.27) :
$('td.quantity input').change(function() { var totalQuantity = 0; var totalCost = 0; $('#cart tbody tr').each(function() { var price = parseFloat($('td.price', this).text() .replace(',', '.').replace(/[^\d\.]/g, '')); price = isNaN(price) ? 0 : price; var quantity = parseInt($('td.quantity input', this).val()); var cost = quantity * price; var costFR = cost.toFixed(2).replace('.', ','); $('td.cost', this).text(costFR + ' '); totalQuantity += quantity; totalCost += cost; }); $('tr.shipping td.quantity').text(String(totalQuantity)); var subTotalCostFR = totalCost.toFixed(2).replace('.', ','); $('tr.subtotal td.cost').text(subTotalCostFR + ' '); });

Figure 8.27
Calcul et mise en forme du sous-total.

Arrondir des valeurs Pour calculer la TVA, nous devons diviser le taux par 100 et multiplier ensuite la valeur obtenue par le sous-total. Puisque le montant de la TVA est toujours arrondi, nous devons nous assurer que la bonne valeur est utilise la fois dans laffichage et dans les

246

jQuery

calculs ultrieurs. La fonction JavaScript Math.ceil() permet darrondir un nombre lentier suprieur le plus proche, mais, puisque nous manipulons des euros et des centimes deuro, nous devons tre plus astucieux :
var taxRate = parseFloat($('tr.tax td.price').text() .replace(',', '.').replace(/[^\d\.]/g, '')) / 100; var tax = Math.ceil(totalCost * taxRate * 100) / 100; var taxFR = tax.toFixed(2).replace('.', ','); $('tr.tax td.cost').text(taxFR + ' '); totalCost += tax;

Le montant de la TVA est tout dabord multipli par 100 afin dobtenir une valeur en centimes, non en euros. Cette valeur peut ensuite tre arrondie par Math.ceil(), puis divise par 100 pour la convertir nouveau en euros. Enfin, nous invoquons la mthode .toFixed() pour obtenir le rsultat correct (voir Figure 8.28).

Figure 8.28
Calcul du montant de la TVA.

Touches finales Le calcul du montant de la livraison est plus simple que celui de la TVA car, dans notre exemple, il nimplique aucun arrondi. Il suffit de multiplier le cot de livraison dun produit par le nombre de produits :
$('tr.shipping td.quantity').text(String(totalQuantity)); var shippingRate = parseFloat($('tr.shipping td.price').text() .replace(',', '.').replace(/[^\d\.]/g, '')); var shipping = totalQuantity * shippingRate; var shippingFR = shipping.toFixed(2).replace('.', ','); $('tr.shipping td.cost').text(shippingFR + ' '); totalCost += shipping;

Chapitre 8

Manipulation des formulaires

247

Tout au long de nos calculs, nous avons maintenu jour le montant total. Il ne nous reste plus qu mettre en forme la variable totalCost avant de lafficher :
var totalCostFR = totalCost.toFixed(2).replace('.', ','); $('tr.total td.cost').text(totalCostFR + ' ');

prsent, nous avons intgralement reproduit les calculs qui seraient effectus sur le serveur et nous pouvons donc masquer le bouton RECALCULER :
$('#recalculate').hide();

Cette modification illustre nouveau notre principe damlioration progressive. Tout dabord, nous faisons en sorte que la page fonctionne correctement sans JavaScript. Ensuite, nous utilisons jQuery pour effectuer les mmes tches, mais de manire plus lgante lorsque cest possible (voir Figure 8.29).

Figure 8.29
Version jQuery du formulaire de panier dachat.

Retirer des produits Si lutilisateur souhaite retirer des produits de son panier, il peut modifier le champ QUANTIT pour le fixer zro. Cependant, nous pouvons mettre en place un comportement plus rassurant en ajoutant des boutons RETIRER explicites pour chaque produit. Un clic sur le bouton aura le mme effet que la modification du champ QUANTIT, mais le retour visuel renforcera le fait que le produit ne sera pas factur. Nous commenons par ajouter les nouveaux boutons. Puisquils ne fonctionneront pas sans JavaScript, nous ne les plaons pas dans le fichier HTML, mais utilisons jQuery pour les insrer dans chaque ligne :

248

jQuery

$('<th>&nbsp;</th>') .insertAfter('#cart thead th:nth-child(2)'); $('#cart tbody tr').each(function() { $deleteButton = $('<img />').attr({ 'width': '16', 'height': '16', 'src': '../images/cross.png', 'alt': 'retirer du panier', 'title': 'retirer du panier', 'class': 'clickable' }); $('<td></td>') .insertAfter($('td:nth-child(2)', this)) .append($deleteButton); }); $('<td>&nbsp;</td>') .insertAfter('#cart tfoot td:nth-child(2)');

Nous devons crer des cellules vides dans les lignes den-tte et de pied de la table pour que les colonnes restent alignes. Les boutons sont crs et ajouts dans les lignes du corps uniquement (voir Figure 8.30).

Figure 8.30
Ajout des boutons de suppression.

Nous devons prsent associer un comportement aux boutons. Nous changeons la dfinition dun bouton de manire ajouter un gestionnaire de click :
$deleteButton = $('<img />').attr({ 'width': '16', 'height': '16', 'src': '../images/cross.png', 'alt': 'retirer du panier',

Chapitre 8

Manipulation des formulaires

249

'title': 'retirer du panier', 'class': 'clickable' }).click(function() { $(this).parents('tr').find('td.quantity input') .val(0); });

Ce gestionnaire obtient le champ QUANTIT plac sur la mme ligne que le bouton et fixe sa valeur 0. Si le champ est bien mis jour, les calculs sont dsynchroniss (voir Figure 8.31).

Figure 8.31
La suppression dun produit nactualise pas les montants.

Nous devons dclencher les calculs comme si lutilisateur avait manuellement modifi la valeur du champ :
$deleteButton = $('<img />').attr({ 'width': '16', 'height': '16', 'src': '../images/cross.png', 'alt': 'retirer du panier', 'title': 'retirer du panier', 'class': 'clickable' }).click(function() { $(this).parents('tr').find('td.quantity input') .val(0).trigger('change'); });

prsent, les totaux sont actualiss lors dun clic sur le bouton (voir Figure 8.32).

250

jQuery

Figure 8.32
La suppression dun produit actualise les montants.

Passons leffet visuel. Nous masquons la ligne qui correspond au bouton sur lequel lutilisateur a cliqu afin que le produit soit clairement retir du panier (voir Figure 8.33) :
$deleteButton = $('<img />').attr({ 'width': '16', 'height': '16', 'src': '../images/cross.png', 'alt': 'retirer du panier', 'title': 'retirer du panier', 'class': 'clickable' }).click(function() { $(this).parents('tr').find('td.quantity input') .val(0).trigger('change') .end().hide(); });

Mme si la ligne est masque, le champ est toujours prsent dans le formulaire. Autrement dit, il sera envoy avec les autres informations du formulaire et le code ct serveur retirera le produit. Notre effet de bande sur la table est perturb par la disparition de la ligne. Pour corriger ce problme, nous dplaons notre code dapplication de cet effet dans une fonction de manire pouvoir linvoquer nouveau ultrieurement. Nous en profitons pour modifier le code afin que lalternance des bandes tienne compte des lignes masques. Malheureusement, mme si nous cartons les lignes masques par un filtre, nous ne pouvons pas utiliser le slecteur :nth-child(even) car il appliquera la classe alt toutes les lignes visibles qui sont un enfant "pair" de leur parent. Voyons ce qui se passe

Chapitre 8

Manipulation des formulaires

251

Figure 8.33
La ligne du produit retir est masque.

lorsque nous utilisons le code suivant sur les quatre lignes de la table alors que la deuxime est masque :
$('#cart tbody tr').removeClass('alt') .filter(':visible:nth-child(even)').addClass('alt');

Nous obtenons le balisage suivant (rsum) :


<tr> ... </tr> <tr style="display: none"> ... </tr> <tr> ... </tr> <tr class="alt"> ... </tr>

Trois lignes sont visibles, mais la classe alt est applique uniquement la quatrime. Nous avons donc besoin de lexpression de slection :visible:odd puisquelle choisira une ligne sur deux aprs avoir cart de la slection les lignes masques (et elle tient compte du slecteur qui commence compter partir de un, non de zro). Nous lavons vu au Chapitre 2, lutilisation de :odd ou de :even peut avoir des rsultats inattendus si plusieurs lments <tbody> sont prsents ; ce nest pas le cas dans notre exemple. Voici la nouvelle version de la fonction avec ce slecteur :
var stripe = function() { $('#cart tbody tr').removeClass('alt') .filter(':visible:odd').addClass('alt'); }; stripe();

Nous linvoquons aprs la suppression dune ligne (voir Figure 8.34) :


$deleteButton = $('<img />').attr({ 'width': '16', 'height': '16',

252

jQuery

'src': '../images/cross.png', 'alt': 'retirer du panier', 'title': 'retirer du panier', 'class': 'clickable' }).click(function() { $(this).parents('tr').find('td.quantity input') .val(0).trigger('change') .end().hide(); stripe(); });

Figure 8.34
Rapplication des bandes aprs la suppression dun produit.

Nous terminons ainsi une autre amlioration avec jQuery, qui na aucune implication sur le code ct serveur. Pour celui-ci, lutilisateur a simplement saisi la valeur 0 dans un champ du formulaire, mais, pour lutilisateur, il sagit dune opration de suppression diffrente dune modification de la quantit. Modifier les informations de livraison La page du panier dachat comprend galement un formulaire pour les informations de livraison. En ralit, au moment du chargement de la page, il ne sagit pas dun formulaire et, si JavaScript nest pas activ, ces informations sont places dans une petite bote sur le ct droit de la zone de contenu, avec un lien vers une page partir de laquelle lutilisateur peut les modifier (voir Figure 8.35). En revanche, lorsque JavaScript est disponible, et avec la puissance de jQuery, nous pouvons convertir ce petit lien en un formulaire complet. Pour cela, nous allons demander le formulaire une page PHP. En gnral, les donnes du formulaire seront enregistres dans une base de donnes, mais pour le besoin de notre dmonstration nous les conservons de manire statique dans un tableau PHP.

Chapitre 8

Manipulation des formulaires

253

Figure 8.35
La bote affichant les informations de livraison.

Pour obtenir le formulaire et le faire apparatre dans la bote LIVRAISON , nous utilisons la mthode $.get() dans le gestionnaire dvnements .click() :
$(document).ready(function() { $('#shipping-name').click(function() { $.get('shipping.php', function(data) { $('#shipping-name').remove(); $(data).hide().appendTo('#shipping').slideDown(); }); return false; }); });

En vrifiant labsence de la variable serveur $_SERVER['HTTP_X_REQUESTED_WITH'] avant dafficher la plus grande partie de la page, la page PHP (shipping.php) retourne uniquement un fragment de la page complte, le formulaire, suite la requte effectue avec la mthode $.get(). Dans la fonction de rappel de la mthode $.get(), nous supprimons le nom sur lequel nous venons de cliquer et le remplaons par le formulaire et ses donnes fournis par shipping.php. La fonction retourne false afin que lvnement par dfaut faisant suite au clic sur le lien (chargement de la page dsigne par lattribut href) ne se produise pas. La bote LIVRAISON est devenue un formulaire modifiable (voir Figure 8.36). Lutilisateur peut prsent modifier les informations de livraison sans quitter la page. Dans ltape suivante, nous devons dtourner la soumission du formulaire et envoyer les donnes modifies au serveur avec jQuery. Nous commenons par srialiser les donnes saisies dans le formulaire et les enregistrerons dans la variable postData. Nous les envoyons ensuite au serveur en utilisant nouveau shipping.php :
$(document).ready(function() { $('#shipping form').submit(function() { var postData = $(this).serialize(); $.post('shipping.php', postData); return false; }; });

254

jQuery

Figure 8.36
Le formulaire de saisie des informations de livraison.

INFO
Le plugin jQuery Form (http://www.malsup.com/jquery/form/) propose une mthode .serialize() plus robuste. Nous vous conseillons de lutiliser dans la plupart des cas de soumission dun formulaire avec AJAX.

ce stade, nous pouvons supprimer le formulaire et remettre la bote LIVRAISON dans son tat dorigine. Nous effectuons cette opration dans la fonction de rappel de la mthode $.post() que nous venons demployer :
$(document).ready(function() { $('#shipping form').submit(function() { var postData = $(this).serialize(); $.post('shipping.php', postData, function(data) { $('#shipping form').remove(); $(data).appendTo('#shipping'); }); return false; }; });

Mais cela ne fonctionne pas ! En procdant ainsi, le gestionnaire dvnements .submit() est li au formulaire LIVRAISON ds que le DOM est charg, mais le formulaire ne se trouve dans le DOM quaprs que lutilisateur a cliqu sur le nom du destinataire. Lvnement ne peut pas tre associ quelque chose qui nexiste pas. Pour contourner ce problme, nous plaons le code de cration du formulaire dans une fonction nomme editShipping() et le code de soumission ou de suppression du formulaire dans une fonction nomme saveShipping(). Nous pouvons ensuite lier saveShipping() dans la fonction de rappel de $.get(), aprs la cration du formulaire.

Chapitre 8

Manipulation des formulaires

255

De la mme manire, nous pouvons lier editShipping() lorsque le DOM est prt et que le lien de modification de la livraison est recr dans la fonction de rappel de $.post() :
$(document).ready(function() { var editShipping = function() { $.get('shipping.php', function(data) { $('#shipping-name').remove(); $(data).hide().appendTo('#shipping').slideDown(); $('#shipping form').submit(saveShipping); }); return false; }; var saveShipping = function() { var postData = $('#shipping :input').serialize(); $.post('shipping.php', postData, function(data) { $('#shipping form').remove(); $(data).appendTo('#shipping'); $('#shipping-name').click(editShipping); }); return false; }; $('#shipping-name').click(editShipping); });

Le code a cr une sorte de motif circulaire, dans lequel une fonction permet lautre de lier nouveau ses gestionnaires dvnements respectifs. Version finale du code En runissant le tout, le code du panier dachat occupe environ quatre-vingts lignes. Cela reprsente finalement assez peu si lon considre les fonctionnalits proposes, en particulier la faon de coder pour amliorer la lisibilit. Si la rduction du nombre de lignes avait t un aspect important, plusieurs instructions jQuery auraient pu tre fusionnes grce au chanage. Quoi quil en soit, voici la version finale du code de mise en uvre de la page du panier dachat, qui conclut ce chapitre sur les formulaires :
$(document).ready(function() { var stripe = function() { $('#cart tbody tr').removeClass('alt') .filter(':visible:odd').addClass('alt'); }; stripe(); $('#recalculate').hide(); $('.quantity input').keypress(function(event) { if (event.which && (event.which < 48 || event.which > 57)) { event.preventDefault(); }

256

jQuery

}).change(function() { var totalQuantity = 0; var totalCost = 0; $('#cart tbody tr').each(function() { var price = parseFloat($('.price', this).text() .replace(',', '.').replace(/[^\d\.]/g, '')); price = isNaN(price) ? 0 : price; var quantity = parseInt($('.quantity input', this).val(), 10); var cost = quantity * price; var costFR = cost.toFixed(2).replace('.', ','); $('.cost', this).text(costFR + ' '); totalQuantity += quantity; totalCost += cost; }); var subTotalCostFR = totalCost.toFixed(2).replace('.', ','); $('.subtotal .cost').text(subTotalCostFR + ' '); var taxRate = parseFloat($('.tax .price').text() .replace(',', '.').replace(/[^\d\.]/g, '')) / 100; var tax = Math.ceil(totalCost * taxRate * 100) / 100; var taxFR = tax.toFixed(2).replace('.', ','); $('.tax .cost').text(taxFR + ' '); totalCost += tax; $('.shipping .quantity').text(String(totalQuantity)); var shippingRate = parseFloat($('.shipping .price').text() .replace(',', '.').replace(/[^\d\.]/g, '')); var shipping = totalQuantity * shippingRate; var shippingFR = shipping.toFixed(2).replace('.', ','); $('.shipping .cost').text(shippingFR + ' '); totalCost += shipping; var totalCostFR = totalCost.toFixed(2).replace('.', ','); $('.total .cost').text(totalCostFR + ' '); }); $('<th>&nbsp;</th>') .insertAfter('#cart thead th:nth-child(2)'); $('#cart tbody tr').each(function() { $deleteButton = $('<img />').attr({ 'width': '16', 'height': '16', 'src': '../images/cross.png', 'alt': 'retirer du panier', 'title': 'retirer du panier', 'class': 'clickable' }).click(function() { $(this).parents('tr').find('td.quantity input') .val(0).trigger('change') .end().hide(); stripe(); }); $('<td></td>') .insertAfter($('td:nth-child(2)', this)) .append($deleteButton); }); $('<td>&nbsp;</td>') .insertAfter('#cart tfoot td:nth-child(2)'); });

Chapitre 8

Manipulation des formulaires

257

$(document).ready(function() { var editShipping = function() { $.get('shipping.php', function(data) { $('#shipping-name').remove(); $(data).hide().appendTo('#shipping').slideDown(); $('#shipping form').submit(saveShipping); }); return false; }; var saveShipping = function() { var postData = $(this).serialize(); $.post('shipping.php', postData, function(data) { $('#shipping form').remove(); $(data).appendTo('#shipping'); $('#shipping-name').click(editShipping); }); return false; }; $('#shipping-name').click(editShipping); });

8.4

En rsum

Dans ce chapitre, nous avons propos des solutions pour amliorer laspect et le comportement des lments de formulaires HTML. Nous avons appris appliquer des styles aux formulaires tout en conservant leur balisage smantique dorigine, masquer et afficher de manire conditionnelle des champs en fonction de la valeur dautres champs, et valider le contenu des champs avant la soumission du formulaire et pendant la saisie des donnes. Nous avons examin dautres fonctionnalits, comme lautocompltion AJAX pour les champs de texte, lautorisation de la saisie de certains caractres dans un champ et le calcul sur des valeurs numriques dans des champs. Nous avons galement vu comment soumettre des formulaires avec AJAX, sans actualisation de la page. Llment form constitue souvent le cur dun site interactif. Grce jQuery, nous pouvons facilement amliorer le processus de remplissage des formulaires, tout en conservant leur utilit et leur souplesse.

9
Carrousels et prompteurs
Au sommaire de ce chapitre
U Prompteur de nouvelles U Carrousel dimages U En rsum

Nous avons tudi plusieurs faons de masquer les informations lorsquelles ne sont pas utiles et de les rvler la demande, notamment les accordons escamotables. Nanmoins, ces solutions taient relativement lmentaires et nous souhaitons parfois que laffichage et le masquage des informations se fassent avec plus de classe. Parmi ces animations labores, on trouve entre autres les carrousels, les diaporamas et les prompteurs. Elles ont toutes en commun une mise en uvre accrocheuse et attrayante pour passer rapidement dun lment dinformation un autre. Dans ce troisime et dernier chapitre de mise en application, nous allons explorer ces animations et les combiner des techniques AJAX et des styles CSS pour pater linternaute. Nous dtaillerons deux exemples : un prompteur de nouvelles et un carrousel dimages. Ils nous permettront dapprendre mettre en uvre les points suivants :
n n n n n n n

animer la position dun lment ; parser des documents XML ; prparer des effets de style labors fonds sur une opacit partielle ; obtenir des informations provenant de sources diffrentes ; crer un lment dinterface utilisateur pour le dfilement horizontal ; composer des calques pour des badges et des superpositions ; effectuer un zoom sur une image.

260

jQuery

9.1

Prompteur de nouvelles

Pour notre premier exemple, un prompteur de nouvelles, nous allons crer un flux dactualit et afficher chaque titre, ainsi quun rsum de larticle, par dfilement. Le texte sera affich progressivement, le restera pendant un certain temps pour que lutilisateur puisse le lire, puis glissera hors de la page comme sil tait crit sur un tambour tournant en boucle sur la page. Prparer la page La mise en uvre dune version basique de cette fonctionnalit nest pas trs complexe. Toutefois, en faire une version de production exige un certain doigt. Comme toujours, nous commenons par un contenu HTML. Le flux dactualit sera plac dans la barre latrale de la page :
<h3>Actualits</h3> <div id="news-feed"> <a href="news/index.html">Nouvelles versions</a> </div>

Pour le moment, la zone qui affiche le flux dactualit contient un seul lien vers la page principale des nouvelles (voir Figure 9.1).
Figure 9.1
Aspect initial du flux dactualit.

Il sagit de notre scnario de dgradation lgante, pour le cas o lutilisateur naurait pas activ JavaScript. Le contenu que nous manipulerons proviendra dun flux RSS rel. Les rgles CSS pour cet lment <div> sont importantes, car elles dterminent non seulement la partie de chaque article qui sera affiche en mme temps, mais galement lemplacement des lments dactualit sur la page. Voici les rgles CSS pour la zone daffichage des nouvelles et pour chaque article :
#news-feed { position: relative; height: 200px; width: 17em; overflow: hidden; }

Chapitre 9

Carrousels et prompteurs

261

.headline { position: absolute; height: 200px; top: 210px; overflow: hidden; }

Vous noterez que la hauteur de la zone contenant une nouvelle (reprsente par la classe headline) et celle du conteneur global sont toutes deux gales 200px. Par ailleurs, puisque les lments headline sont positionns de manire absolue relativement #news-feed, nous sommes en mesure daligner le bord suprieur de la nouvelle avec le bord infrieur du conteneur. Ainsi, en fixant la proprit overflow de #news-feed hidden, les nouvelles sont initialement masques. Il existe une autre raison de fixer la proprit position des nouvelles absolute : pour que lemplacement dun lment puisse tre anim sur la page, son positionnement doit tre absolute ou relative, la place du positionnement static par dfaut. Le balisage HTML et les rgles CSS tant en place, nous pouvons injecter les titres dactualit provenant dun flux RSS. Tout dabord, nous plaons le code dans une mthode .each() qui joue le rle dune sorte dinstruction if et concentre le code dans un espace de noms priv :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); }); });

Habituellement, la mthode .each() est employe pour itrer sur une collection dlments potentiellement vaste. Mais, ici, le slecteur #news-feed correspond un identifiant et seules deux actions sont possibles. La fonction $() cre un objet jQuery qui correspond un lment unique dont lidentifiant est news-feed, ou, si aucun lment de la page ne possde cet identifiant, elle produit un objet jQuery vide. Lappel .each() excute donc le code associ si, et seulement si, lobjet jQuery nest pas vide. Au dbut de la boucle .each(), le conteneur des nouvelles est vid afin quil soit prt recevoir un nouveau contenu (voir Figure 9.2). Obtenir le flux Pour lire le flux de nouvelles, nous utilisons la mthode $.get(), qui fait partie des nombreuses fonctions AJAX de jQuery permettant de communiquer avec le serveur. Nous lavons vu, cette mthode permet de manipuler du contenu fourni par une source distante en dfinissant un gestionnaire de russite. Le contenu du flux est pass ce gestionnaire sous forme dune structure XML. Nous pouvons ensuite employer le moteur de slection de jQuery pour traiter ces donnes :

262

jQuery

Figure 9.2
Initialement, le prompteur de nouvelles est vide.

$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); $.get('news/feed.xml', function(data) { $('rss item', data).each(function() { // Manipuler les nouvelles. }); }); }); });

INFO
Pour de plus amples informations concernant $.get() et les autres mthodes AJAX, consultez le Chapitre 6.

Nous devons prsent combiner les parties de chaque article en un bloc HTML utilisable. Nous pouvons nouveau parcourir les articles du flux avec .each() et construire des liens pour les titres :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); $.get('news/feed.xml', function(data) { $('rss item', data).each(function() { var $link = $('<a></a>') .attr('href', $('link', this).text()) .text($('title', this).text()); var $headline = $('<h4></h4>').append($link); $('<div></div>') .append($headline) .appendTo($container); }); }); }); });

Chapitre 9

Carrousels et prompteurs

263

Nous rcuprons le contenu des lments <title> et <link> de chaque nouvelle et lutilisons pour construire llment <a>. Ce lien est envelopp dans un lment <h4>. Nous plaons chaque nouvelle dans <div id="news-feed">, mais, pour le moment, sans appliquer la classe headline au <div> de la nouvelle afin de pouvoir suivre la progression de notre travail (voir Figure 9.3).
Figure 9.3
Ajout des titres des nouvelles.

Outre les titres dactualit, nous souhaitons afficher quelques informations concernant chaque nouvelle, notamment sa date de publication et son rsum :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); $.get('news/feed.xml', function(data) { $('rss item', data).each(function() { var $link = $('<a></a>') .attr('href', $('link', this).text()) .text($('title', this).text()); var $headline = $('<h4></h4>').append($link); var pubDate = new Date( $('pubDate', this).text()); var pubMonth = pubDate.getMonth() + 1; var pubDay = pubDate.getDate(); var pubYear = pubDate.getFullYear(); var $publication = $('<div></div>') .addClass('publication-date') .text(pubDay + '/' + pubMonth + '/' + pubYear); var $summary = $('<div></div>') .addClass('summary') .html($('description', this).text()); $('<div></div>') .append($headline, $publication, $summary) .appendTo($container); });

264

jQuery

}); }); });

Dans le flux RSS, les informations de date sont donnes au format RFC 822, avec la date, lheure et le fuseau horaire (par exemple Sun, 28 Sep 2008 18:01:55 +0000). Puisque ce format nest pas particulirement agrable lire, nous utilisons lobjet JavaScript Date pour produire une date plus concise (par exemple 28/9/2008). Le rsum de larticle est plus facile obtenir et mettre en forme. Toutefois, nous devons signaler que, dans notre flux dexemple, ce contenu peut inclure des entits HTML. Pour tre certain que jQuery ne leur applique pas automatiquement lchappement, le rsum est ajout avec la mthode .html() la place de la mthode .text(). Les nouveaux lments crs sont placs dans le document par la mthode .append(). Nous utilisons une nouvelle fonctionnalit de cette mthode : lorsque plusieurs arguments sont passs, ils sont tous ajouts la suite (voir Figure 9.4).
Figure 9.4
Ajout de la date de publication et du rsum.

Vous le constatez, le titre, la date de publication, le lien et le rsum de chaque nouvelle sont prsent en place. Avant de pouvoir les animer, il nous reste appliquer la classe headline avec .addClass('headline'), ce qui masquera les nouvelles en raison des rgles CSS dfinies prcdemment. Prparer le prompteur Puisque les articles dactualit visibles changeront au fil du temps, nous devons mettre en place un mcanisme qui permet de savoir lesquels sont visibles et ceux qui ne le sont pas. Nous commenons par dfinir deux variables, la premire pour la nouvelle visible, la seconde pour celle qui vient de disparatre. Leur valeur initiale est 0 :
var currentHeadline = 0, oldHeadline = 0;

Chapitre 9

Carrousels et prompteurs

265

Nous nous occupons galement du positionnement initial des titres. Pour rappel, la feuille de style dfinie affecte la proprit top des nouvelles une valeur de dix pixels suprieure la hauteur du conteneur. Puisque la proprit overflow du conteneur est fixe hidden, les nouvelles sont initialement masques. Si nous enregistrons cette proprit dans une variable, nous facilitons le dplacement ultrieur des articles cette position :
var hiddenPosition = $container.height() + 10;

Nous souhaitons galement que le premier article soit visible ds le chargement de la page. Pour cela, nous fixons sa proprit top 0.
$('div.headline').eq(currentHeadline).css('top', 0);

La Figure 9.5 prsente le prompteur dans son tat initial.


Figure 9.5
Le prompteur aprs le chargement de la page.

Enfin, nous enregistrons le nombre total de nouvelles, pour lutiliser ultrieurement, et dfinissons une variable de temporisation, qui servira la mise en place dune pause entre chaque rotation :
var headlineCount = $('div.headline').length; var pause;

Pour le moment, il est inutile de donner une valeur pause ; elle sera fixe au cours de la rotation. Nanmoins, nous pouvons toujours dclarer des variables locales avec var de manire viter les conflits avec des variables globales ponymes. La fonction de rotation des nouvelles Nous sommes prts faire dfiler les titres, les dates et les rsums. Nous allons pour cela dfinir une fonction qui nous permettra deffectuer facilement cette opration chaque fois que nous en aurons besoin.

266

jQuery

Tout dabord, nous mettons jour les variables qui servent au suivi de la nouvelle active. Loprateur modulo (%) nous permet de boucler sur le nombre darticles. Nous pouvons ajouter un la valeur de currentHeadline lors de chaque appel notre fonction, puis effectuer le modulo entre cette valeur et celle de headlineCount de manire conserver des numros de nouvelles valides.
INFO
Au Chapitre 7, nous avons employ la mme technique pour la couleur des bandes appliques aux tables.

Nous devons galement actualiser oldHeadline afin de pouvoir manipuler facilement larticle qui disparat :
var headlineRotate = function() { currentHeadline = (oldHeadline + 1) % headlineCount; // Animer lemplacement de la nouvelle. oldHeadline = currentHeadline; };

Passons prsent au code qui fait dfiler les nouvelles. Tout dabord, nous ajoutons une animation qui fait disparatre lancien article. Ensuite, nous ajoutons une autre animation qui fait apparatre le suivant :
var headlineRotate = function() { currentHeadline = (oldHeadline + 1) % headlineCount; $('div.headline').eq(oldHeadline).animate( {top: -hiddenPosition}, 'slow', function() { $(this).css('top', hiddenPosition); }); $('div.headline').eq(currentHeadline).animate( {top: 0}, 'slow', function() { pause = setTimeout(headlineRotate, 5000); }); oldHeadline = currentHeadline; };

Dans les deux cas, lanimation se fait sur la proprit top de la nouvelle. Les nouvelles sont masques car leur proprit top est fixe la valeur hiddenPosition, qui est un nombre suprieur la hauteur du conteneur. En animant cette proprit jusqu la valeur 0, nous affichons progressivement un article. En poursuivant lanimation jusqu la valeur -hiddenPosition, nous le faisons disparatre.
INFO
Au Chapitre 4, nous avons vu que la proprit CSS top a un effet uniquement lorsque le positionnement de llment est absolute ou relative.

Chapitre 9

Carrousels et prompteurs

267

Dans les deux cas, nous indiquons galement une fonction de rappel invoque la fin de lanimation. Lorsque lancien article a compltement disparu, sa proprit top est rinitialise la valeur hiddenPosition afin quil soit prt rapparatre ultrieurement. Lorsque lanimation du nouvel article est termine, nous prparons la transition suivante. Pour cela, nous appelons la fonction JavaScript setTimeout(), qui dfinit la fonction qui sera invoque aprs une certaine dure. Dans ce cas, nous dclenchons lappel headlineRotate() aprs une pause de cinq secondes (5 000 millisecondes). Nous avons cr un cycle dactivit. Lorsquune animation est termine, la suivante est prte dmarrer. Il nous reste appeler une premire fois la fonction. Pour cela, nous utilisons nouveau setTimeout() de manire dclencher la premire rotation cinq secondes aprs la lecture du flux RSS. Notre prompteur de nouvelles est prsent oprationnel :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); $.get('news/feed.xml', function(data) { $('rss item', data).each(function() { var $link = $('<a></a>') .attr('href', $('link', this).text()) .text($('title', this).text()); var $headline = $('<h4></h4>').append($link); var pubDate = new Date($('pubDate', this).text()); var pubMonth = pubDate.getMonth() + 1; var pubDay = pubDate.getDate(); var pubYear = pubDate.getFullYear(); var $publication = $('<div></div>') .addClass('publication-date') .text(pubDay + '/' + pubMonth + '/' + pubYear); var $summary = $('<div></div>') .addClass('summary') .html($('description', this).text()); $('<div></div>') .addClass('headline') .append($headline, $publication, $summary) .appendTo($container); }); var currentHeadline = 0, oldHeadline = 0; var hiddenPosition = $container.height() + 10; $('div.headline').eq(currentHeadline).css('top', 0); var headlineCount = $('div.headline').length; var pause; var headlineRotate = function() { currentHeadline = (oldHeadline + 1) % headlineCount; $('div.headline').eq(oldHeadline).animate( {top: -hiddenPosition}, 'slow', function() { $(this).css('top', hiddenPosition); }); $('div.headline').eq(currentHeadline).animate( {top: 0}, 'slow', function() {

268

jQuery

pause = setTimeout(headlineRotate, 5000); }); oldHeadline = currentHeadline; }; pause = setTimeout(headlineRotate, 5000); }); }); });

Durant lanimation, nous pouvons constater que la partie suprieure de lancien article est coupe, tandis que le nouveau voit sa partie infrieure tronque (voir Figure 9.6).
Figure 9.6
Transition entre laffichage de deux nouvelles.

Effectuer une pause lors du survol Bien que le prompteur soit pleinement oprationnel, il nous reste rsoudre un problme dutilisabilit : un article peut disparatre de la zone daffichage avant que lutilisateur ait pu cliquer sur lun de ses liens. Il est alors oblig dattendre le dfilement complet de toutes les nouvelles avant davoir une seconde chance. Pour viter ce genre de dsagrments, nous pouvons faire en sorte que le prompteur fasse une pause lorsque le pointeur de la souris survole une nouvelle :
$container.hover(function() { clearTimeout(pause); }, function() { pause = setTimeout(headlineRotate, 250); });

Lorsque la souris entre dans la zone daffichage de larticle, le premier gestionnaire .hover() invoque la fonction JavaScript clearTimeout(). Elle annule la temporisation en cours, empchant lappel headlineRotate(). Lorsque la souris quitte la zone daffichage, le second gestionnaire de .hover() remet en place la temporisation, dclenchant un appel headlineRotate() aprs un dlai de 250 millisecondes.

Chapitre 9

Carrousels et prompteurs

269

Le code semble fonctionner parfaitement. Toutefois, si lutilisateur dplace rapidement et de manire rpte le pointeur de la souris sur et hors du <div>, un effet dsagrable se produit. Plusieurs nouvelles sont animes en mme temps et se superposent dans la zone daffichage (voir Figure 9.7).
Figure 9.7
Un effet indsirable d aux pauses.

Pour supprimer ce comportement, nous devons intervenir au cur du code. Avant la fonction headlineRotate(), nous ajoutons une variable supplmentaire :
var rotateInProgress = false;

Ds la premire ligne de la fonction, nous vrifions si une rotation est en cours. Il faut que la valeur de rotateInProgress soit false pour que nous puissions excuter nouveau le code. Cest pourquoi nous plaons tout le contenu de la fonction dans une instruction if. Ds le dbut de ce code conditionnel, nous fixons la variable rotateInProgress true et, dans la fonction de rappel de la seconde mthode .animate(), nous la remettons false :
var headlineRotate = function() { if (!rotateInProgress) { rotateInProgress = true; currentHeadline = (oldHeadline + 1) % headlineCount; $('div.headline').eq(oldHeadline).animate( {top: -hiddenPosition}, 'slow', function() { $(this).css('top', hiddenPosition); }); $('div.headline').eq(currentHeadline).animate( {top: 0}, 'slow', function() { rotateInProgress = false; pause = setTimeout(headlineRotate, 5000); }); oldHeadline = currentHeadline; } };

270

jQuery

Ces quelques lignes supplmentaires amliorent considrablement notre prompteur. Un survol rapide et rpt de la zone ne provoque plus la superposition des nouvelles. Nanmoins, un tel comportement de lutilisateur rvle un problme tenace : le prompteur dclenche deux ou trois animations immdiatement lune aprs lautre au lieu de les sparer de cinq secondes. Ce problme vient du fait quune temporisation peut sactiver pendant que lutilisateur dplace le pointeur de la souris hors du <div> et avant que la temporisation en cours ne soit coule. Nous devons donc mettre en place une nouvelle protection, en utilisant la variable pause pour signaler limminence dune autre animation. Pour cela, nous fixons cette variable false lorsque la temporisation est annule ou lorsquelle est termine. Nous pouvons prsent tester la valeur de la variable pause pour nous assurer quaucune minuterie nest active avant den dclencher une nouvelle :
var headlineRotate = function() { if (!rotateInProgress) { rotateInProgress = true; pause = false; currentHeadline = (oldHeadline + 1) % headlineCount; $('div.headline').eq(oldHeadline).animate( {top: -hiddenPosition}, 'slow', function() { $(this).css('top', hiddenPosition); }); $('div.headline').eq(currentHeadline).animate( {top: 0}, 'slow', function() { rotateInProgress = false; if (!pause) { pause = setTimeout(headlineRotate, 5000); } }); oldHeadline = currentHeadline; } }; if (!pause) { pause = setTimeout(headlineRotate, 5000); } $container.hover(function() { clearTimeout(pause); pause = false; }, function() { if (!pause) { pause = setTimeout(headlineRotate, 250); } });

Dsormais, notre prompteur de nouvelles peut encaisser sans broncher toutes les tentatives de perturbation menes par lutilisateur.

Chapitre 9

Carrousels et prompteurs

271

Lire un flux provenant dun domaine diffrent Le flux de nouvelles utilis dans notre exemple est un fichier local, mais nous pourrions vouloir lire un flux provenant dun autre site. Nous lavons vu au Chapitre 6, les requtes AJAX ne peuvent pas, de manire gnrale, cibler un site autre que celui qui hberge la page consulte. Dans ce chapitre, nous avons prsent le format de donnes JSONP en tant que mthode permettant de contourner cette limitation. Dans cette section, nous supposons que nous ne pouvons pas agir sur la source de donnes et quune solution diffrente doit donc tre imagine. Pour accder au fichier via AJAX, nous crivons du code ct serveur qui sert de relais, ou proxy, pour la requte et fait ainsi croire au code JavaScript ct client que le fichier XML se trouve sur le serveur alors quil rside ailleurs. Nous crivons un petit script PHP pour extraire le contenu des nouvelles depuis notre serveur et retransmettre ces donnes vers le script jQuery demandeur. Ce script, nomm feed.php, peut tre invoqu comme nous lavons fait prcdemment pour feed.xml :
$.get('news/feed.php', function(data) { // ... });

Dans le fichier feed.php, nous extrayons le contenu du flux dactualit partir du site distant, puis laffichons en sortie du script :
<?php header('Content-Type: text/xml'); print file_get_contents('http://jquery.com/blog/feed'); ?>

Nous devons indiquer explicitement que le contenu de la page est de type text/xml. En effet, cela permet jQuery de le rcuprer et de lanalyser comme sil sagissait dun document XML statique normal.
INFO
Certains hbergeurs web interdisent lutilisation de la fonction PHP file_get_contents() pour accder des fichiers distants car cela pose des problmes de scurit. Dans ce cas, dautres solutions peuvent tre disponibles, comme la bibliothque cURL (http://wiki. dreamhost.com/CURL).

Ajouter un indicateur de chargement Puisque la lecture dun fichier distant peut prendre un certain temps, en fonction de diffrents facteurs, il est prfrable dinformer lutilisateur du chargement en cours. Pour cela, avant deffectuer notre requte AJAX, nous ajoutons sur la page une image qui sert dindicateur de chargement :

272

jQuery

var $loadingIndicator = $('<img/>') .attr({ 'src': 'images/loading.gif', 'alt': 'Chargement en cours. Veuillez patienter.' }) .addClass('news-wait') .appendTo($container);

Ensuite, sur la premire ligne de la fonction de rappel de $.get() invoque en cas de russite, nous retirons limage de la page par une simple instruction :
$loadingIndicator.remove();

prsent, lors du chargement de la page, si lobtention des nouvelles prend un certain temps, lutilisateur voit lindicateur de chargement la place dune zone vide (voir Figure 9.8).
Figure 9.8
Lindicateur de chargement en cours.

Cette image est un GIF anim qui, une fois affich par le navigateur web, donne limpression de tourner pour signaler une activit en cours.
INFO
Vous pouvez facilement crer des images GIF animes servant dindicateurs de chargement AJAX en utilisant le service propos par le site http://ajaxload.info/.

Effet de fondu en dgrad Avant de mettre de ct notre exemple de prompteur de nouvelles, nous allons lui ajouter une touche finale : le texte de larticle va safficher en fondu en partant du bas du conteneur. Nous voulons obtenir un effet de fondu en dgrad, dans lequel le texte est opaque en haut de la zone et transparent en bas. Toutefois, il nest pas possible dappliquer plusieurs opacits sur un mme lment de texte. Pour simuler cet effet, nous recouvrons la zone de leffet par une srie dl-

Chapitre 9

Carrousels et prompteurs

273

ments, chacun ayant sa propre opacit. Ces bandes seront des lments <div> partageant quelques proprits de style que nous dclarons dans notre fichier CSS :
.fade-slice { position: absolute; width: 20em; height: 2px; background: #efd; z-index: 3; }

Elles ont toutes la largeur et la couleur darrire-plan de leur lment conteneur, <div id="news-feed">. Cela permet de tromper lil de lutilisateur et de lui faire croire que le texte saffiche en fondu au lieu dtre recouvert par un autre lment. Nous crons prsent les lments <div class="fade-slice">. Pour que leur nombre soit correct, nous commenons par dterminer la hauteur en pixels de la zone de leffet. Dans ce cas, nous choisissons 25 % de la hauteur du <div id="news-feed">. Nous utilisons une boucle for pour itrer sur la hauteur de cette zone, en crant une nouvelle bande pour chaque segment de deux pixels du dgrad :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); var fadeHeight = $container.height() / 4; for (var yPos = 0; yPos < fadeHeight; yPos += 2) { $('<div></div>') .addClass('fade-slice') .appendTo($container); } }); });

Nous avons prsent vingt-cinq bandes, une pour chaque segment de deux pixels dans une zone de dgrad de cinquante pixels, mais elles sont toutes empiles par-dessus le conteneur. Pour que notre astuce fonctionne, chacune delles doit avoir une position et une opacit diffrentes. Nous pouvons nous servir de la variable ditration yPos pour dterminer mathmatiquement les proprits opacity et top de chaque lment :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); var fadeHeight = $container.height() / 4; for (var yPos = 0; yPos < fadeHeight; yPos += 2) { $('<div></div>').css({ opacity: yPos / fadeHeight, top: $container.height() - fadeHeight + yPos }).addClass('fade-slice').appendTo($container); } }); });

274

jQuery

Puisque ces calculs sont un peu difficiles comprendre, nous prsentons les valeurs dans un tableau. Les valeurs dopacit passent progressivement du transparent lopaque, tandis que les valeurs de top commencent en haut de la zone de fondu (150) et augmentent jusqu la hauteur du conteneur (voir Tableau 9.1).
Tableau 9.1 : Les valeurs dopacit et de positionnement calcules

yPos
0 2 4 6 8

opacity
0 0.04 0.08 0.12 0.16

top
150 152 154 156 158

...
40 42 44 46 48 0.80 0.84 0.88 0.92 0.96 190 192 194 196 198

Noubliez pas que, le positionnement haut du dernier <div class="fade-slice"> tant gal 198, sa hauteur de deux pixels recouvrira parfaitement les deux pixels infrieurs du <div> conteneur haut de deux cents pixels. Avec le code correspondant en place, le texte dans la zone de larticle prsente un joli effet de fondu en dgrad lorsquil passe du transparent lopaque dans la partie infrieure du conteneur (voir Figure 9.9). Version finale du code Notre premier prompteur est prsent termin. Les nouvelles sont prises sur un serveur distant, mises en forme, affiches progressivement une une et joliment styles :
$(document).ready(function() { $('#news-feed').each(function() { var $container = $(this); $container.empty(); var fadeHeight = $container.height() / 4; for (var yPos = 0; yPos < fadeHeight; yPos += 2) {

Chapitre 9

Carrousels et prompteurs

275

Figure 9.9
Leffet de fondu en dgrad sur le texte.

$('<div></div>').css({ opacity: yPos / fadeHeight, top: $container.height() - fadeHeight + yPos }).addClass('fade-slice').appendTo($container); } var $loadingIndicator = $('<img/>') .attr({ 'src': 'images/loading.gif', 'alt': 'Chargement en cours. Veuillez patienter.' }) .addClass('news-wait') .appendTo($container); $.get('news/feed.php', function(data) { $loadingIndicator.remove(); $('rss item', data).each(function() { var $link = $('<a></a>') .attr('href', $('link', this).text()) .text($('title', this).text()); var $headline = $('<h4></h4>').append($link); var pubDate = new Date($('pubDate', this).text()); var pubMonth = pubDate.getMonth() + 1; var pubDay = pubDate.getDate(); var pubYear = pubDate.getFullYear(); var $publication = $('<div></div>') .addClass('publication-date') .text(pubDay + '/' + pubMonth + '/' + pubYear); var $summary = $('<div></div>') .addClass('summary') .html($('description', this).text()); $('<div></div>') .addClass('headline') .append($headline, $publication, $summary) .appendTo($container); });

276

jQuery

var currentHeadline = 0, oldHeadline = 0; var hiddenPosition = $container.height() + 10; $('div.headline').eq(currentHeadline).css('top', 0); var headlineCount = $('div.headline').length; var pause; var rotateInProgress = false; var headlineRotate = function() { if (!rotateInProgress) { rotateInProgress = true; pause = false; currentHeadline = (oldHeadline + 1) % headlineCount; $('div.headline').eq(oldHeadline).animate( {top: -hiddenPosition}, 'slow', function() { $(this).css('top', hiddenPosition); }); $('div.headline').eq(currentHeadline).animate( {top: 0}, 'slow', function() { rotateInProgress = false; if (!pause) { pause = setTimeout(headlineRotate, 5000); } }); oldHeadline = currentHeadline; } }; if (!pause) { pause = setTimeout(headlineRotate, 5000); } $container.hover(function() { clearTimeout(pause); pause = false; }, function() { if (!pause) { pause = setTimeout(headlineRotate, 250); } }); }); }); });

9.2

Carrousel dimages

Comme autre exemple danimation du contenu de la page, nous allons mettre en uvre une galerie dimages pour la page daccueil de notre librairie. Cette galerie mettra en avant quelques livres, avec pour chacun un lien vers son image de couverture agrandie. Contrairement lexemple prcdent, o les nouvelles taient affiches intervalle rgulier, nous utiliserons jQuery pour faire dfiler les images sur lcran en rponse une action de lutilisateur.

Chapitre 9

Carrousels et prompteurs

277

INFO
Un autre mcanisme permettant de faire dfiler un ensemble dimages est mis en uvre par le plugin jCarousel pour jQuery. Par ailleurs, la grande flexibilit du plugin SerialScroll permet de faire dfiler tout type de contenu. Bien que le rsultat obtenu avec ces plugins ne soit pas totalement identique celui de notre galerie, ils permettent de raliser des effets de haute qualit avec trs peu de code. Pour de plus amples informations concernant lutilisation des plugins, consultez le Chapitre 10.

Prparer la page Comme toujours, nous commenons par un balisage HTML et des styles CSS pour que les utilisateurs sans JavaScript puissent obtenir une reprsentation attrayante et oprationnelle des informations :
<div id="featured-books"> <div class="covers"> <a href="images/covers/large/1847190871.jpg" title="Community Server Quickly"> <img src="images/covers/medium/1847190871.jpg" width="120" height="148" alt="Community Server Quickly" /> <span class="price">35,99 </span> </a> <a href="images/covers/large/1847190901.jpg" title="Deep Inside osCommerce: The Cookbook"> <img src="images/covers/medium/1847190901.jpg" width="120" height="148" alt="Deep Inside osCommerce: The Cookbook" /> <span class="price">44,99 </span> </a> <a href="images/covers/large/1847190979.jpg" title="Learn OpenOffice.org Spreadsheet Macro Programming: OOoBasic and Calc automation"> <img src="images/covers/medium/1847190979.jpg" width="120" height="148" alt="Learn OpenOffice.org Spreadsheet Macro Programming: OOoBasic and Calc automation" /> <span class="price">35,99 </span> </a> <a href="images/covers/large/1847190987.jpg" title="Microsoft AJAX C# Essentials: Building Responsive ASP.NET 2.0 Applications"> <img src="images/covers/medium/1847190987.jpg" width="120" height="148" alt="Microsoft AJAX C# Essentials: Building Responsive ASP.NET 2.0 Applications" /> <span class="price">31,99 </span> </a> <a href="images/covers/large/1847191002.jpg" title="Google Web Toolkit GWT Java AJAX Programming"> <img src="images/covers/medium/1847191002.jpg" width="120" height="148" alt="Google Web Toolkit GWT Java AJAX Programming" />

278

jQuery

<span class="price">40,49 </span> </a> <a href="images/covers/large/1847192386.jpg" title="Building Websites with Joomla! 1.5 Beta 1"> <img src="images/covers/medium/1847192386.jpg" width="120" height="148" alt="Building Websites with Joomla! 1.5 Beta 1" /> <span class="price">40,49 </span> </a> </div> </div>

Chaque image est place dans une balise dancre, qui pointe vers une version agrandie de la couverture. Un prix est galement indiqu pour chaque ouvrage ; ils seront initialement cachs et nous utiliserons JavaScript pour les afficher au moment opportun. De manire ne pas trop encombrer la page daccueil, nous souhaitons afficher uniquement trois couvertures la fois. Lorsque JavaScript nest pas disponible, nous pouvons obtenir ce rsultat en fixant la proprit overflow du conteneur scroll et en ajustant sa largeur en consquence :
#featured-books { position: relative; background: #ddd; width: 440px; height: 186px; overflow: scroll; margin: 1em auto; padding: 0; text-align: center; z-index: 2; } #featured-books .covers { position: relative; width: 840px; z-index: 1; } #featured-books a { float: left; margin: 10px; height: 146px; } #featured-books .price { display: none; }

Ces rgles de style mritent quelques explications. La valeur de la proprit z-index de llment le plus extrieur doit tre suprieure celle de llment quil contient ; ainsi, Internet Explorer peut masquer la partie de llment interne qui dborde de son conteneur. Nous fixons la largeur de llment externe 440px de manire accepter trois

Chapitre 9

Carrousels et prompteurs

279

images, avec une marge de 10px autour de chacune et un espace supplmentaire de 20px pour la barre de dfilement. Avec ces styles en place, les images peuvent tre parcourues laide de la barre de dfilement standard du systme (voir Figure 9.10).
Figure 9.10
Prsentation standard des livres mis en avant.

Modifier les styles avec JavaScript Puisque nous avons fait le ncessaire pour que notre galerie fonctionne sans JavaScript, nous allons prsent retirer quelques raffinements. La barre de dfilement standard sera redondante lorsque nous mettrons en place notre propre mcanisme, et la disposition automatique des couvertures avec la proprit float entrera en conflit avec le positionnement dont nous aurons besoin pour les animer. Par consquent, notre premire tche consiste redfinir certains styles :
$(document).ready(function() { var spacing = 140; $('#featured-books').css({ 'width': spacing * 3, 'height': '166px', 'overflow': 'hidden' }).find('.covers a').css({ 'float': 'none', 'position': 'absolute', 'left': 1000 }); var $covers = $('#featured-books .covers a'); $covers.eq(0).css('left', 0); $covers.eq(1).css('left', spacing); $covers.eq(2).css('left', spacing * 2); });

280

jQuery

La variable spacing sera utile dans de nombreux calculs. Elle reprsente la largeur dune image de couverture, plus lespacement ajout de chaque ct. La largeur de llment conteneur peut donc tre fixe prcisment de manire prsenter trois images, sans lespace requis par la barre de dfilement. Nous fixons videmment la proprit overflow hidden pour faire disparatre la barre de dfilement. Les images de couverture sont toutes positionnes de manire absolue, avec la coordonne left fixe 1000 pour quelles se trouvent hors de la zone visible. Ensuite, nous dplaons les trois premires couvertures dans la zone visible, une la fois. La variable $covers, qui contient tous les lments dancre, se rvlera galement pratique ultrieurement. La Figure 9.11 montre que les trois premires couvertures sont visibles, sans mcanisme de dfilement.
Figure 9.11
Positionnement initial de trois couvertures, sans mcanisme de dfilement.

Dcaler les images sur un clic prsent, nous devons ajouter le code qui rpond au clic sur lune des images dextrmit et rordonne les couvertures en consquence. Si le clic se fait sur la couverture de gauche, cela signifie que lutilisateur souhaite voir les autres images qui se trouvent sa gauche. Autrement dit, nous devons dcaler les couvertures vers la droite. linverse, si la couverture de droite est clique, nous devons dcaler les images vers la gauche. Puisque nous souhaitons un fonctionnement en carrousel, les images masques gauche sont ajoutes droite. Pour commencer, nous modifions simplement lemplacement des images, sans les animer :
$(document).ready(function() { var spacing = 140; $('#featured-books').css({ 'width': spacing * 3, 'height': '166px', 'overflow': 'hidden' }).find('.covers a').css({

Chapitre 9

Carrousels et prompteurs

281

'float': 'none', 'position': 'absolute', 'left': 1000 }); var setUpCovers = function() { var $covers = $('#featured-books .covers a'); $covers.unbind('click'); // Image de gauche : dfilement vers la droite (pour afficher les // images qui se trouvent gauche). $covers.eq(0) .css('left', 0) .click(function(event) { $covers.eq(2).css('left', 1000); $covers.eq($covers.length - 1).prependTo('#featured-books .covers'); setUpCovers(); event.preventDefault(); }); // Image de droite : dfilement vers la gauche (pour afficher les // images qui se trouvent droite). $covers.eq(2) .css('left', spacing * 2) .click(function(event) { $covers.eq(0).css('left', 1000); $covers.eq(0).appendTo('#featured-books .covers'); setUpCovers(); event.preventDefault(); }); // Image au centre. $covers.eq(1) .css('left', spacing); }; setUpCovers(); });

La nouvelle fonction setUpCovers() comprend le code de positionnement de limage crit prcdemment. En lencapsulant dans une fonction, nous pouvons renouveler le positionnement des images aprs que les lments ont t rordonns ; cette possibilit est importante, comme nous le verrons plus loin. Notre exemple se fonde sur six couvertures au total, rfrences par les nombres 0 5 en JavaScript, et les images 0, 1 et 2 sont visibles. Lors dun clic sur limage 0, nous voulons dcaler toutes les images dune position vers la droite. Nous sortons limage 2 de la zone visible (avec .css('left', 1000)), puisquelle ne doit plus tre affiche aprs le dcalage. Ensuite, nous dplaons limage 5 situe en fin de liste vers la premire place (avec .prependTo()). Cette opration rordonne toutes les images et, lors dun appel suivant setUpCovers(), lancienne image 5 est devenue limage 0, la n 0 est devenue la n 1, et la n 1 est devenue la n 2. Le code de positionnement mis en uvre par cette fonction suffit donc dplacer les couvertures leur nouvel emplacement (voir Figure 9.12).

282

jQuery

Figure 9.12
Dplacement des couvertures suite au clic sur limage lextrme droite.

En cliquant sur limage 2, la procdure est inverse. Cette fois-ci, limage 0 est masque, puis dplace la fin de la file. Cela dcale la n 1 lemplacement 0, la n 2, lemplacement 1, et la n 3, lemplacement 2. Pour viter des anomalies dans les actions de lutilisateur, nous devons faire attention deux points : 1. Nous devons appeler .preventDefault() dans notre gestionnaire de click, car les couvertures sont places dans des liens menant une version agrandie. Si nous ninvoquons pas cette fonction, le lien est suivi et leffet nest pas dclench. 2. Nous devons dlier tous les gestionnaires de click au dbut de la fonction setUpCovers() ou nous finirions par avoir plusieurs gestionnaires lis la mme image suite la rotation du carrousel. Ajouter une animation de glissement Il peut tre difficile de comprendre ce qui se passe lors dun clic sur limage. En effet, les couvertures se dcalent instantanment et lutilisateur risque de penser quelles ont simplement chang au lieu dtre dplaces. Pour rsoudre ce problme, nous ajoutons une animation qui fait glisser les couvertures au lieu quelles apparaissent simplement leur nouvel emplacement. Pour cela, nous devons revoir la fonction setUpCovers() :
var setUpCovers = function() { var $covers = $('#featured-books .covers a'); $covers.unbind('click'); // Image de gauche : dfilement vers la droite (pour afficher les // images qui se trouvent gauche). $covers.eq(0) .css('left', 0) .click(function(event) { $covers.eq(0).animate({'left': spacing}, 'fast'); $covers.eq(1).animate({'left': spacing * 2}, 'fast'); $covers.eq(2).animate({'left': spacing * 3}, 'fast');

Chapitre 9

Carrousels et prompteurs

283

$covers.eq($covers.length - 1) .css('left', -spacing) .animate({'left': 0}, 'fast', function() { $(this).prependTo('#featured-books .covers'); setUpCovers(); }); event.preventDefault(); }); // Image de droite : dfilement vers la gauche (pour afficher les // images qui se trouvent droite). $covers.eq(2) .css('left', spacing * 2) .click(function(event) { $covers.eq(0) .animate({'left': -spacing}, 'fast', function() { $(this).appendTo('#featured-books .covers'); setUpCovers(); }); $covers.eq(1).animate({'left': 0}, 'fast'); $covers.eq(2).animate({'left': spacing}, 'fast'); $covers.eq(3) .css('left', spacing * 3) .animate({'left': spacing * 2}, 'fast'); event.preventDefault(); }); // Image au centre. $covers.eq(1) .css('left', spacing); };

Suite au clic sur limage de gauche, nous dplaons les trois images visibles vers la droite en les dcalant dune largeur dimage (par rutilisation de la variable spacing dfinie prcdemment). Cette partie est simple, mais nous devons galement faire en sorte que la nouvelle image devienne visible. Pour cela, nous prenons celle qui se trouve la fin de la file et ajustons sa position lcran pour quelle se trouve hors de la zone visible sur le ct gauche (-spacing). Ensuite, nous la faisons glisser vers la droite avec les autres lments (voir Figure 9.13).
Figure 9.13
Glissement des couvertures vers la droite.

284

jQuery

Bien que lanimation tienne compte du dplacement initial, nous devons toujours ajuster lordre des couvertures en invoquant nouveau setUpCovers(). Dans le cas contraire, le clic suivant ne fonctionnerait pas correctement. Puisque setUpCovers() modifie la position des couvertures, nous devons reporter son invocation la fin de lanimation. Cest pourquoi nous la plaons dans la fonction de rappel de lanimation. Un clic sur limage lextrme droite ralise une animation semblable, mais inverse. Cette fois-ci, limage lextrme gauche quitte la zone visible et est dplace la fin de la file avant lappel setUpCovers() lorsque lanimation est termine. La nouvelle image de droite est, quant elle, mise son nouvel emplacement (spacing * 3) avant que son animation ne dbute. Afficher des icnes daction Notre carrousel dimages tourne parfaitement, mais rien nindique lutilisateur quil peut cliquer sur les couvertures pour les faire dfiler. Nous pouvons laider en affichant des icnes appropries lorsque le pointeur de la souris survole les images. Dans notre exemple, les icnes seront places par-dessus les images existantes. En donnant une valeur adquate la proprit opacity, la couverture place en dessous sera toujours visible mme lorsque licne sera affiche. Nous choisissons des icnes monochromes afin que la couverture ne soit pas trop assombrie (voir Figure 9.14).
Figure 9.14
Les icnes de reprsentation des actions.

Nous avons besoin de trois icnes, deux associes aux couvertures de gauche et de droite qui indiquent lutilisateur quil peut faire dfiler les images, et une pour la couverture centrale afin de lui signaler quil peut obtenir une version agrandie de limage. Nous crons des lments HTML qui font rfrence aux icnes et les enregistrons dans des variables en vue de leur utilisation ultrieure :
var $leftRollover = $('<img/>') .attr('src', 'images/left.gif') .addClass('control') .css('opacity', 0.6) .css('display', 'none'); var $rightRollover = $('<img/>') .attr('src', 'images/right.gif') .addClass('control') .css('opacity', 0.6) .css('display', 'none');

Chapitre 9

Carrousels et prompteurs

285

var $enlargeRollover = $('<img/>') .attr('src', 'images/enlarge.gif') .addClass('control') .css('opacity', 0.6) .css('display', 'none');

Vous aurez videmment remarqu la redondance dans ce code. Pour la rduire, nous factorisons les parties redondantes dans une fonction, que nous appellerons pour crer chaque icne requise :
function createControl(src) { return $('<img/>') .attr('src', src) .addClass('control') .css('opacity', 0.6) .css('display', 'none'); } var $leftRollover = createControl('images/left.gif'); var $rightRollover = createControl('images/right.gif'); var $enlargeRollover = createControl('images/enlarge.gif');

Dans les rgles de style, nous fixons la proprit z-index de ces contrles une valeur suprieure celle des images et nous les positionnons de manire absolue afin quelles se superposent aux couvertures :
#featured-books .control { position: absolute; z-index: 3; left: 0; top: 0; }

Puisque les trois icnes possdent la mme classe control, nous pourrions tre tents de fixer la proprit opacity dans la feuille de style CSS. Malheureusement, lopacit dun lment nest pas prise en charge de la mme manire dans tous les navigateurs. Ainsi, dans Internet Explorer, pour indiquer une opacit de 60 %, il faut crire filter: alpha(opacity=60). Au lieu de nous emptrer dans la gestion de toutes ces distinctions, nous fixons lopacit laide de la mthode .css() de jQuery, qui soccupera de tous ces dtails. prsent, dans les gestionnaires de hover, nous devons simplement placer les images au bon endroit dans le DOM et les afficher :
var setUpCovers = function() { var $covers = $('#featured-books .covers a'); $covers.unbind('click mouseenter mouseleave'); // Image de gauche : dfilement vers la droite (pour afficher les // images qui se trouvent gauche). $covers.eq(0) .css('left', 0)

286

jQuery

.click(function(event) { $covers.eq(0).animate({'left': spacing}, 'fast'); $covers.eq(1).animate({'left': spacing * 2}, 'fast'); $covers.eq(2).animate({'left': spacing * 3}, 'fast'); $covers.eq($covers.length - 1) .css('left', -spacing) .animate({'left': 0}, 'fast', function() { $(this).prependTo('#featured-books .covers'); setUpCovers(); }); event.preventDefault(); }).hover(function() { $leftRollover.appendTo(this).show(); }, function() { $leftRollover.hide(); }); // Image de droite : dfilement vers la gauche (pour afficher les // images qui se trouvent droite). $covers.eq(2) .css('left', spacing * 2) .click(function(event) { $covers.eq(0) .animate({'left': -spacing}, 'fast', function() { $(this).appendTo('#featured-books .covers'); setUpCovers(); }); $covers.eq(1).animate({'left': 0}, 'fast'); $covers.eq(2).animate({'left': spacing}, 'fast'); $covers.eq(3) .css('left', spacing * 3) .animate({'left': spacing * 2}, 'fast'); event.preventDefault(); }).hover(function() { $rightRollover.appendTo(this).show(); }, function() { $rightRollover.hide(); }); // Image au centre. $covers.eq(1) .css('left', spacing) .hover(function() { $enlargeRollover.appendTo(this).show(); }, function() { $enlargeRollover.hide(); }); };

Comme prcdemment pour click, nous commenons par dlier les gestionnaires de mouseenter et mouseleave au dbut de la fonction setUpCovers() afin que les comportements associs au survol ne se cumulent pas. Dans ce code, nous exploitons une autre possibilit de la mthode .unbind() : les gestionnaires de diffrents vnements peuvent tre dlis en mme temps en sparant les noms des vnements par des espaces.

Chapitre 9

Carrousels et prompteurs

287

Pourquoi mouseenter et mouseleave ? Lorsque nous invoquons la mthode .hover(), jQuery transforme cet appel en deux liaisons dvnements. La premire fonction fournie est associe au gestionnaire de lvnement mouseenter, la seconde, celui de lvnement mouseleave. Par consquent, pour retirer les gestionnaires indiqus avec .hover(), nous devons dlier mouseenter et mouseleave. prsent, lorsque le pointeur de la souris survole une couverture, licne approprie est affiche par-dessus limage (voir Figure 9.15).
Figure 9.15
Affichage de licne approprie lors du survol dune couverture.

Agrandir limage Notre galerie dimages est oprationnelle, avec un carrousel qui permet lutilisateur dafficher la couverture qui lintresse. Un clic sur limage centrale mne une version agrandie de la couverture en question. Toutefois, nous pouvons mieux exploiter cette possibilit dagrandissement de limage. Au lieu de conduire lutilisateur vers une URL distincte lorsquil clique sur limage centrale, nous pouvons afficher la couverture agrandie par-dessus la page elle-mme.
INFO
Plusieurs variantes de laffichage dinformations par-dessus la page sont disponibles sous la forme de plugins jQuery. FancyBox, ShadowBox, Thickbox, SimpleModal et jqModal font partie des plus populaires. Pour de plus amples informations concernant lutilisation des plugins, consultez le Chapitre 10.

Pour cette image de couverture agrandie, nous avons besoin dun nouvel lment image, que nous crons au moment de linstanciation des icnes :
var $enlargedCover = $('<img/>') .addClass('enlarged') .hide() .appendTo('body');

288

jQuery

cette nouvelle classe, nous appliquons des rgles de style semblables celles dfinies prcdemment :
img.enlarged { position: absolute; z-index: 5; cursor: pointer; }

Le positionnement absolu permet dafficher la couverture par-dessus les autres images positionnes, car la valeur de la proprit z-index est suprieure celles dj attribues. Il nous reste positionner rellement limage agrandie lorsque lutilisateur clique sur limage au centre du diaporama :
// Image au centre : agrandir la couverture. $covers.eq(1) .css('left', spacing) .click(function(event) { $enlargedCover.attr('src', $(this).attr('href')) .css({ 'left': ($('body').width() - 360) / 2, 'top' : 100, 'width': 360, 'height': 444 }).show(); event.preventDefault(); }) .hover(function() { $enlargeRollover.appendTo(this).show(); }, function() { $enlargeRollover.hide(); });

Nous exploitons les liens dj prsents dans le source HTML pour connatre lemplacement du fichier de limage de couverture agrandie sur le serveur. Nous lextrayons de lattribut href du lien et laffectons lattribut src de limage agrandie. Ensuite, nous positionnons cette image. Les proprits top, width et height sont, pour le moment, figes, mais la proprit left exige un petit calcul. Nous voulons que limage agrandie soit centre sur la page, mais nous ne pouvons connatre lavance les coordonnes permettant dobtenir ce placement. Nous dterminons laxe central de la page en mesurant la largeur de llment <body> et en la divisant par deux. Puisque notre image agrandie doit tre place cheval sur cet axe et que sa largeur est gale 360 pixels, sa proprit left est donc ($('body').width() - 360) / 2. La couverture est ainsi positionne au bon endroit, cest--dire centre horizontalement sur la page (voir Figure 9.16).

Chapitre 9

Carrousels et prompteurs

289

Figure 9.16
Affichage de la couverture agrandie au centre de la page.

Masquer la couverture agrandie Nous avons besoin dun mcanisme pour enlever la couverture agrandie. La solution la plus simple consiste la faire disparatre lors dun clic :
// Image au centre : agrandir la couverture. $covers.eq(1) .css('left', spacing) .click(function(event) { $enlargedCover.attr('src', $(this).attr('href')) .css({ 'left': ($('body').width() - 360) / 2, 'top' : 100, 'width': 360, 'height': 444 }) .show() .one('click', function() { $enlargedCover.fadeOut(); }); event.preventDefault(); }) .hover(function() { $enlargeRollover.appendTo(this).show(); }, function() { $enlargeRollover.hide(); });

290

jQuery

En invoquant la mthode .one() pour lier ce gestionnaire de click, nous cartons deux problmes potentiels. Si le gestionnaire est li laide de la mthode .bind(), lutilisateur peut cliquer sur limage pendant quelle disparat. Cela dclenche un nouvel appel au gestionnaire. Par ailleurs, puisque nous rutilisons le mme lment dimage pour chaque couverture agrandie, la liaison est effectue chaque agrandissement. Si nous ne faisons rien pour dlier le gestionnaire, ils vont se cumuler. Grce .one(), nous sommes certains que les gestionnaires sont retirs aprs usage. Afficher un bouton de fermeture Cette mise en uvre suffit retirer la couverture agrandie, mais lutilisateur ne sait pas quil peut cliquer sur limage pour la faire disparatre. Nous pouvons laider en ajoutant un bouton FERMER sous forme dun badge. La cration de ce bouton ressemble la dfinition des autres lments singletons que nous avons utiliss ceux qui apparaissent une seule fois et nous pouvons donc appeler la fonction utilitaire prcdente :
var $closeButton = createControl('images/close.gif') .addClass('enlarged-control') .appendTo('body');

Lors dun clic sur la couverture centrale, limage agrandie est affiche, puis nous devons positionner et faire apparatre le bouton :
$closeButton.css({ 'left': ($('body').width() - 360) / 2, 'top' : 100 }).show();

Puisque les coordonnes du bouton FERMER sont identiques celles de la couverture agrandie, leurs coins suprieurs gauches sont aligns (voir Figure 9.17). Un comportement est dj associ limage pour la masquer lorsque lutilisateur clique dessus. Dans ce type de situation, nous pouvons nous appuyer sur le bouillonnement dvnement pour que le clic sur le bouton FERMER provoque le mme effet. Toutefois, contrairement ce que lon pourrait croire, le bouton FERMER nest pas un lment descendant de la couverture. Nous avons positionn ce bouton de manire absolue audessus de la couverture. Autrement dit, un clic sur le bouton nest pas transmis limage agrandie. Nous devons donc grer les clics sur le bouton FERMER :
// Image au centre : agrandir la couverture. $covers.eq(1) .css('left', spacing) .click(function(event) { $enlargedCover.attr('src', $(this).attr('href')) .css({ 'left': ($('body').width() - 360) / 2, 'top' : 100, 'width': 360, 'height': 444 }) .show()

Chapitre 9

Carrousels et prompteurs

291

Figure 9.17
Ajout dun bouton de fermeture.

.one('click', function() { $closeButton.unbind('click').hide(); $enlargedCover.fadeOut(); }); $closeButton .css({ 'left': ($('body').width() - 360) / 2, 'top' : 100 }) .show() .click(function() { $enlargedCover.click(); }); event.preventDefault(); }) .hover(function() { $enlargeRollover.appendTo(this).show(); }, function() { $enlargeRollover.hide(); });

Lorsque nous affichons le bouton FERMER, nous lui associons un gestionnaire de click. Ce gestionnaire dclenche simplement le gestionnaire de click dj li limage agrandie. Nous devons nanmoins modifier ce gestionnaire pour y masquer le bouton FERMER. Nous en profitons galement pour dlier le gestionnaire de click de manire viter leur cumul.

292

jQuery

Ajouter un autre badge Le source HTML comprend les prix des livres, que nous nous proposons dafficher sur la couverture agrandie. Nous allons appliquer la technique dveloppe pour le bouton FERMER du contenu textuel la place dune image. Nous crons nouveau un lment singleton au dbut du code JavaScript :
var $priceBadge = $('<div/>') .addClass('enlarged-price') .css('opacity', 0.6) .css('display', 'none') .appendTo('body');

Puisque le prix sera partiellement transparent, un contraste lev entre la couleur du texte et larrire-plan est prfrable :
.enlarged-price { background-color: #373c40; color: #fff; width: 80px; padding: 5px; font-size: 18px; font-weight: bold; text-align: right; position: absolute; z-index: 6; }

Avant de pouvoir afficher le badge indiquant le prix, nous devons le remplir avec cette information tire du contenu HTML. Dans le gestionnaire de click de la couverture centrale, le mot cl this fait rfrence llment du lien. Puisque le prix est indiqu dans un lment <span> lintrieur du lien, il est trs facile dobtenir le texte correspondant :
var price = $(this).find('.price').text();

Nous pouvons alors afficher le badge en mme temps que la couverture agrandie :
$priceBadge.css({ 'right': ($('body').width() - 360) / 2, 'top' : 100 }).text(price).show();

Les styles dfinis placent le prix dans le coin suprieur droit de limage (voir Figure 9.18). Aprs avoir ajout $priceBadge.hide(); dans le gestionnaire de click de la couverture, la gestion de ce nouveau badge est termine.

Chapitre 9

Carrousels et prompteurs

293

Figure 9.18
Affichage du prix du livre dans un nouveau badge.

Animer lagrandissement de la couverture Lorsque lutilisateur clique sur la couverture centrale, la version agrandie apparat immdiatement au centre de la page. Pour apporter plus de classe cet affichage, nous pouvons utiliser les animations intgres jQuery. Nous allons mettre en uvre une transition harmonieuse entre la vignette de la couverture et sa version agrandie. Pour cela, nous devons connatre les coordonnes de dpart de lanimation, cest--dire lemplacement de la couverture centrale sur la page. Pour obtenir cette information, il nous faut parcourir intelligemment le DOM avec du code JavaScript, mais jQuery va nous faciliter la tche. La mthode .offset() retourne un objet qui contient les coordonnes left et top dun lment relativement la page. Nous pouvons ensuite insrer les proprits width et height de limage dans cet objet pour encapsuler les informations de positionnement :
var startPos = $(this).offset(); startPos.width = $(this).width(); startPos.height = $(this).height();

Les coordonnes de destination sont prsent relativement simples calculer. Nous les plaons dans un objet semblable :

294

jQuery

var endPos = {}; endPos.width = startPos.width * 3; endPos.height = startPos.height * 3; endPos.top = 100; endPos.left = ($('body').width() - endPos.width) / 2;

Ces deux objets sont utiliss comme des mappes dattributs CSS, que nous pouvons passer des mthodes telles que .css() et .animate() :
$enlargedCover.attr('src', $(this).attr('href')) .css(startPos) .show() .animate(endPos, 'normal', function() { $enlargedCover .one('click', function() { $closeButton.unbind('click').hide(); $priceBadge.hide(); $enlargedCover.fadeOut(); }); $closeButton .css({ 'left': endPos.left, 'top' : endPos.top }) .show() .click(function() { $enlargedCover.click(); }); $priceBadge .css({ 'right': endPos.left, 'top' : endPos.top }) .text(price) .show(); });

Vous remarquerez que le bouton FERMER et le prix ne peuvent pas tre ajouts tant que lanimation nest pas termine. Nous dplaons donc le code correspondant dans la fonction de rappel de la mthode .animate(). Par ailleurs, nous profitons de cette opportunit pour simplifier les appels .css() sur ces lments en rutilisant les informations de positionnement calcules pour la couverture agrandie. prsent, le passage de la vignette limage agrandie se fait progressivement (voir Figure 9.19). Retarder les animations jusquau chargement de limage Notre animation est harmonieuse, mais elle dpend dune connexion rapide avec le serveur. Si le tlchargement de la couverture agrandie prend du temps, le dbut de lanimation risque dafficher la croix rouge qui indique une image inexistante ou de continuer afficher limage prcdente. Nous pouvons faire en sorte que la transition

Chapitre 9

Carrousels et prompteurs

295

Figure 9.19
Passage progressif de la vignette la couverture agrandie.

reste lgante en attendant que limage soit intgralement reue pour dmarrer lanimation :
$enlargedCover.attr('src', $(this).attr('href')) .css(startPos) .show(); var performAnimation = function() { $enlargedCover.animate(endPos, 'normal', function() { $enlargedCover.one('click', function() { $closeButton.unbind('click').hide(); $priceBadge.hide(); $enlargedCover.fadeOut(); });

296

jQuery

$closeButton .css({ 'left': endPos.left, 'top' : endPos.top }) .show() .click(function() { $enlargedCover.click(); }); $priceBadge .css({ 'right': endPos.left, 'top' : endPos.top }) .text(price) .show(); }); }; if ($enlargedCover[0].complete) { performAnimation(); } else { $enlargedCover.bind('load', performAnimation); }

Nous devons prendre en compte deux cas : soit limage est disponible quasi immdiatement (en raison du cache), soit son tlchargement prend du temps. Dans le premier cas, lattribut complete de limage est gal true et nous pouvons invoquer immdiatement la nouvelle fonction performAnimation(). Dans le second cas, nous devons attendre que le tlchargement de limage soit termin avant dappeler performAnimation(). Il sagit dun des rares cas o lvnement load standard du DOM nous est plus utile que lvnement ready de jQuery. Puisque load est dclench sur une page, une image ou un cadre lorsque lintgralit du contenu correspondant a t tlcharge, nous pouvons lobserver afin dtre certains que limage puisse tre affiche correctement. ce moment-l, le gestionnaire est excut et lanimation est ralise.
INFO
Nous utilisons la syntaxe .bind('load') la place de la mthode abrge .load() pour une question de clart. En effet, .load() est galement une mthode AJAX. Les deux syntaxes sont interchangeables.

Internet Explorer et Firefox interprtent diffremment laction effectuer lorsque limage se trouve dans le cache du navigateur. Firefox dclenche immdiatement lvnement load pour JavaScript, tandis quInternet Explorer ne lenvoie jamais car aucun chargement nest effectu. Notre test de lattribut complete permet de passer outre cette diffrence dinterprtation.

Chapitre 9

Carrousels et prompteurs

297

Ajouter un indicateur de chargement Si la connexion rseau est lente, nous risquons de faire face une situation embarrassante due au temps de chargement de limage. La page semble bloque pendant ce tlchargement. Comme nous lavons fait pour le prompteur de nouvelles, nous pouvons signaler lutilisateur quune activit est en cours par laffichage dun indicateur de chargement. Cet indicateur est une autre image singleton affiche au moment opportun :
var $waitThrobber = $('<img/>') .attr('src', 'images/wait.gif') .addClass('control') .css('z-index', 4) .hide();

Pour cette image, nous choisissons un GIF anim (voir Figure 9.20), car cela permet dindiquer plus clairement lutilisateur quune activit a lieu.
Figure 9.20
Le GIF anim servant dindicateur de chargement.

Llment tant dfini, deux lignes suffisent mettre en place notre indicateur de chargement. Au tout dbut de notre gestionnaire de click associ limage centrale, avant de faire quoi que ce soit dautre, nous affichons lindicateur :
$waitThrobber.appendTo(this).show();

Et, au tout dbut de la fonction performAnimation(), lorsque nous savons que limage a t charge, nous le retirons :
$waitThrobber.hide();

Ces deux modifications permettent dajouter un indicateur de chargement la couverture agrandie. Lanimation se superpose au coin suprieur gauche de la couverture centrale (voir Figure 9.21). Version finale du code Ce chapitre na prsent quune petite partie des possibilits offertes par lanimation dimages animes et des prompteurs sur le Web. Voici la version finale du code de mise en uvre du carrousel dimages :

298

jQuery

Figure 9.21
Affichage de lindicateur de chargement sur la couverture centrale.

$(document).ready(function() { var spacing = 140; function createControl(src) { return $('<img/>') .attr('src', src) .addClass('control') .css('opacity', 0.6) .css('display', 'none'); } var $leftRollover = createControl('images/left.gif'); var $rightRollover = createControl('images/right.gif'); var $enlargeRollover = createControl('images/enlarge.gif'); var $enlargedCover = $('<img/>') .addClass('enlarged') .hide() .appendTo('body'); var $closeButton = createControl('images/close.gif') .addClass('enlarged-control') .appendTo('body'); var $priceBadge = $('<div/>') .addClass('enlarged-price') .css('opacity', 0.6) .css('display', 'none') .appendTo('body'); var $waitThrobber = $('<img/>') .attr('src', 'images/wait.gif') .addClass('control') .css('z-index', 4) .hide(); $('#featured-books').css({ 'width': spacing * 3, 'height': '166px', 'overflow': 'hidden' }).find('.covers a').css({ 'float': 'none', 'position': 'absolute', 'left': 1000 });

Chapitre 9

Carrousels et prompteurs

299

var setUpCovers = function() { var $covers = $('#featured-books .covers a'); $covers.unbind('click mouseenter mouseleave'); // Image de gauche : dfilement vers la droite (pour afficher les // images qui se trouvent gauche). $covers.eq(0) .css('left', 0) .click(function(event) { $covers.eq(0).animate({'left': spacing}, 'fast'); $covers.eq(1).animate({'left': spacing * 2}, 'fast'); $covers.eq(2).animate({'left': spacing * 3}, 'fast'); $covers.eq($covers.length - 1) .css('left', -spacing) .animate({'left': 0}, 'fast', function() { $(this).prependTo('#featured-books .covers'); setUpCovers(); }); event.preventDefault(); }).hover(function() { $leftRollover.appendTo(this).show(); }, function() { $leftRollover.hide(); }); // Image de droite : dfilement vers la gauche (pour afficher les // images qui se trouvent droite). $covers.eq(2) .css('left', spacing * 2) .click(function(event) { $covers.eq(0) .animate({'left': -spacing}, 'fast', function() { $(this).appendTo('#featured-books .covers'); setUpCovers(); }); $covers.eq(1).animate({'left': 0}, 'fast'); $covers.eq(2).animate({'left': spacing}, 'fast'); $covers.eq(3) .css('left', spacing * 3) .animate({'left': spacing * 2}, 'fast'); event.preventDefault(); }).hover(function() { $rightRollover.appendTo(this).show(); }, function() { $rightRollover.hide(); }); // Image au centre : agrandir la couverture. $covers.eq(1) .css('left', spacing) .click(function(event) { $waitThrobber.appendTo(this).show(); var price = $(this).find('.price').text(); var startPos = $(this).offset(); startPos.width = $(this).width();

300

jQuery

startPos.height = $(this).height(); var endPos = {}; endPos.width = startPos.width * 3; endPos.height = startPos.height * 3; endPos.top = 100; endPos.left = ($('body').width() - endPos.width) / 2; $enlargedCover.attr('src', $(this).attr('href')) .css(startPos) .show(); var performAnimation = function() { $waitThrobber.hide(); $enlargedCover.animate(endPos, 'normal', function() { $enlargedCover.one('click', function() { $closeButton.unbind('click').hide(); $priceBadge.hide(); $enlargedCover.fadeOut(); }); $closeButton .css({ 'left': endPos.left, 'top' : endPos.top }) .show() .click(function() { $enlargedCover.click(); }); $priceBadge .css({ 'right': endPos.left, 'top' : endPos.top }) .text(price) .show(); }); }; if ($enlargedCover[0].complete) { performAnimation(); } else { $enlargedCover.bind('load', performAnimation); } event.preventDefault(); }) .hover(function() { $enlargeRollover.appendTo(this).show(); }, function() { $enlargeRollover.hide(); }); }; setUpCovers(); });

Chapitre 9

Carrousels et prompteurs

301

9.3

En rsum

Dans ce chapitre, nous avons examin des lments de la page qui changent au fil du temps, soit de leur propre fait, soit en rponse une action de lutilisateur. Ces diaporamas et prompteurs peuvent faire toute la diffrence entre une prsence web moderne et une conception traditionnelle des sites. Nous avons tudi la prsentation dun flux dinformations XML sur la page, ainsi que le dfilement planifi dlments afin de les faire apparatre puis disparatre. Au cours de la mise en uvre dun affichage dimages dans une galerie de type carrousel, nous avons galement vu comment agrandir une image pour une vue plus dtaille, en ajoutant de manire non intrusive une animation et des contrles dinterface utilisateur. La conjugaison de ces techniques donne vie des pages qui seraient sinon fades, tout en amliorant la convivialit des applications web. Grce la puissance de jQuery, les animations et les effets peuvent tre mis en uvre sans trop defforts.

10
Utilisation des plugins
Au sommaire de ce chapitre
U U U U U U

Rechercher des plugins et de laide Utiliser un plugin Le plugin Form La bibliothque jQuery UI Autres plugins recommands En rsum

Tout au long des chapitres prcdents, nous avons examin diffrentes manires dutiliser la bibliothque jQuery, pour raliser une grande varit de tches. Nous navons pas encore vraiment explor un autre de ses aspects importants, son extensibilit. Aussi puissant que puisse tre le noyau de jQuery, son lgante architecture de plugins a permis aux dveloppeurs dtendre la bibliothque pour la rendre encore plus riche en fonctionnalits. La communaut jQuery en pleine expansion a cr des centaines de plugins, allant des petits assistants la slection jusquaux widgets complets pour linterface utilisateur. Au Chapitre 7, nous avons dj pu constater la puissance des plugins et en avons mme cr un relativement simple. Dans ce chapitre, nous expliquerons comment rechercher les plugins dvelopps par dautres programmeurs et les inclure dans nos pages web. Nous examinerons le trs populaire plugin Form, ainsi que la bibliothque officielle jQuery UI. Nous prsenterons galement plusieurs autres plugins trs utiliss, que nous ne pouvons que recommander.

10.1

Rechercher des plugins et de laide

Le site web de jQuery propose un vaste catalogue des plugins disponibles (http:// plugins.jquery.com/), avec une notation effectue par les utilisateurs, une gestion des

304

jQuery

versions et la possibilit de signaler des bogues. Ce catalogue constitue galement un bon point de dpart pour la recherche de documentation. Chaque plugin disponible peut tre tlcharg sous forme de fichier .zip et nombre dentre eux possdent des liens vers des pages de dmonstration, du code dexemple et des didacticiels. Un plus grand nombre de plugins encore sont disponibles dans des entrepts de code gnraux, comme http://github.com/, et sur les blogs des dveloppeurs de plugins. Si vous ne trouvez pas de rponse toutes vos questions partir du catalogue de plugins, du site web dun auteur et des commentaires sur les plugins, vous pouvez vous tourner vers les groupes Google ddis jQuery, notamment http://groups.google.com/ group/jquery-en/ (en anglais) et http://groups.google.com/group/jquery-fr/ (en franais). La plupart des auteurs de plugins contribuent frquemment la liste de discussion et sont toujours prts aider les nouveaux utilisateurs.

10.2

Utiliser un plugin

Lutilisation dun plugin jQuery est trs simple. La premire tape consiste linclure dans llment <head> du document, en vrifiant quil apparat aprs le fichier de jQuery :
<head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <script src="jquery.js" type="text/javascript"></script> <script src="jquery.plugin.js" type="text/javascript"></script> <script src="custom.js" type="text/javascript"></script> <title>Exemple</title> </head>

Ensuite, il suffit dinclure notre propre fichier JavaScript, dans lequel nous invoquons les mthodes cres ou tendues par le plugin. Trs souvent, la ralisation dune opration demande simplement dajouter une seule ligne dans la mthode $(document). ready() de notre fichier :
$(document).ready(function() { $('#monID').unPlugin(); });

De nombreux plugins font galement preuve de flexibilit en fournissant des paramtres facultatifs qui nous permettent de modifier leur comportement loisir. En gnral, cette configuration se fait en passant une mappe en argument de la mthode :
$(document).ready(function() { $('#monID').unPlugin ({ send: true, message: 'Ce plugin est super !' }); });

Chapitre 10

Utilisation des plugins

305

La syntaxe des plugins jQuery ressemble fortement celle des mthodes du noyau de la bibliothque. Puisque nous savons prsent comment inclure un plugin dans une page web, nous pouvons en examiner deux parmi les plus utiliss.

10.3

Le plugin Form

Le plugin Form est un parfait exemple de script qui transforme une tche difficile et complexe en quelque chose dextrmement simple. Le fichier du plugin et sa documentation dtaille sont disponibles ladresse http://www.malsup.com/jquery/form/. Au cur du plugin, nous trouvons la mthode .ajaxForm(). Pour convertir un formulaire classique en un formulaire AJAX, une seule ligne de code suffit :
$(document).ready(function() { $('#monFormulaire').ajaxForm(); });

Cet exemple prpare <form id="monFormulaire"> pour que linternaute puisse le soumettre sans que la page courante ne soit actualise. En soi, cette fonctionnalit est dj intressante, mais le vritable intrt se trouve dans la mappe doptions que nous pouvons passer la mthode. Par exemple, le code suivant invoque .ajaxForm() en prcisant les options target, beforeSubmit et success :
$(document).ready(function() { function validateForm() { // Le code de validation du formulaire est plac ici. // Nous pouvons retourner false pour annuler la soumission. }; $('#test-form').ajaxForm({ target: '#log', beforeSubmit: validateForm, success: function() { alert('Merci pour vos commentaires !'); } }); });

Loption target1 dsigne le ou les lments, dans notre cas llment dont lidentifiant est "log", qui seront mis jour partir de la rponse du serveur. Loption beforeSubmit prcise les oprations qui seront ralises avant que le formulaire ne soit soumis. Dans notre exemple, elle fait rfrence la fonction validateForm(). Si cette fonction retourne false, le formulaire nest pas soumis.

1. N.d.T. : attention, il ne faut pas confondre cette option avec lattribut HTML target de la balise <form>.

306

jQuery

Loption success indique les tches qui seront effectues aprs une soumission russie du formulaire. Dans lexemple, elle affiche simplement un message dalerte afin que lutilisateur sache que lenvoi du formulaire sest parfaitement droul. Voici les autres options disponibles avec les mthodes .ajaxForm() et .ajaxSubmit() :
n

url. LURL vers laquelle les donnes du formulaire seront envoyes, si elle diffre de lattribut action du formulaire. type. La mthode employe pour soumettre le formulaire GET ou POST. La valeur par dfaut est donne par lattribut method du formulaire. Si cet attribut est absent, la mthode GET est choisie par dfaut. dataType. Le type de donnes attendu pour la rponse du serveur. Les valeurs possibles sont null, xml, script et json. La valeur par dfaut est null et correspond une rponse HTML. resetForm. La valeur par dfaut de ce boolen est false. Lorsquil est fix true, tous les champs du formulaire sont rinitialiss leur valeur par dfaut aprs la russite de la soumission. clearForm. La valeur par dfaut de ce boolen est false. Lorsquil est fix true, tous les champs du formulaire sont effacs aprs la russite de la soumission.

Le plugin Form propose dautres mthodes de gestion des formulaires et de leurs donnes. Sur le site http://www.malsup.com/jquery/form/, vous trouverez de plus amples informations concernant ces mthodes, ainsi que des dmonstrations et des exemples. Astuces et conseils En gnral, la mthode .ajaxForm() est plus pratique que la mthode .ajaxSubmit(), mais elle offre moins de souplesse. Si nous souhaitons que le plugin soccupe de la liaison des vnements notre place et invoque pour nous la mthode .ajaxSubmit() au moment appropri, nous devons utiliser .ajaxForm(). Si nous voulons disposer dun contrle plus prcis sur la gestion de lvnement submit, il faut opter pour .ajaxSubmit(). Par dfaut, .ajaxForm() et .ajaxSubmit() utilisent les valeurs des attributs action et method indiques dans le balisage du formulaire. Tant que le balisage du formulaire est valide, le plugin fonctionne comme attendu, sans que nous ayons besoin dun quelconque rglage. Par ailleurs, nous bnficions automatiquement des avantages de lamlioration progressive ; le formulaire est totalement oprationnel mme lorsque JavaScript est dsactiv.

Chapitre 10

Utilisation des plugins

307

Normalement, lorsquun formulaire est envoy et que llment utilis pour cette soumission est nomm, ses attributs name et value sont transmis avec les autres donnes du formulaire. Sur ce point, la mthode .ajaxForm() a une approche proactive, en ajoutant des gestionnaires de click tous les lments <input type="submit"> afin quelle puisse dterminer celui qui a servi lenvoi du formulaire. En revanche, la mthode .ajaxSubmit() a une approche ractive et na aucun moyen de connatre cette information. Elle ne capture pas llment utilis pour la soumission. La mme distinction concerne les lments <input type="image"> : .ajaxForm() les prend en charge, tandis que .ajaxSubmit() les ignore. moins quun fichier ne soit joint la soumission du formulaire, les mthodes .ajaxForm() et .ajaxSubmit() passent leur argument options la mthode $.ajax(), qui fait partie du noyau de jQuery. Par consquent, les options reconnues par la mthode $.ajax() peuvent tre transmises au travers du plugin Form. Grce cette fonctionnalit, les rponses aux formulaires AJAX peuvent tre encore plus robustes :
$(document).ready(function() { $(#monFormulaire).ajaxForm({ timeout: 2000, error: function (xml, status, e) { alert(e.message); } }); });

Lorsque le niveau de personnalisation na pas besoin dtre aussi lev, nous pouvons passer aux mthodes .ajaxForm() et .ajaxSubmit() une fonction la place dune mappe doptions. Puisque cette fonction est traite comme un gestionnaire invoqu en cas de russite, nous pouvons obtenir le texte de la rponse renvoye par le serveur :
$(document).ready(function() { $(#monFormulaire).ajaxForm(function(responseText) { alert(responseText); }); });

10.4

La bibliothque jQuery UI

Si le plugin Form ne propose quune seule fonction, tout en la faisant trs bien, le plugin jQuery UI offre de nombreuses fonctionnalits, tout en les faisant galement trs bien. En ralit, jQuery UI nest pas tant un plugin quune bibliothque de plugins. Dirige par Paul Bakaus, lquipe jQuery UI a cr de nombreux composants dinteraction de base et des widgets permettant de proposer linternaute des pages web qui sapparentent des applications bureautiques. Parmi les composants dinteraction, nous trouvons des mthodes pour faire glisser, dposer, trier et redimensionner des lments.

308

jQuery

Dans sa version stable, la collection de widgets propose un menu accordon, un slecteur de date, une bote de dialogue, un curseur et des onglets. Dautres widgets font lobjet dun dveloppement actif. Par ailleurs, jQuery UI propose un jeu deffets labors qui compltent les animations de jQuery. Puisque la bibliothque est trop vaste pour la dtailler intgralement dans ce chapitre, nous nous limiterons aux effets graphiques, au composant dinteraction Sortable et au widget Dialog. Les modules jQuery, leur documentation et des dmonstrations sont disponibles sur le site http://ui.jquery.com/. Effets Le module des effets de jQuery UI est compos dun fichier central et dun ensemble de fichiers pour chaque effet. Le fichier central contient des animations orientes couleur et classe, ainsi quun easing labor. Animation des couleurs Aprs avoir rfrenc le fichier central des effets dans le document, la mthode .animate() accepte de nouvelles proprits de style, comme borderTopColor, backgroundColor et color. Par exemple, nous pouvons animer un lment en le faisant passer progressivement dun texte noir sur fond blanc un texte blanc sur fond noir :
$(document).ready(function() { $('#mydiv').animate({ color: '#fff', backgroundColor: '#000' }, 'slow'); });

La Figure 10.1 montre laspect du <div> lorsque lanimation en est un peu plus de la moiti. Le texte de llment est en train de devenir blanc, tandis que son arrire-plan est presque noir.
Figure 10.1
Animation des couleurs dun lment.

Chapitre 10

Utilisation des plugins

309

Animation des classes Les trois mthodes de manipulation des classes que nous avons utilises dans les chapitres prcdents, cest--dire .addClass(), .removeClass() et .toggleClass(), prennent prsent un second paramtre facultatif prcisant la vitesse de lanimation. Nous pouvons les invoquer sous la forme .addClass('highlight', 'fast'), .removeClass('highlight', 'slow') ou .toggleClass('highlight, 1000). Easing labor Les nouvelles fonctions deasing font varier la vitesse et la position auxquelles les transitions se produisent. Par exemple, la fonction easeInQuart termine une animation quatre fois plus rapidement quelle ne la commence. Nous pouvons indiquer une fonction deasing personnalise dans toutes les mthodes danimation standard de jQuery ou des mthodes deffet de jQuery UI. Pour cela, nous passons un argument supplmentaire ou ajoutons une option une mappe doptions, selon la syntaxe choisie. Par exemple, pour prciser la fonction easeInQuart lanimation des couleurs, nous pouvons ajouter un argument supplmentaire :
$(document).ready(function() { $('#mydiv').animate({ color: '#fff', backgroundColor: '#000' }, 'slow', 'easeInQuart'); });

Nous pouvons galement ajouter une option la deuxime mappe :


$(document).ready(function() { $('#mydiv').animate({ color: '#fff', backgroundColor: '#000' }, { duration: 'slow', easing: 'easeInQuart' }); });

Sur le site http://gsgd.co.uk/sandbox/jquery/easing/, vous trouverez des dmonstrations pour lensemble des fonctions deasing. Effets supplmentaires Les fichiers de chaque effet ajoutent diverses transitions, toutes pouvant tre mises en uvre avec la mthode .effect(), certaines tendant galement les fonctionnalits des mthodes jQuery .show(), .hide() et .toggle(). Par exemple, leffet explode, qui masque des lments en les faisant exploser en un nombre de morceaux prcis, peut tre obtenu avec la mthode .effect() :

310

jQuery

$(document).ready(function() { $('#explode').effect('explode', {pieces: 16}, 800); });

Il peut galement tre appliqu avec la mthode .hide() :


$(document).ready(function() { $('#explode').hide('explode', {pieces: 16}, 800); });

Dans les deux cas, llment, dont ltat initial est illustr la Figure 10.2 et celui mianimation, la Figure 10.3, finit masqu.
Figure 10.2
Apparence initiale de llment.

Figure 10.3
Apparence de ltat mi-animation.

Composants dinteraction Entre autres composants dinteraction, jQuery UI propose Sortable, qui peut transformer nimporte quel groupe dlments en une liste acceptant le glisser-dposer. La Figure 10.4 prsente une liste non ordonne, avec des styles CSS appliqus chaque lment. Le balisage HTML correspondant est relativement simple :
<ul id="sort-container"> <li>Jean</li> <li>Paul</li> <li>Georges</li>

Chapitre 10

Utilisation des plugins

311

Figure 10.4
Une liste non ordonne style.

<li>Rgis</li> <li>Philippe</li> <li>Stphane</li> </ul>

Pour que la liste puisse tre rorganise, nous ajoutons simplement le code suivant :
$(document).ready(function() { $('#sort-container').sortable(); });

Cette unique ligne dans le gestionnaire $(document).ready() nous donne la possibilit de faire glisser chaque lment et de le dposer un emplacement diffrent de la liste (voir Figure 10.5).
Figure 10.5
La liste peut tre rorganise par glisser-dposer.

312

jQuery

Nous pouvons amliorer cette interaction avec lutilisateur en ajoutant des options la mthode .sortable(). Elle reconnat plus de trente options, mais nous nen utiliserons que quelques-unes dans notre exemple :
$(document).ready(function() { $('#sort-container').sortable({ opacity: .5, cursor: 'move', axis: 'y' }); });

Les deux premires options, opacity et cursor, se comprennent delles-mmes. La troisime, axis, limite le mouvement de llment selon laxe indiqu, dans ce cas laxe y (voir Figure 10.6).
Figure 10.6
Le dplacement de llment styl se fait selon laxe indiqu.

Comme lillustre la couleur darrire-plan plus claire de llment en cours de dplacement, nous profitons dune classe qui lui est attribue automatiquement, ui-sortablehelper, pour appliquer un style cette classe. Pour de plus amples informations concernant les composants dinteraction standard de jQuery UI, rendez-vous sur la page http://docs.jquery.com/UI#Interaction. Widgets Outre les briques de construction, jQuery UI fournit tout un ensemble de widgets robustes pour linterface utilisateur. Il sagit de composants immdiatement oprationnels, semblables aux lments auxquels nous ont habitus les applications bureautiques. Par exemple, le widget Dialog se fonde sur les composants Draggable et Resizable pour crer une bote de dialogue, que nous navons pas construire nous-mmes.

Chapitre 10

Utilisation des plugins

313

Comme les autres widgets pour linterface utilisateur, Dialog accepte un grand nombre doptions. Sa mthode .dialog() peut galement prendre des chanes en argument pour modifier le contenu affich. Dans sa version la plus simple, la mthode .dialog() convertit un lment existant en une bote de dialogue et laffiche avec le contenu de llment. Nous pouvons, par exemple, partir dune structure <div> simple :
<div id="dlg">Ma bote de dialogue</div>

Vous ne serez pas surpris de constater que ce <div> apparat sous forme dun bloc de texte (voir Figure 10.7).
Figure 10.7
Initialement, llment est affich comme un bloc de texte.

Ds que le DOM est prt, nous pouvons invoquer la bote de dialogue de base dans notre fichier JavaScript :
$(document).ready(function() { $('#dlg').dialog(); });

Le texte est alors envelopp dans une bote de dialogue (voir Figure 10.8).
Figure 10.8
Transformation de llment en une bote de dialogue.

Cette bote de dialogue peut tre redimensionne en cliquant sur lun des bords et en le faisant glisser. Elle peut galement tre dplace en cliquant nimporte o dans la partie haute, juste en dessous de la bordure suprieure. Il est mme possible de la fermer en cliquant sur le lien plac dans le coin suprieur gauche.

314

jQuery

videmment, nous pouvons obtenir beaucoup mieux en appliquant des styles. Si les styles qui permettent aux widgets dtre oprationnels sont fournis par jQuery UI, cest nous de dfinir leur apparence. La Figure 10.9 prsente la bote de dialogue laquelle un thme par dfaut est appliqu.
Figure 10.9
Application dun thme la bote de dialogue.

Les diffrentes zones sont dsormais clairement indiques. De plus, le pointeur de la souris change lorsquil arrive sur les parties de la bote de dialogue qui peuvent servir au dplacement et au redimensionnement. linstar des autres mthodes de jQuery UI, .dialog() reconnat plusieurs options. Certaines dentre elles affectent laspect de la bote de dialogue, tandis que dautres permettent de dclencher des vnements. Voici un exemple de ces options :
$(document).ready(function() { var $dlg = $('#dlg'); var dlgText = $dlg.text(); $dlg.dialog({ autoOpen: false, title: dlgText, open: function() { $dlg.empty(); }, buttons: { 'ajouter un message': function() { $dlg.append('<p>Message ajout</p>'); }, 'effacer les messages': function() { $('p', $dlg).remove(); } } }); $('#do-dialog').click(function() { $dlg.dialog('open'); }); });

Chapitre 10

Utilisation des plugins

315

La bote de dialogue est initialement masque, pour tre ouverte lorsque lutilisateur clique sur un bouton dont lidentifiant est do-dialog. Nous dplaons galement le texte initial de la bote de dialogue vers la zone de titre et ajoutons deux boutons, lun avec AJOUTER UN MESSAGE en tant que libell et lautre avec EFFACER LES MESSAGES. Une fonction est associe chaque bouton de manire ajouter et effacer des messages. La Figure 10.10 montre laspect de la bote de dialogue aprs trois clics sur le bouton AJOUTER UN MESSAGE.
Figure 10.10
La bote de dialogue amliore.

Il existe de nombreuses autres options pour configurer laffichage et le comportement des botes de dialogue. Pour de plus amples informations, consultez la page http:// docs.jquery.com/UI/Dialog#options. ThemeRoller ThemeRoller est un ajout rcent la bibliothque jQuery UI. Il sagit dun moteur interactif accessible au travers du Web pour crer des thmes pour les widgets de linterface utilisateur (voir Figure 10.11). Grce ThemeRoller, il est possible de crer rapidement et facilement des lments hautement personnaliss laspect professionnel. Nous lavons indiqu, un thme par dfaut a t appliqu la bote de dialogue cre prcdemment. Il a t produit par ThemeRoller sans modifier aucun des paramtres. Pour gnrer un jeu de styles totalement diffrent, il suffit daller sur la page http:// jqueryui.com/themeroller/, de modifier les diffrents paramtres et dappuyer sur le bouton DOWNLOAD THEME. Le fichier .zip contenant les feuilles de style et les images peut ensuite tre plac dans le rpertoire appropri. Par exemple, en choisissant des couleurs et des textures diffrentes, nous pouvons transformer laspect de notre bote de dialogue prcdente en moins de cinq minutes (voir Figure 10.12).

316

jQuery

Figure 10.11
Le moteur de thmes ThemeRoller.

Figure 10.12
Application dun autre thme la bote de dialogue.

Chapitre 10

Utilisation des plugins

317

10.5

Autres plugins recommands

Outre les plugins prsents dans ce chapitre et ailleurs dans cet ouvrage, nous recommandons ceux mentionns dans cette section, pas uniquement en raison de leur popularit, mais galement du fait de la robustesse de leur code. Formulaires Au Chapitre 8, nous avons tudi plusieurs manires de manipuler les formulaires. Les plugins suivants permettent daccomplir les tches associes avec facilit. Autocomplete
n n

http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/ http://plugins.jquery.com/project/autocompletex

crit par Jrn Zaefferer, lun des dveloppeurs du noyau de jQuery, le plugin Autocomplete propose une liste des correspondances possibles au fur et mesure que lutilisateur saisit un texte (voir Figure 10.13).

Figure 10.13
Dmonstration du plugin Autocomplete.

Validation
n n

http://bassistance.de/jquery-plugins/jquery-plugin-validation/ http://plugins.jquery.com/project/validate

318

jQuery

Validation, un autre plugin d Jrn Zaefferer, est un outil extrmement flexible pour la validation des informations saisies dans un formulaire en fonction dun grand nombre de critres (voir Figure 10.14).

Figure 10.14
Dmonstration du plugin Validation.

Jeditable
n n

http://www.appelsiini.net/projects/jeditable http://plugins.jquery.com/project/jeditable

Le plugin Jeditable convertit les lments qui ne servent pas dans les formulaires en zones de saisie modifiables lorsque lutilisateur effectue une certaine action, comme un clic ou un double-clic (voir Figure 10.15). Le contenu modifi est automatiquement envoy au serveur pour y tre enregistr. Masked Input
n n

http://digitalbush.com/projects/masked-input-plugin/ http://plugins.jquery.com/project/maskedinput

Le plugin Masked Input propose aux utilisateurs une manire plus conviviale de saisir des donnes dans un certain format, comme des dates, des numros de tlphone ou des numros de scurit sociale. Il place automatiquement certains caractres dans le champ, comme des barres obliques pour les dates, tout en autorisant uniquement la saisie des caractres dfinis par les options (voir Figure 10.16).

Chapitre 10

Utilisation des plugins

319

Clic sur le texte modifier

Slection du texte de remplacement

Le texte est remplac dans la page

Figure 10.15
Dmonstration du plugin Jeditable.

Figure 10.16
Dmonstration du plugin Masked Input.

Tables Au Chapitre 7, nous avons prsent des techniques pour organiser, embellir et mettre en valeur des donnes tabulaires. Plusieurs dveloppeurs de plugins ont regroup des procdures permettant de faciliter ces tches.

320

jQuery

Tablesorter
n n

http://tablesorter.com/ http://plugins.jquery.com/project/tablesorter

Le plugin Tablesorter peut convertir nimporte quelle table comprenant des lments <thead> et <tbody> en une table triable qui ne requiert pas une actualisation de la page (voir Figure 10.17). Il offre une fonction de tri multicolonne, des parseurs prenant en charge diffrents formats, comme les dates, les heures, la monnaie et les URL, un tri secondaire "masqu" et des possibilits dextension au travers dun systme de widget.

Figure 10.17
Dmonstration du plugin Tablesorter.

jqGrid
n n

http://www.trirand.com/blog/ http://plugins.jquery.com/project/jqGrids

jqGrid est un contrle JavaScript compatible AJAX qui permet aux dveloppeurs de prsenter et de manipuler dynamiquement des donnes tabulaires sur le Web (voir Figure 10.18). Il propose des options pour la modification en ligne, ldition des cellules, la navigation par page, la slection dlments multiples, les grilles secondaires et les grilles arborescentes. La documentation complte est disponible ladresse http:// www.secondpersonplural.ca/jqgriddocs/. Flexigrid
n n

http://code.google.com/p/flexigrid/ http://plugins.jquery.com/project/flexigrid

Chapitre 10

Utilisation des plugins

321

Figure 10.18
Dmonstration du plugin jqGrid.

Comme jqGrid, Flexigrid est un plugin de prsentation des donnes sous forme de grille (voir Figure 10.19). Parmi ses nombreuses fonctionnalits, citons la prise en charge de JSON, la pagination, la recherche rapide, laffichage, le masquage et le redimensionnement des colonnes, ainsi que le tri des lignes.
Figure 10.19
Dmonstration du plugin Flexigrid.

322

jQuery

Images La manipulation des images requiert souvent un traitement intensif ct serveur. Toutefois, quelques crateurs de plugins ont dvelopp des solutions JavaScript de traitement simple des images en se fondant sur jQuery. Jcrop
n n

http://deepliquid.com/content/Jcrop.html http://plugins.jquery.com/project/Jcrop

Jcrop constitue une solution rapide et simple pour ajouter le rognage des images dans les applications web. Ce plugin permet notamment de verrouiller les proportions de limage, de prciser les tailles minimale et maximale, de prendre en charge le clavier, de faciliter les interactions avec lutilisateur et de personnaliser les styles (voir Figure 10.20).

Figure 10.20
Dmonstration du plugin Jcrop.

Chapitre 10

Utilisation des plugins

323

Magnify
n n

http://www.jnathanson.com/index.cfm?page=jquery/magnify/magnify http://plugins.jquery.com/project/magnify

Si on lui fournit une image rduite proportionnellement et une grande image, le plugin Magnify gnre une "loupe" comme celles couramment employes pour afficher les dtails dun produit et les zooms (voir Figure 10.21).

Figure 10.21
Dmonstration du plugin Magnify.

Lightbox et bote de dialogue modales Nos exemples du Chapitre 9 ont expliqu comment afficher des informations de dtail au-dessus dune page sans utiliser une fentre popup. Cette fonctionnalit est souvent appele lightbox. Les plugins suivants facilitent la cration de ces fentres daffichage superposes. FancyBox
n

http://fancy.klade.lv/

Ce plugin pour lightbox met laccent sur le style, avec son aspect Mac et son lgant effet dombre porte (voir Figure 10.22, page suivante). Outre laffichage automatique des images redimensionnes, FancyBox peut prendre en charge du contenu en ligne ou <iframe>.

324

jQuery

Figure 10.22
Dmonstration du plugin FancyBox.

Thickbox
n

http://jquery.com/demo/thickbox/

Thickbox est un plugin polyvalent pour lightbox qui peut afficher une seule image, plusieurs images, du contenu en ligne, du contenu <iframe> ou du contenu obtenu par une requte AJAX, dans une bote de dialogue modale personnalise (voir Figure 10.23).
Figure 10.23
Dmonstration du plugin Thickbox.

Chapitre 10

Utilisation des plugins

325

BlockUI
n n

http://malsup.com/jquery/block/ http://plugins.jquery.com/project/blockUI

Le plugin BlockUI simule un comportement synchrone, sans bloquer le navigateur. Lorsquil est activ, il empche lutilisateur dinteragir avec lintgralit ou une partie de la page, jusqu ce quil soit dsactiv (voir Figure 10.24).
Figure 10.24
Dmonstration du plugin BlockUI.

jqModal
n n

http://dev.iceburg.net/jquery/jqModal/ http://plugins.jquery.com/project/jqModal

Le plugin jqModal apporte une solution lgre pour la cration de botes de dialogue modales, tout en restant puissant et flexible (voir Figure 10.25, page suivante). En mettant laccent sur les possibilits dextension, il laisse une grande partie de la gestion des interactions et de la dfinition des styles aux dveloppeurs web qui lutilisent. Cration de graphiques linstar de la manipulation des images, la cration de graphiques est une activit traditionnellement effectue sur le serveur et ncessitant un traitement important. Des programmeurs ingnieux ont dvelopp plusieurs solutions pour crer des graphiques dans le navigateur et ont regroup ces techniques dans les plugins prsents ici.

326

jQuery

Figure 10.25
Dmonstration du plugin jqModal.

Flot
n n

http://code.google.com/p/flot/ http://plugins.jquery.com/project/flot

Le plugin Flot se sert de llment <canvas> pour produire des graphiques partir de jeux de donnes et permet lutilisateur de les modifier (voir Figure 10.26). En ajoutant le script Excanvas fourni, Flot devient compatible avec Internet Explorer, par traduction des instructions de Canvas au format VML dInternet Explorer. Sparklines
n n

http://omnipotent.net/jquery.sparkline/ http://plugins.jquery.com/project/sparklines

Nomm daprs un concept popularis par Edward Tufte, expert en visualisation des donnes, le plugin Sparklines gnre des minigraphiques simples en ligne (voir Figure 10.27). Comme Flot, Sparklines utilise llment <canvas> pour gnrer les graphiques, mais la compatibilit avec Internet Explorer est prise en charge par le plugin lui-mme, au lieu de se fonder sur Excanvas.

Chapitre 10

Utilisation des plugins

327

Figure 10.26
Dmonstration du plugin Flot.

Figure 10.27
Dmonstration du plugin Sparklines.

328

jQuery

vnements Nous lavons vu de nombreuses fois, jQuery propose tout un ensemble doutils pour intercepter et ragir aux vnements provenant de lutilisateur, comme les clics de souris et les appuis sur les touches. Cependant, mme si la bibliothque standard propose un grand nombre doptions, il existe toujours des techniques plus labores explorer. Les plugins suivants facilitent la mise en uvre dune gestion des vnements moins commune. hoverIntent
n n

http://cherne.net/brian/resources/jquery.hoverIntent.html http://plugins.jquery.com/project/hoverIntent

Le plugin hoverIntent fournit une seule mthode, qui remplace .hover() lorsquil est important dviter le dclenchement accidentel danimations au moment o le pointeur de la souris survole ou quitte un lment. Elle tente de dterminer les intentions de lutilisateur en surveillant la modification de la vitesse du pointeur de la souris. Ce plugin est particulirement efficace lorsquil est employ conjointement une navigation par menus droulants. Live Query
n n

http://github.com/brandonaaron/livequery/ http://plugins.jquery.com/project/livequery

linstar de la mthode .live() intgre jQuery, le plugin Live Query associe et maintient dynamiquement des liaisons dvnements dans le DOM, quel que soit le moment de cration des lments. Il constitue une autre mise en uvre, qui se rvle plus approprie dans certains cas.

10.6

En rsum

Dans ce chapitre, nous avons vu comment employer des plugins tiers avec nos pages web. Nous avons dtaill les plugins Form et jQuery UI. Nous avons galement mentionn quelques autres plugins intressants. Au chapitre suivant, nous allons exploiter larchitecture dextension de jQuery pour dvelopper diffrents plugins de notre cru.

11
Dveloppement de plugins
Au sommaire de ce chapitre
U U U U U U U U

Ajouter de nouvelles fonctions globales Ajouter des mthodes lobjet jQuery Mthodes de parcours du DOM Ajouter des mthodes abrges Paramtres dune mthode Ajouter une expression de slection Distribuer un plugin En rsum

Les plugins tiers disponibles apportent une multitude de solutions pour amliorer nos scripts, mais nous devons parfois aller plus loin. Lorsque nous crivons du code qui pourra tre rutilis par dautres personnes, ou mme par nous, nous devons le proposer sous forme de plugin. Heureusement, le dveloppement dun plugin ne demande pas un travail beaucoup plus important que lcriture du code qui lutilise. Dans ce chapitre, nous allons examiner la cration de diffrentes sortes de plugins, du plus simple au plus complexe. Nous commencerons par des plugins qui offrent de nouvelles fonctions globales, puis nous fournirons diffrents types de mthodes de lobjet jQuery. Nous verrons galement comment ajouter de nouvelles expressions au moteur de slection de jQuery et conclurons par quelques conseils propos de la distribution dun plugin aux autres dveloppeurs.

11.1

Ajouter de nouvelles fonctions globales

Certaines fonctionnalits intgres jQuery sont disponibles au travers de fonctions globales. Nous lavons vu, il sagit en ralit de mthodes de lobjet jQuery, mais, dun point de vue pratique, ces fonctions sont places dans lespace de noms jQuery.

330

jQuery

La fonction $.ajax() en est un premier exemple. Toutes ses possibilits peuvent tre offertes au travers dune fonction globale normale nomme ajax(), mais cette solution nous soumet aux conflits sur les noms des fonctions. En plaant la fonction dans lespace de noms jQuery, nous navons plus nous proccuper des conflits potentiels avec dautres mthodes de jQuery. Pour ajouter une fonction dans lespace de noms jQuery, il suffit daffecter la nouvelle fonction en tant que proprit de lobjet jQuery :
jQuery.fonctionGlobale = function() { alert('Un test, juste un test.'); };

Ensuite, dans le code qui utilise ce plugin, nous pouvons invoquer la fonction :
jQuery.fonctionGlobale();

Nous pouvons galement employer lalias $ pour lappel de la fonction :


$.fonctionGlobale();

Le fonctionnement est identique un appel de fonction normale, conduisant laffichage du message dalerte. Ajouter plusieurs fonctions Si notre plugin doit proposer plusieurs fonctions globales, nous pouvons les dclarer indpendamment :
jQuery.fonctionUne = function() { alert('Un test, juste un test.'); }; jQuery.fonctionDeux = function(param) { alert('Le paramtre est "' + param + '".'); };

Les deux mthodes tant dfinies, nous pouvons les invoquer de manire normale :
$.fonctionUne(); $.fonctionDeux('test');

Il existe galement une autre syntaxe pour dfinir nos fonctions, fonde sur la fonction $.extend() :
jQuery.extend({ fonctionUne: function() { alert('Un test, juste un test.'); }, fonctionDeux: function(param) { alert('Le paramtre est "' + param + '".'); } });

Le rsultat est identique.

Chapitre 11

Dveloppement de plugins

331

Toutefois, nous prenons un nouveau risque concernant la pollution de lespace de noms. Mme si lutilisation de lespace de noms jQuery nous protge des conflits de noms avec la plupart des fonctions et des variables JavaScript, il nen est rien avec les noms des fonctions dfinies par dautres plugins jQuery. Pour viter ce problme, il est prfrable dencapsuler toutes les fonctions globales dun plugin dans un objet :
jQuery.monPlugin = { fonctionUne: function() { alert(Un test, juste un test.'); }, fonctionDeux: function(param) { alert('Le paramtre est "' + param + '".'); } };

Cette approche cre pour nos fonctions globales un autre espace de noms nomm jQuery.monPlugin. Mme si nous continuons prtendre informellement que ces fonctions sont "globales", il sagit en ralit de mthodes de lobjet monPlugin, lui-mme une proprit de lobjet jQuery global. Par consquent, nous devons inclure le nom du plugin dans les appels nos fonctions :
$.monPlugin.fonctionUne(); $.monPlugin.fonctionDeux('test');

Grce cette technique, et un nom de plugin suffisamment unique, nous sommes totalement protgs contre les conflits despace de noms dans nos fonctions globales. O est lintrt ? Nous possdons prsent les bases du dveloppement de plugins. Aprs avoir enregistr nos fonctions dans un fichier nomm jquery.monplugin.js, nous pouvons inclure ce script et utiliser nos fonctions partir dautres scripts de la page. Mais en quoi est-ce diffrent dun autre fichier JavaScript que nous pouvons crer et inclure ? Nous avons dj mentionn les avantages de regrouper notre code dans lespace de noms de lobjet jQuery. Par ailleurs, le dveloppement de notre bibliothque de fonctions sous forme dextension jQuery prsente un autre avantage : puisque nous savons que jQuery est inclus, les fonctions peuvent utiliser cette bibliothque.
INFO
Mme si jQuery est inclus, vous ne pouvez pas supposer que le raccourci $ soit disponible. Noubliez pas que la mthode $.noConflict() peut librer le contrle de ce raccourci. Vos plugins doivent donc toujours invoquer les mthodes de jQuery en utilisant jQuery ou dfinir eux-mmes le raccourci $, comme nous lexpliquerons plus loin.

332

jQuery

Crer une mthode utilitaire Bon nombre des fonctions globales fournies par la bibliothque jQuery standard sont des mthodes utilitaires. Autrement dit, elles proposent des raccourcis pour effectuer facilement des tches frquentes. Les mthodes de manipulation des tableaux $.each(), $.map() et $.grep() en sont de bons exemples. Pour illustrer la cration de telles mthodes, nous allons ajouter $.sum(). Notre nouvelle mthode prend en argument un tableau, effectue la somme des valeurs quil contient et retourne le rsultat. Le code du plugin est plutt court :
jQuery.sum = function(array) { var total = 0; jQuery.each(array, function(index, value) { total += value; }); return total; };

Vous remarquerez que nous avons utilis la mthode $.each() pour parcourir le tableau. Nous pourrions certainement utiliser une simple boucle for() ici mais, puisque nous sommes certains que la bibliothque jQuery a t charge avant notre plugin, nous pouvons opter pour la syntaxe laquelle nous sommes habitus. Pour tester notre plugin, nous construisons une page simple qui affiche les entres et les sorties de la fonction :
<body> <p>Contenu du tableau :</p> <ul id="array-contents"></ul> <p>Somme des valeurs :</p> <div id="array-sum"></div> </body>

Nous crivons galement un court script qui place les valeurs du tableau et leur somme dans les emplacements que nous avons dfinis :
$(document).ready(function() { var myArray = [52, 97, 0.5, -22]; $.each(myArray, function(index, value) { $('#array-contents').append('<li>' + value + '</li>'); }); $('#array-sum').append($.sum(myArray)); });

La Figure 11.1 prsente la page HTML qui valide le fonctionnement de notre plugin. Nous savons que les espaces de noms apportent une protection et que les plugins jQuery garantissent la disponibilit de la bibliothque. Toutefois, il sagit uniquement davantages organisationnels. Pour rellement profiter de la puissance des plugins jQuery, nous devons apprendre crer de nouvelles mthodes sur des instances individuelles de lobjet jQuery.

Chapitre 11

Dveloppement de plugins

333

Figure 11.1
Notre premier plugin est oprationnel.

11.2

Ajouter des mthodes lobjet jQuery

La plupart des fonctionnalits intgres jQuery sont fournies par ses mthodes dobjet. Cest dans ce contexte que les plugins prennent tout leur sens. Ds que nous envisageons dcrire une fonction qui opre sur le DOM, nous devons songer crer la place une mthode dobjet. Nous avons vu que lajout de fonctions globales passe par lextension de lobjet jQuery avec de nouvelles mthodes. Lajout de mthodes dinstance est comparable, mais il concerne lobjet jQuery.fn :
jQuery.fn.maMethode = function() { alert('Rien ne se produit.'); }

INFO
Lobjet jQuery.fn est un alias de jQuery.prototype, propos pour plus de concision.

Nous pouvons ensuite invoquer cette nouvelle mthode depuis notre code aprs une expression de slection :
$('div').maMethode();

Cette invocation affiche le message dalerte. Mais, puisque nous ne manipulons pas les nuds du DOM obtenus, nous aurions trs bien pu crire une fonction globale. La mise en uvre dune mthode agit normalement sur son contexte. Contexte dune mthode dobjet Dans une mthode de plugin, le mot cl this dsigne lobjet jQuery qui a invoqu la mthode. Par consquent, nous pouvons invoquer nimporte quelle mthode jQuery standard sur this ou extraire ses nuds du DOM et les manipuler :
jQuery.fn.showAlert = function() { alert('Vous avez slectionn ' + this.length + ' lments.'); }

334

jQuery

Pour tudier les possibilits offertes par le contexte dobjet, nous allons crire un petit plugin qui manipule les classes des lments slectionns. Notre nouvelle mthode prend en argument deux noms de classes et les change sur chaque lment :
jQuery.fn.swapClass = function(class1, class2) { if (this.hasClass(class1)) { this.removeClass(class1).addClass(class2); } else if (this.hasClass(class2)) { this.removeClass(class2).addClass(class1); } };

Nous commenons par tester si class1 est affecte llment obtenu et, dans laffirmative, la remplaons par class2. Sinon nous testons la prsence de class2 et lui substituons class1 si ncessaire. Si aucune des classes nest prsente, nous ne faisons rien. Pour tester notre mthode, nous avons besoin dun contenu HTML :
<ul> <li>Lorem ipsum dolor sit amet</li> <li class="this">Consectetur adipisicing elit</li> <li>Sed do eiusmod tempor incididunt ut labore</li> <li class="that">Magna aliqua</li> <li class="this">Ut enim ad minim veniam</li> <li>Quis nostrud exercitation ullamco</li> <li>Laboris nisi ut aliquip ex ea commodo</li> <li class="that">Duis aute irure dolor</li> </ul> <input type="button" value="changer les classes" id="swap" />

Les lments de classe this sont affichs en gras, tandis que ceux de classe that sont affichs en italique (voir Figure 11.2).
Figure 11.2
Aspect initial de la page.

Un clic sur le bouton invoque notre mthode :


$(document).ready(function() { $('#swap').click(function() { $('li').swapClass('this', 'that');

Chapitre 11

Dveloppement de plugins

335

return false; }); });

Mais cela ne fonctionne pas correctement. En cliquant sur le bouton, la classe that est applique toutes les lignes (voir Figure 11.3).
Figure 11.3
Le premier essai nest pas concluant.

Nous avons oubli quune expression de slection jQuery peut correspondre zro, un ou plusieurs lments. Nous devons tenir compte de ces diffrents cas lors de la conception dune mthode de plugin. Dans notre exemple, nous invoquons .hasClass(), qui examine uniquement le premier lment slectionn. la place, nous devons vrifier et oprer sur chaque lment de manire indpendante. Pour garantir un comportement correct quel que soit le nombre dlments slectionns, la solution la plus simple consiste toujours appeler .each() sur le contexte de mthode. Nous imposons ainsi une itration implicite, qui est importante pour la coopration entre les mthodes de plugin et les mthodes intgres. Dans lappel .each(), this fait rfrence chaque lment du DOM tour tour. Nous pouvons ainsi corriger notre code pour tester sparment chaque lment slectionn et appliquer les classes :
jQuery.fn.swapClass = function(class1, class2) { this.each(function() { var $element = jQuery(this); if ($element.hasClass(class1)) { $element.removeClass(class1).addClass(class2); } else if ($element.hasClass(class2)) { $element.removeClass(class2).addClass(class1); } }); };

ATTENTION
Puisque le mot cl this reprsente lobjet qui a appel la mthode, il fait rfrence un objet jQuery dans le corps de la mthode dobjet, mais fait rfrence un lment du DOM dans linvocation de .each().

336

jQuery

prsent, lorsque nous cliquons sur le bouton, les classes sont changes sans affecter les lments auxquels aucune des deux classes nest attribue (voir Figure 11.4).
Figure 11.4
Lchange des classes fonctionne.

Chaner des mthodes Outre litration implicite, les utilisateurs de jQuery doivent pouvoir sappuyer sur le chanage. Autrement dit, toutes les mthodes de plugin doivent retourner un objet jQuery, moins quune mthode ne soit clairement conue pour retrouver un autre lment dinformation. Lobjet jQuery retourn est habituellement celui dsign par this. Si nous utilisons .each() pour itrer sur this, nous pouvons simplement le retourner en rsultat :
jQuery.fn.swapClass = function(class1, class2) { return this.each(function() { var $element = jQuery(this); if ($element.hasClass(class1)) { $element.removeClass(class1).addClass(class2); } else if ($element.hasClass(class2)) { $element.removeClass(class2).addClass(class1); } }); };

Prcdemment, lorsque nous avons invoqu .swapClass(), nous avons commenc une nouvelle instruction pour manipuler les lments. Grce linstruction de retour, nous pouvons prsent chaner des mthodes intgres notre mthode de plugin :
$(document).ready(function() { $('#swap').click(function() { $('li') .swapClass('this', 'that') .css('text-decoration', 'underline'); return false; }); });

Chapitre 11

Dveloppement de plugins

337

La Figure 11.5 montre que le contenu de la liste est bien soulign aprs change des classes this et that.
Figure 11.5
La mthode de plugin est compatible avec le chanage.

11.3

Mthodes de parcours du DOM

Dans certains cas, nous voulons que la mthode de plugin modifie les lments du DOM rfrencs par lobjet jQuery. Par exemple, supposons que nous ajoutions une mthode de parcours du DOM qui recherche les grands-parents des lments obtenus :
jQuery.fn.grandparent = function() { var grandparents = []; this.each(function() { grandparents.push(this.parentNode.parentNode); }); grandparents = jQuery.unique(grandparents); return this.setArray(grandparents); };

Cette mthode cre un nouveau tableau grandparents et le remplit en itrant sur tous les lments actuellement rfrencs par lobjet jQuery. Nous utilisons la proprit DOM standard .parentNode pour rechercher les lments grands-parents, qui sont alors ajouts au tableau grandparents. Les doublons ventuels en sont retirs par un appel $.unique(), puis la mthode jQuery .setArray() affecte le nouveau tableau en tant que collection des lments slectionns. Ainsi, par un seul appel de mthode, nous pouvons rechercher et manipuler les grands-parents dun lment. Pour tester notre mthode, nous construisons une structure <div> arborescente :
<div>Deserunt mollit anim id est laborum</div> <div>Ut enim ad minim veniam <div>Quis nostrud exercitation <div>Ullamco laboris nisi <div>Ut aliquip ex ea</div> <div class="target">Commodo consequat <div>Lorem ipsum dolor sit amet</div> </div> </div>

338

jQuery

<div>Duis aute irure dolor</div> <div>In reprehenderit <div>In voluptate</div> <div>Velit esse <div>Cillum dolore</div> <div class="target">Fugiat nulla pariatur</div> </div> <div>Excepteur sint occaecat cupidatat</div> </div> </div> <div>Non proident</div> </div> <div>Sunt in culpa qui officia</div>

Les lments cibles (<div class="target">) sont identifiables leur texte en gras (voir Figure 11.6).
Figure 11.6
Larborescence pour les tests.

Nous pouvons retrouver les grands-parents des lments en utilisant notre nouvelle mthode :

Chapitre 11

Dveloppement de plugins

339

$(document).ready(function() { $('.target').grandparent().addClass('highlight'); });

La classe highlight met en italique les deux grands-parents des lments cibles (voir Figure 11.7).
Figure 11.7
Les lments grandsparents sont correctement trouvs.

Toutefois, cette mthode est destructive : lobjet jQuery est modifi. Leffet devient vident si nous enregistrons lobjet jQuery dans une variable :
$(document).ready(function() { var $target = $('.target'); $target.grandparent().addClass('highlight'); $target.hide(); });

Ce code est cens mettre en exergue les lments grands-parents, puis masquer les lments cibles. Cependant, il masque les grands-parents (voir Figure 11.8).

340

jQuery

Figure 11.8
Illustration de la modification de lobjet jQuery.

Lobjet jQuery enregistr dans la variable $target a t modifi par la recherche des grands-parents. Pour viter cela, nous devons faire en sorte que la mthode ne soit pas destructive. Nous utilisons donc la pile interne de jQuery associe chaque objet :
jQuery.fn.grandparent = function() { var grandparents = []; this.each(function() { grandparents.push(this.parentNode.parentNode); }); grandparents = jQuery.unique(grandparents); return this.pushStack(grandparents); };

En appelant .pushStack() la place de .setArray(), nous crons un nouvel objet jQuery au lieu de modifier lancien. prsent, lobjet $target nest pas modifi et les objets cibles dorigine sont masqus par notre code (voir Figure 11.9). Par ailleurs, grce .pushStack(), notre plugin devient compatible avec les mthodes .end() et .andSelf(). Nous pouvons donc les chaner correctement. Le code suivant ajoute la classe highlight aux lments cibles (voir Figure 11.10) :
$(document).ready(function() { $('.target').grandparent().andSelf().addClass('highlight'); });

INFO
Les mthodes de parcours du DOM, comme .children(), taient des oprations destructives dans jQuery 1.0. Elles ne le sont plus depuis la version 1.1.

11.4

Ajouter des mthodes abrges

De nombreuses mthodes fournies par jQuery sont en ralit des raccourcis vers dautres mthodes sous-jacentes. Par exemple, la plupart des mthodes dvnement sont des raccourcis pour .bind() ou .trigger(), et plusieurs mthodes AJAX invoquent en interne $.ajax(). Grce ces mthodes abrges, il est plus pratique dutiliser des fonctionnalits qui passeraient sinon par de nombreuses options complexes.

Chapitre 11

Dveloppement de plugins

341

Figure 11.9
Lobjet jQuery nest plus modifi et les objets cibles sont masqus.

Figure 11.10
Le plugin est compatible avec la mthode .andSelf().

342

jQuery

La bibliothque jQuery doit conserver un quilibre prcaire entre commodit et complexit. Chaque mthode ajoute la bibliothque peut aider les dveloppeurs crire plus rapidement certaines parties du code, mais la taille de la base de code augmente et les performances risquent de baisser. Cest pourquoi de nombreuses mthodes abrges pour une fonctionnalit interne sont places dans des plugins. Nous pouvons ainsi choisir celles qui sont utiles chaque projet et omettre celles qui ne prsentent pas dintrt. Lorsque lon constate quun idiome se rpte dans le code, la cration dune mthode abrge simpose. Par exemple, supposons que nous animions frquemment des lments en employant une combinaison des techniques intgres de "glissement" et de "fondu". La combinaison de ces effets signifie que la hauteur et lopacit dun lment doivent tre animes simultanment. Grce la mthode .animate(), cette opration est simple :
.animate({height: 'hide', opacity: 'hide'});

Nous pouvons alors crer trois mthodes abrges pour raliser cette animation lors de laffichage et du masquage des lments :
jQuery.fn.slideFadeOut = function() { return this.animate({ height: 'hide', opacity: 'hide' }); }; jQuery.fn.slideFadeIn = function() { return this.animate({ height: 'show', opacity: 'show' }); }; jQuery.fn.slideFadeToggle = function() { return this.animate({ height: 'toggle', opacity: 'toggle' }); };

Nous pouvons prsent appeler .slideFadeOut() ds que cest ncessaire pour dclencher lanimation. Puisque, dans la dfinition dune mthode de plugin, this fait rfrence lobjet jQuery courant, lanimation concernera tous les lments slectionns la fois. Pour un plugin complet, les nouvelles mthodes doivent prendre en charge les mmes paramtres que les mthodes abrges standard. En particulier, des mthodes comme .fadeIn() peuvent prendre en arguments une vitesse et une fonction de rappel. Puisque .animate() reconnat galement ces paramtres, leur prise en charge dans nos mthodes est simple. Nous les acceptons simplement en argument et les transmettons .animate() :

Chapitre 11

Dveloppement de plugins

343

jQuery.fn.slideFadeOut = function(speed, callback) { return this.animate({ height: 'hide', opacity: 'hide' }, speed, callback); }; jQuery.fn.slideFadeIn = function(speed, callback) { return this.animate({ height: 'show', opacity: 'show' }, speed, callback); }; jQuery.fn.slideFadeToggle = function(speed, callback) { return this.animate({ height: 'toggle', opacity: 'toggle' }, speed, callback); };

Nous disposons dsormais de mthodes abrges personnalises qui se comportent comme leurs quivalents intgrs. Pour illustrer leur fonctionnement, nous avons besoin dune simple page HTML :
<body> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> <div class="controls"> <input type="button" value="Glissement et fondu ferm" id="out" /> <input type="button" value="Glissement et fondu ouvert" id="in" /> <input type="button" value="Inversion" id="toggle" /> </div> </body>

Notre script invoque simplement les nouvelles mthodes lors des clics sur les boutons :
$(document).ready(function() { $('#out').click(function() { $('p').slideFadeOut('slow'); return false; }); $('#in').click(function() { $('p').slideFadeIn('slow'); return false; }); $('#toggle').click(function() { $('p').slideFadeToggle('slow'); return false; }); });

344

jQuery

La Figure 11.11 montre en trois tapes que lanimation se passe comme attendu.

Figure 11.11
Animation de la hauteur et de lopacit du bloc de texte par une mthode abrge.

11.5

Paramtres dune mthode

Nous venons dtudier plusieurs exemples de mthodes de plugin, certaines prenant des paramtres explicites. Nous lavons vu, le contexte de la mthode est toujours disponible au travers du mot cl this, mais nous pouvons galement fournir des informations supplmentaires pour influencer le fonctionnement de la mthode. Jusqu prsent, les paramtres taient peu nombreux, mais la liste peut sallonger normment. Pour grer les paramtres dune mthode et pour faciliter le travail des utilisateurs de nos plugins, il existe plusieurs astuces. Notre premier exemple sera celui dune mthode de plugin qui ajoute une ombre un bloc de texte. Nous utilisons une technique semblable celle employe au Chapitre 9 pour leffet de fondu sur le prompteur des nouvelles : des lments partiellement transparents sont affichs par superposition en diffrents endroits de la page.

Chapitre 11

Dveloppement de plugins

345

jQuery.fn.shadow = function() { return this.each(function() { var $originalElement = jQuery(this); for (var i = 0; i < 5; i++) { $originalElement .clone() .css({ position: 'absolute', left: $originalElement.offset().left + i, top: $originalElement.offset().top + i, margin: 0, zIndex: -1, opacity: 0.1 }) .appendTo('body'); } }); };

Pour chaque lment sur lequel cette mthode est invoque, nous crons des copies de llment, en modifiant leur opacit. Ces clones sont positionns de manire absolue, avec un dcalage variable partir de llment dorigine. Nous allons tester notre plugin sur un contenu HTML simple (voir Figure 11.12) :
<body> <h1>Zoe ma grande fille veut que je boive ce whisky dont je ne veux pas.</h1> </body>

Figure 11.12
La phrase de texte pour nos tests.

Pour le moment, notre mthode de plugin ne prend aucun paramtre et son invocation est donc simple, avec le rsultat illustr la Figure 11.13 :
$(document).ready(function() { $('h1').shadow(); });

346

jQuery

Figure 11.13
Invocation de la mthode du plugin sans paramtres.

Paramtres simples Nous allons prsent ajouter une certaine flexibilit la mthode du plugin. Le fonctionnement de cette mthode se fonde sur plusieurs valeurs numriques que lutilisateur pourrait souhaiter modifier. Pour cela, nous les transformons en paramtres :
jQuery.fn.shadow = function(slices, opacity, zIndex) { return this.each(function() { var $originalElement = jQuery(this); for (var i = 0; i < slices; i++) { $originalElement .clone() .css({ position: 'absolute', left: $originalElement.offset().left + i, top: $originalElement.offset().top + i, margin: 0, zIndex: zIndex, opacity: opacity }) .appendTo('body'); } }); };

Pour invoquer notre mthode, nous devons prsent fournir ces trois valeurs (voir Figure 11.14) :
$(document).ready(function() { $('h1').shadow(10, 0.1, -1); });

Nos paramtres ont leffet souhait lombre est plus longue et utilise deux fois plus de copies que prcdemment , mais linterface de la mthode nest pas vraiment idale. Il est trs facile de confondre les trois nombres et il est impossible de dduire logiquement leur ordre. Il est prfrable de libeller les paramtres, tant pour la personne qui appelle la mthode que pour celle qui devra lire et interprter le code ultrieurement.

Chapitre 11

Dveloppement de plugins

347

Figure 11.14
La mthode avec trois paramtres permet de varier leffet.

Mappes de paramtres Dans lAPI de jQuery, nous avons rencontr plusieurs exemples de mappes servant passer des paramtres une mthode. Cette solution permet de prsenter les diffrentes options lutilisateur du plugin, mieux que la simple liste de paramtres prcdente. Une mappe donne un libell chaque paramtre et retire toute exigence sur leur ordre. Par ailleurs, chaque fois que cest possible, il est prfrable dimiter lAPI de jQuery dans les plugins car cela amliore la cohrence du code et, par consquent, la facilit dutilisation.
jQuery.fn.shadow = function(opts) { return this.each(function() { var $originalElement = jQuery(this); for (var i = 0; i < opts.slices; i++) { $originalElement .clone() .css({ position: 'absolute', left: $originalElement.offset().left + i, top: $originalElement.offset().top + i, margin: 0, zIndex: opts.zIndex, opacity: opts.opacity }) .appendTo('body'); } }); };

La seule modification de notre interface rside dans la manire de rfrencer chaque paramtre. Au lieu dutiliser un nom de variable spar, nous accdons chaque valeur en tant que proprit de largument opts de la fonction. Pour invoquer la mthode, nous devons passer une mappe de valeurs la place de trois nombres individuels (voir Figure 11.15) :

348

jQuery

$(document).ready(function() { $('h1').shadow({ slices: 5, opacity: 0.25, zIndex: -1 }); });

Figure 11.15
Paramtrage de leffet au travers dune mappe.

Par simple lecture de lappel de la mthode, il est facile de comprendre le rle de chaque paramtre. Valeurs par dfaut des paramtres Plus le nombre de paramtres dune mthode augmente, moins il est probable que nous voulions toujours prciser chacun deux. La dfinition de valeurs par dfaut permet de faciliter lutilisation de linterface dun plugin. Heureusement, grce la mappe utilise pour indiquer les paramtres, cette technique est simple mettre en uvre. Toute proprit absente de la mappe est remplace par sa valeur par dfaut :
jQuery.fn.shadow = function(options) { var defaults = { slices: 5, opacity: 0.1, zIndex: -1 }; var opts = jQuery.extend(defaults, options); return this.each(function() { var $originalElement = jQuery(this); for (var i = 0; i < opts.slices; i++) { $originalElement .clone() .css({ position: 'absolute', left: $originalElement.offset().left + i, top: $originalElement.offset().top + i, margin: 0,

Chapitre 11

Dveloppement de plugins

349

zIndex: opts.zIndex, opacity: opts.opacity }) .appendTo('body'); } }); };

Dans la mthode, nous dfinissons une nouvelle mappe nomme defaults. La fonction utilitaire $.extend() prend la mappe options passe en argument et utilise ses valeurs pour remplacer celles par dfaut, sans toucher aux lments omis. Nous invoquons toujours la mthode avec une mappe, mais nous pouvons prsent prciser uniquement les paramtres qui doivent avoir une valeur diffrente de celle par dfaut (voir Figure 11.16) :
$(document).ready(function() { $('h1').shadow({ opacity: 0.05 }); });

Figure 11.16
La mappe prcise uniquement les paramtres dont la valeur change.

Les paramtres non prciss gardent leur valeur par dfaut. La mthode $.extend() accepte mme les valeurs null. Ainsi, si les paramtres par dfaut conviennent parfaitement, nous pouvons invoquer notre mthode trs simplement sans gnrer derreur :
$(document).ready(function() { $('h1').shadow(); });

Fonctions de rappel Bien entendu, certains paramtres de mthodes peuvent tre plus complexes quune simple valeur numrique. Dans lAPI de jQuery, nous avons trs souvent rencontr des paramtres de type fonction de rappel. Ces fonctions apportent une trs grande flexibilit au plugin sans imposer un travail important au moment de sa cration.

350

jQuery

Pour utiliser une fonction de rappel dans notre mthode, nous devons simplement accepter un objet fonction en paramtre et invoquer cette fonction lendroit adquat dans la mthode. titre dexemple, nous tendons notre mthode dombrage du texte pour que lutilisateur puisse dfinir lemplacement de lombre relativement au texte :
jQuery.fn.shadow = function(options) { var defaults = { slices: 5, opacity: 0.1, zIndex: -1, sliceOffset: function(i) { return {x: i, y: i}; } }; var opts = jQuery.extend(defaults, options); return this.each(function() { var $originalElement = jQuery(this); for (var i = 0; i < opts.slices; i++) { var offset = opts.sliceOffset(i); $originalElement .clone() .css({ position: 'absolute', left: $originalElement.offset().left + offset.x, top: $originalElement.offset().top + offset.y, margin: 0, zIndex: opts.zIndex, opacity: opts.opacity }) .appendTo('body'); } }); };

Chaque composante de lombre a un dcalage diffrent par rapport au texte dorigine. Prcdemment, ce dcalage tait simplement gal lindice de la composante. prsent, nous le calculons laide de la fonction sliceOffset(), qui est un paramtre que lutilisateur peut redfinir. Par exemple, nous pouvons fournir des valeurs ngatives pour le dcalage dans les deux dimensions :
$(document).ready(function() { $('h1').shadow({ sliceOffset: function(i) { return {x: -i, y: -2*i}; } }); });

Avec cette fonction de rappel, lombre se dcale prsent vers le haut et la gauche, non plus vers le bas et la droite (voir Figure 11.17).

Chapitre 11

Dveloppement de plugins

351

Figure 11.17
Une fonction de rappel permet de modifier la cration de lombre.

La fonction de rappel autorise des modifications simples de la direction de lombre, ou un positionnement beaucoup plus sophistiqu si lutilisateur du plugin crit la fonction adquate. Lorsque la fonction de rappel nest pas prcise, le comportement par dfaut est utilis. Valeurs par dfaut personnalisables Nous lavons vu, nous pouvons amliorer lutilisation de nos plugins en proposant des valeurs par dfaut raisonnables pour les paramtres de notre mthode. Cependant, il arrive que la valeur par dfaut raisonnable soit difficile trouver. Lorsquun script invoque plusieurs fois notre plugin avec des valeurs de paramtres diffrentes de celles dfinies par dfaut, la possibilit de changer ces valeurs par dfaut permet de rduire significativement la quantit de code crire. Pour que les valeurs par dfaut puissent tre personnalises, nous devons les sortir de la dfinition de la mthode et les placer dans un endroit accessible au code externe :
jQuery.fn.shadow = function(options) { var opts = jQuery.extend({}, jQuery.fn.shadow.defaults, options); return this.each(function() { var $originalElement = jQuery(this); for (var i = 0; i < opts.slices; i++) { var offset = opts.sliceOffset(i); $originalElement .clone() .css({ position: 'absolute', left: $originalElement.offset().left + offset.x, top: $originalElement.offset().top + offset.y, margin: 0, zIndex: opts.zIndex, opacity: opts.opacity }) .appendTo('body'); }

352

jQuery

}); }; jQuery.fn.shadow.defaults = { slices: 5, opacity: 0.1, zIndex: -1, sliceOffset: function(i) { return {x: i, y: i}; } };

Les valeurs par dfaut se trouvent prsent dans lespace de noms du plugin et il est possible de les rfrencer directement avec $.fn.shadow.defaults. Nous avons galement modifi notre appel $.extend() pour en tenir compte. Puisque nous rutilisons la mme mappe de valeurs par dfaut pour chaque appel .shadow(), nous ne pouvons plus autoriser $.extend() la modifier. la place, nous passons une mappe vide ({}) comme premier argument de $.extend() et cest ce nouvel objet qui est modifi. Le code qui utilise notre plugin peut prsent modifier les valeurs par dfaut, qui seront employes pour tous les appels ultrieurs .shadow(). Des options peuvent toujours tre fournies au moment de linvocation de la mthode :
$(document).ready(function() { $.fn.shadow.defaults.slices = 10; $('h1').shadow({ sliceOffset: function(i) { return {x: -i, y: i}; } }); });

Ce script cre une ombre dix composantes, puisquil sagit de la nouvelle valeur par dfaut, mais dirige galement lombre vers la gauche et le bas du fait de la fonction de rappel sliceOffset passe en argument de la mthode (voir Figure 11.18).

Figure 11.18
Changement dune valeur par dfaut et passage dune fonction de rappel en argument.

Chapitre 11

Dveloppement de plugins

353

11.6

Ajouter une expression de slection

Les parties internes de jQuery peuvent galement tre tendues. Au lieu dajouter de nouvelles mthodes, nous pouvons personnaliser celles qui existent dj. Par exemple, il est frquent dtendre les expressions de slection de jQuery pour proposer des options sotriques. La pseudo-classe est le type dexpression de slection le plus facile ajouter. Les pseudo-classes sont les expressions qui commencent par des deux-points, comme :checked ou :nth-child(). Pour illustrer la procdure de cration dune expression de slection, nous allons construire une pseudo-classe nomme :css(). Ce nouveau slecteur nous permettra de localiser des lments en fonction des valeurs numriques de leurs attributs CSS. Lorsquune expression de slection est utilise pour rechercher des lments, jQuery prend ses instructions dans une mappe interne appele expr. Elle contient du code JavaScript qui est excut sur un lment et, sil retourne la valeur true, llment est ajout au jeu de rsultats. Nous pouvons ajouter de nouvelles expressions cette mappe en utilisant la fonction $.extend() :
jQuery.extend(jQuery.expr[':'], { 'css': function(element, index, matches, set) { var parts = /([\w-]+)\s*([<>=]+)\s*(\d+)/.exec(matches[3]); var value = parseFloat(jQuery(element).css(parts[1])); switch (parts[2]) { case '<': return value < parseInt(parts[3]); case '<=': return value <= parseInt(parts[3]); case '=': case '==': return value == parseInt(parts[3]); case '>=': return value >= parseInt(parts[3]); case '>': return value > parseInt(parts[3]); } } });

Ce code indique jQuery que css est une chane valide qui peut venir aprs des deuxpoints dans une expression de slection et que, si cest le cas, la fonction indique doit tre invoque pour dterminer si llment fait partie de la collection rsultante. Dans notre exemple, la fonction dvaluation reoit quatre paramtres :
n n

element. Llment du DOM considr. Il est requis pour la plupart des slecteurs. index. Lindice de llment du DOM dans le jeu de rsultats. Il est utile pour les slecteurs du type :eq() et :lt().

354

jQuery

matches. Un tableau contenant le rsultat de lexpression rgulire qui a servi analyser le slecteur. En gnral, matches[3] est le seul lment pertinent du tableau ; dans un slecteur de la forme :a(b), llment matches[3] contient b, cest--dire le texte entre les parenthses. set. Lintgralit des lments du DOM qui correspondent jusqu prsent. Ce paramtre est rarement ncessaire.

Les slecteurs de pseudo-classes doivent utiliser les informations contenues dans ces quatre arguments pour dterminer si llment appartient ou non la collection rsultante. Dans notre cas, nous avons uniquement besoin de element et de matches. Dans la fonction de slection, nous utilisons une expression rgulire pour obtenir les parties intressantes du slecteur. Nous souhaitons quun slecteur de la forme :css(width < 200) retourne tous les lments dont la largeur est infrieure 200. Nous devons donc examiner le texte plac entre les parenthses afin dextraire le nom de la proprit (width), loprateur de comparaison (<) et la valeur comparer (200). Lexpression rgulire /([\w-]+)\s*([<>=]+)\ s*(\d+)/ effectue cette recherche et place les trois parties de la chane dans le tableau parts. Ensuite, nous devons dterminer la valeur courante de la proprit. Nous nous servons de la mthode jQuery .css() pour obtenir la valeur de la proprit dont le nom est prcis dans le slecteur. Puisque cette valeur est retourne comme une chane de caractres, nous appelons parseFloat() pour la convertir en une valeur numrique. Enfin, nous procdons la comparaison. Une instruction switch dtermine le type de la comparaison en fonction du contenu du slecteur, puis le rsultat de la comparaison (true ou false) est retourn. Nous disposons prsent dune nouvelle expression de slection que nous pouvons employer nimporte o dans notre code jQuery. Un document HTML simple nous permettra deffectuer nos tests (voir Figure 11.19) :
<body> <div>Deserunt mollit anim id est laborum</div> <div>Ullamco</div> <div>Ut enim ad minim veniam laboris</div> <div>Quis nostrud exercitation consequat nisi</div> <div>Ut aliquip</div> <div>Commodo</div> <div>Lorem ipsum dolor sit amet ex ea</div> </body>

Grce notre nouveau slecteur, il est trs facile de mettre en exergue les lments les moins larges de cette liste (voir Figure 11.20) :
$(document).ready(function() { $('div:css(width < 100)').addClass('highlight'); });

Chapitre 11

Dveloppement de plugins

355

Figure 11.19
Le document de test du slecteur.

Figure 11.20
Slection des lments les moins larges.

11.7

Distribuer un plugin

Lorsque le plugin est termin, nous souhaitons le publier afin que les autres dveloppeurs puissent profiter du code, voire lamliorer. Pour cela, nous pouvons le dposer dans le catalogue officiel des plugins de jQuery http://plugins.jquery.com/. Sur ce site, nous pouvons ouvrir une session ou nous inscrire si ce nest dj fait, puis suivre les instructions des options du plugin et envoyer une archive .zip de son code. Mais, avant cela, nous devons vrifier que le plugin est parfaitement termin et prt tre utilis par tout le monde. Nous devons respecter quelques rgles dcriture des plugins pour quils puissent fonctionner correctement avec dautres codes. Nous en avons dj vu certaines, mais elles sont regroupes ici pour plus de commodit.

356

jQuery

Conventions de nommage Tout fichier de plugin doit tre nomm jQuery.monPlugin.js, o monPlugin correspond au nom du plugin. Dans le fichier, toutes les fonctions globales doivent tre regroupes dans un objet nomm jQuery.monPlugin, except lorsquil ny en a quune. Dans ce cas, elle peut tre une fonction appele simplement jQuery.monPlugin(). Le nommage des mthodes est plus souple, mais les noms doivent tre uniques autant que possible. Si une seule mthode est dfinie, elle doit sappeler jQuery.fn.monPlugin(). Si plusieurs mthodes sont dfinies, chaque nom doit commencer par celui du plugin pour viter toute confusion. Il est prfrable dviter les noms de mthodes courts et ambigus, comme .load() ou .get(), qui peuvent tre confondus avec des mthodes dfinies dans dautres plugins. Utiliser lalias $ Les plugins jQuery ne doivent pas supposer que lalias $ soit disponible. la place, il faut crire chaque fois le nom complet jQuery. Pour les plugins assez longs, les dveloppeurs trouvent souvent que labsence du raccourci $ rend le code plus difficile lire. Pour y remdier, le raccourci peut tre cr dans la porte du plugin en dfinissant et en excutant une fonction. Voici la syntaxe qui permet la fois de dfinir et dexcuter une fonction :
(function($) { // Code excuter. })(jQuery);

La fonction enveloppante prend un seul paramtre dans lequel nous passons lobjet jQuery global. Puisque largument se nomme $, nous pouvons utiliser lalias $ lintrieur de la fonction sans craindre les conflits. Interfaces de mthode Toutes les mthodes jQuery sont appeles dans le contexte dun objet jQuery. Par consquent, this dsigne un objet qui peut faire rfrence un ou plusieurs lments du DOM. Toutes les mthodes doivent avoir un comportement correct vis--vis des lments slectionns. En gnral, les mthodes doivent appeler this.each() pour itrer sur tous les lments slectionns, en les traitant chacun tour tour. Les mthodes doivent retourner lobjet jQuery pour conserver le chanage. Si la collection des objets slectionns est modifie, un nouvel objet doit tre cr en invoquant .pushStack() et cet objet doit tre retourn. Si la mthode retourne autre chose quun objet jQuery, la documentation doit lindiquer clairement.

Chapitre 11

Dveloppement de plugins

357

Lorsque des mthodes prennent plusieurs options, il est prfrable dutiliser une mappe en argument afin de libeller les options et de ne pas fixer un ordre. Des valeurs par dfaut doivent tre dfinies dans une mappe, qui pourra tre redfinie si ncessaire. Les dfinitions de mthodes doivent se terminer par un point-virgule (;) pour que les compacteurs de code puissent effectuer une analyse correcte des fichiers. Par ailleurs, les plugins peuvent commencer par un caractre point-virgule afin que les autres scripts mal crits ne provoquent pas de conflits aprs la compression. Style de la documentation Une documentation doit tre ajoute dans le fichier avant chaque dfinition de fonction ou de mthode, en utilisant le format ScriptDoc. Pour de plus amples informations sur ce format, consultez le site http://www.scriptdoc.org/.

11.8

En rsum

Dans ce dernier chapitre, nous avons vu que les fonctionnalits fournies en standard par jQuery ne limitent en rien les possibilits de la bibliothque. Les plugins disponibles permettent dtendre la liste des fonctionnalits et nous pouvons crer nos propres plugins pour aller encore plus loin. Les plugins que nous avons crs proposent diffrentes fonctionnalits, notamment lajout de fonctions globales qui se fondent sur la bibliothque jQuery, de nouvelles mthodes de lobjet jQuery pour manipuler les lments du DOM, des mthodes extensibles quil est facile de personnaliser, et des expressions de slection amliores pour rechercher de diffrentes manires les lments du DOM. Avec ces outils disposition, nous pouvons modeler jQuery, ainsi que notre propre code JavaScript, comme bon nous semble.

Annexe A
Ressources en ligne
Les ressources en ligne suivantes constituent un point de dpart pour en apprendre plus sur jQuery, JavaScript et le dveloppement web de manire gnrale. Les sources dinformation sont beaucoup trop nombreuses sur le Web pour que cette annexe puisse en faire une liste exhaustive. Par ailleurs, vous trouverez galement des informations prcieuses dans dautres publications imprimes, mais nous ne les citons pas ici.

A.1

Documentation sur jQuery

Les ressources suivantes constituent des rfrences et fournissent des dtails sur la bibliothque jQuery. Wiki jQuery
n

http://docs.jquery.com/

Ce site est un wiki. Autrement dit, son contenu peut tre modifi par des internautes. Vous y trouverez des informations sur lintgralit de lAPI de jQuery, des didacticiels, des guides de dmarrage et plus encore. API de jQuery
n

http://remysharp.com/jquery-api/

Si la documentation officielle se trouve sur le site jquery.com, celui-ci propose galement des informations sur lAPI. Navigateur dans lAPI de jQuery
n

http://jquery.bassistance.de/api-browser-1.2/

Jrn Zaefferer a runi lAPI de jQuery dans un systme pratique avec navigation arborescente. Ce site propose galement une fonctionnalit de recherche, ainsi quun tri alphabtique et par catgorie.

360

jQuery

Visual jQuery
n

http://www.visualjquery.com/

Ce systme de navigation dans lAPI, conu par Yehuda Katz et mis jour par Remy Sharp, est la fois lgant et pratique. Il propose galement une prsentation rapide des mthodes de plusieurs plugins jQuery. Visionneuse de lAPI de jQuery avec Adobe AIR
n

http://remysharp.com/downloads/jquery-api-browser.air.zip

Remy Sharp a conditionn lAPI de jQuery dans une application Adobe AIR de manire pouvoir la consulter hors ligne.

A.2

Rfrences JavaScript

Les sites suivants proposent des rfrences et des guides sur le langage JavaScript en gnral, sans se focaliser sur jQuery. Centre de dveloppement Mozilla
n

https://developer.mozilla.org/fr/JavaScript

Ce site comprend une rfrence complte sur JavaScript, un guide de programmation avec JavaScript, des liens vers des outils utiles et bien dautres informations. Dev.opera
n

http://dev.opera.com/articles/

Bien quorient vers son propre navigateur, le site dOpera pour les dveloppeurs web inclut de nombreux articles intressants sur JavaScript. Rfrence JScript de MSDN
n

http://msdn.microsoft.com/fr-fr/library/x85xxsf4.aspx

La rfrence JScript sur MSDN dcrit lintgralit des fonctions, des objets et autres lments. Elle est particulirement utile pour comprendre la mise en uvre Microsoft du standard ECMAScript dans Internet Explorer. Quirksmode
n

http://www.quirksmode.org/

Le site Quirksmode de Peter-Paul Koch est une formidable ressource pour comprendre les diffrences dimplmentation des fonctions JavaScript et des proprits CSS dans les navigateurs.

Annexe A

Ressources en ligne

361

Bote outils JavaScript


n

http://www.javascripttoolbox.com/

La bote outils JavaScript de Matt Kruse propose un large assortiment de bibliothques JavaScript rustiques, des conseils aviss sur les meilleures pratiques JavaScript et une collection de ressources JavaScript dcrites ailleurs sur le Web.

A.3

Compacteurs de code JavaScript

Au cours de la phase finale de construction dun site, il est souvent avis de compacter le code JavaScript. Cette procdure permet de rduire le temps de tlchargement des pages. YUI Compressor
n

http://developer.yahoo.com/yui/compressor/

Ce compacteur JavaScript propos par Yahoo! UI Library permet de rduire la taille du code source jQuery. Loutil Java en ligne de commande peut tre tlcharg gratuitement. Le code rsultant est trs efficace, tant en termes de taille de fichier que de performances, et peut mme faire lobjet dune compression Gzip supplmentaire. JSMin
n

http://www.crockford.com/javascript/jsmin.html

Cr par Douglas Crockford, JSMin est un filtre qui retire les commentaires et les espaces inutiles dans les fichiers JavaScript. En gnral, il permet de diviser par deux la taille des fichiers, conduisant des tlchargements plus rapides, en particulier sil est associ une compression du fichier sur le serveur. Embellisseur de code
n

http://www.prettyprinter.de/

Cet outil intervient sur le code JavaScript qui a t compress. Il remet en place les passages la ligne et lindentation lorsque cest possible. Ses diffrentes options permettent dajuster le rsultat obtenu.

A.4

Rfrence (X)HTML

La bibliothque jQuery est plus efficace lorsquelle est associe des documents HTML et XHTML smantiques correctement baliss. La ressource suivante constitue une aide sur ces langages de balisage.

362

jQuery

Page daccueil des langages de balisage du W3C


n

http://www.w3.org/MarkUp/

Le W3C (World Wide Web Consortium) est en charge de la dfinition du standard pour (X)HTML. La page daccueil du groupe de travail sur HTML est un bon point de dpart pour consulter les spcifications et les guides.

A.5

Rfrences CSS

Les effets et les animations que nous avons rencontrs se fondent sur la puissance des feuilles de style en cascade (CSS, Cascading Style Sheets). Pour ajouter vos sites des dcorations visuelles, vous pouvez consulter les ressources CSS suivantes. Page daccueil des feuilles de style en cascade du W3C
n

http://www.w3.org/Style/CSS/

La page daccueil CSS du W3C fournit des liens vers des didacticiels, des spcifications, des suites de tests et dautres ressources. Fiche CSS de Mezzoblue
n

http://mezzoblue.com/css/cribsheet/

Dave Shea propose cette fiche CSS pour faciliter le processus de conception et fournir une rfrence rapide consulter lorsque vous faites face un problme. Position is everything
n

http://www.positioniseverything.net/

Ce site est un catalogue des bogues des navigateurs vis--vis de CSS. Il explique galement comment les contourner.

A.6

Blogs

Dans toute technologie dynamique, de nouvelles techniques et de nouvelles fonctionnalits sont constamment dveloppes et proposes. Pour rester jour, il suffit de consulter de temps en temps les ressources suivantes, qui proposent une actualit du dveloppement web.

Annexe A

Ressources en ligne

363

Le blog de jQuery
n

http://jquery.com/blog/

John Resig et dautres contributeurs au blog officiel de jQuery publient des annonces concernant les nouvelles versions et dautres initiatives en cours dans le projet. Ils proposent parfois des didacticiels et dautres informations ditoriales. Learning jQuery
n

http://www.learningjquery.com/

Karl Swedberg est responsable de ce blog qui sintresse aux didacticiels, aux techniques et aux annonces concernant jQuery. Parmi les auteurs invits se trouvent Mike Alsup et Brandon Aaron, qui font partie de lquipe du projet jQuery. Ajaxian
n

http://ajaxian.com/

Ce blog frquemment mis jour a t initi par Dion Almaer et Ben Galbraith. Il propose de nombreuses nouvelles et fonctionnalits pour JavaScript, ainsi que des didacticiels occasionnels. John Resig
n

http://ejohn.org/

Le crateur de jQuery, John Resig, discute de sujets JavaScript avancs sur son blog personnel. JavaScript ant
n

http://javascriptant.com/

Ce site propose un catalogue darticles concernant JavaScript et ses utilisations dans les navigateurs web modernes. Vous y trouverez galement une liste organise des ressources JavaScript disponibles sur le Web. Robert's talk
n

http://www.robertnyman.com/

Robert Nyman rdige des billets sur le dveloppement pour Internet, notamment sur les scripts ct client.

364

jQuery

Standards web avec imagination


n

http://www.dustindiaz.com/

Le blog de Dustin Diaz propose des articles sur la conception et le dveloppement web, en mettant laccent sur JavaScript. Snook
n

http://snook.ca/

Le blog de Jonathan Snook sintresse au dveloppement web et la programmation en gnral. Ressources JavaScript de Matt Snider
n

http://mattsnider.com/

Le blog de Matt Snider est ddi la comprhension de JavaScript et de ses nombreux frameworks rpandus. I can't
n n n

http://icant.co.uk/ http://www.wait-till-i.com/ http://www.onlinetools.org/

Ces trois sites de Christian Heilmann proposent des billets de blog, des exemples de code et de longs articles concernant JavaScript et le dveloppement web. Scripts pour le DOM
n

http://domscripting.com/blog/

Le blog de Jeremy Keith commence l o le livre DOM scripting sarrte. Il constitue une ressource inestimable pour du JavaScript non intrusif. As days pass by
n

http://www.kryogenix.org/code/browser/

Stuart Langridge mne des expriences sur lutilisation avance du DOM dans le navigateur.

Annexe A

Ressources en ligne

365

A list apart
n

http://www.alistapart.com/

Ce site explore la conception, le dveloppement et la signification du contenu web, avec un intrt particulier sur les standards web et les meilleures pratiques.

A.7

Frameworks de dveloppement web utilisant jQuery

Lorsque les dveloppeurs de projets open-source ont connu lexistence de jQuery, ils ont t nombreux inclure cette bibliothque JavaScript dans leur propre systme. Voici une liste non exhaustive de ces frameworks :
n n n n n n n n n n n n n n

Digitalus Site Manager (http://code.google.com/p/digitalus-site-manager/) ; Drupal (http://drupal.org/) ; DutchPIPE (http://dutchpipe.org/) ; Hpricot (http://code.whytheluckystiff.net/hpricot/) ; JobberBase (http://www.jobberbase.com/) ; Laconica (http://laconi.ca/) ; Piwik (http://piwik.org/) ; Pommo (http://pommo.org/) ; simfony (http://www.symfony-project.org/) ; SPIP (http://www.spip.net/) ; Textpattern (http://www.textpattern.com/) ; Trac (http://trac.edgewall.org/) ; WordPress (http://wordpress.org/) ; Z-Blog (http://www.rainbowsoft.org/zblog).

Vous trouverez une liste plus complte en allant sur la page Sites Using jQuery ladresse http://docs.jquery.com/Sites_Using_jQuery.

Annexe B
Outils de dveloppement
Si la documentation aide la rsolution des problmes rencontrs dans les applications JavaScript, rien ne remplace de bons outils de dveloppement. Heureusement, il existe de nombreux paquetages logiciels pour inspecter et dboguer du code JavaScript, dont la plupart sont gratuits.

B.1

Outils pour Firefox

Mozilla Firefox est un navigateur choisi par de nombreux dveloppeurs web et cest pourquoi il dispose des outils de dveloppement parmi les plus complets et les plus reconnus. Firebug
n

http://www.getfirebug.com/

Lextension Firebug pour Firefox est indispensable tout dveloppement jQuery. Voici certaines de ses fonctionnalits :
n

un excellent inspecteur du DOM pour rechercher les noms et les slecteurs des lments du document ; des outils de manipulation des styles CSS pour comprendre pourquoi une page a une certaine apparence et la modifier ; une console JavaScript interactive ; un dbogueur JavaScript, avec suivi des variables et trace de lexcution du code.

n n

368

jQuery

Barre doutils du dveloppeur web


n

http://chrispederick.com/work/web-developer/

Cette barre doutils non seulement entre en concurrence avec Firebug dans le domaine de linspection du DOM, mais propose galement des outils pour les tches courantes, comme la manipulation des cookies, linspection des formulaires et le redimensionnement de la page. Vous pouvez vous servir de cette barre doutils pour dsactiver JavaScript de manire vrifier que le site se dgrade de manire lgante lorsque le navigateur de lutilisateur offre moins de possibilits. Venkman
n

http://www.mozilla.org/projects/venkman/

Venkman est le dbogueur JavaScript officiel du projet Mozilla. Il fournit un environnement de dpannage issu de GDB, qui sert au dbogage des programmes crits dans dautres langages. Testeur dexpressions rgulires
n

http://sebastianzartner.ath.cx/new/downloads/RExT/

Il nest pas toujours facile de construire les bonnes expressions rgulires pour la correspondance des chanes de caractres en JavaScript. Cette extension de Firefox permet de tester facilement des expressions rgulires en utilisant une interface de saisie du texte de recherche.

B.2

Outils pour Internet Explorer

Les sites nont pas toujours le mme comportement dans Internet Explorer que dans les autres navigateurs web. Il est donc important de disposer doutils de dbogage pour cette plate-forme. Barre doutils du dveloppeur pour Microsoft Internet Explorer
n

http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d4511-bb3e-2d5e1db91038

La barre doutils du dveloppeur propose essentiellement une vue de larborescence DOM de la page web. Il est possible de localiser visuellement des lments et dapporter des modifications la vole avec de nouvelles rgles CSS. Elle apporte galement dautres aides au dveloppement, comme une rgle permettant de mesurer les lments sur la page.

Annexe B

Outils de dveloppement

369

Visual Web Developer de Microsoft


n

http://msdn.microsoft.com/fr-fr/express/aa700797.aspx

Cette version de Visual Studio peut servir inspecter et dboguer du code JavaScript. Pour excuter interactivement le dbogueur dans la version gratuite (Visual Web Developer Express), suivez la procdure dtaille sur le site http://www.berniecode.com/ blog/2007/03/08/how-to-debug-javascript-with-visual-web-developer-express/. DebugBar
n

http://www.debugbar.com/

DebugBar propose un inspecteur du DOM, ainsi quune console JavaScript pour le dbogage. Drip
n

http://Sourceforge.net/projects/ieleak/

Les fuites de mmoire dans le code JavaScript peuvent conduire des problmes de performances et de stabilit avec Internet Explorer. Drip facilite la dtection et lisolation de ces fuites. Pour de plus amples informations concernant les causes des fuites de mmoire dans Internet Explorer, consultez lAnnexe C.

B.3

Outils pour Safari

Safari est le dernier-n en tant que plate-forme de dveloppement, mais il existe nanmoins des outils pour examiner le code lorsque son comportement est diffrent dans ce navigateur. Menu Dveloppement Depuis Safari 3.1, une option de longlet AVANCES des prfrences donne accs un menu spcial nomm DVELOPPEMENT. Lorsque ce menu est activ, un inspecteur web et une console JavaScript sont disponibles. Inspecteur web
n

http://trac.webkit.org/wiki/Web%20Inspector

Safari 3 offre la possibilit dinspecter les lments de la page et de collecter des informations, notamment concernant les rgles CSS appliques.

370

jQuery

Les versions actuelles de WebKit ont beaucoup amlior linspecteur web, en lui donnant plusieurs des excellentes fonctionnalits de Firebug, comme un dbogueur JavaScript intgr nomm Drosera (http://trac.webkit.org/wiki/Drosera).

B.4

Outils pour Opera

Bien que ses parts de march sur le segment des navigateurs soient faibles, Opera est un acteur important dans les systmes embarqus et les priphriques mobiles. Ses possibilits doivent tre examines avec attention au cours dun dveloppement web. Dragonfly Encore au dbut de son dveloppement, Dragonfly est un environnement de dbogage prometteur pour les navigateurs Opera sur les ordinateurs ou les priphriques mobiles. Ses fonctionnalits sont comparables celles de Firebug, y compris le dbogage JavaScript et linspection et la modification du DOM et des styles CSS.

B.5

Autres outils

Si les outils prcdents taient spcifiques un navigateur, les utilitaires suivants ont une gamme dutilisation plus large. Firebug Lite
n

http://www.getfirebug.com/lite.html

Lextension Firebug est limite au navigateur web Firefox, mais certaines de ses fonctionnalits peuvent tre reproduites en incluant le script Firebug Lite dans la page web. Ce paquetage simule la console Firebug, autorisant mme les appels console.log() dans tous les navigateurs, sans gnrer derreurs JavaScript. NitobiBug
n

http://www.nitobibug.com/

linstar de Firebug Lite, NitobiBug est un outil multinavigateur qui offre certaines des fonctionnalits de lextension Firebug en plus robuste et abouti. Sa force rside dans son inspecteur du DOM et des objets, mme sil propose galement une console. Pour invoquer la console et linspecteur, il suffit dinclure une rfrence au fichier JavaScript de Nitobi et dappeler nitobi.Debug.log().

Annexe B

Outils de dveloppement

371

Extension TextMate pour jQuery


n

http://github.com/kswedberg/jquery-tmbundle/

Cette extension de TextMate, un diteur de texte trs rpandu sur Mac OS X, apporte la mise en exergue syntaxique pour les mthodes et les slecteurs jQuery, la compltion du code pour les mthodes et un accs une rfrence rapide de lAPI depuis le code. Le paquetage est galement compatible avec lditeur de texte E pour Windows. Charles
n

http://www.xk72.com/charles/

Lors du dveloppement dapplications fortement orientes AJAX, il est utile de voir prcisment quelles donnes sont changes entre le navigateur et le serveur. Le proxy de dbogage web Charles affiche tout le trafic HTTP entre deux extrmits, y compris les requtes web normales, le trafic HTTPS, les changes Flash remoting et les rponses AJAX. Fiddler
n

http://www.fiddlertool.com/fiddler/

Fiddler est un autre proxy de dbogage HTTP, dont les caractristiques sont comparables celles de Charles. Si lon en croit son site, Fiddler "comporte un sous-systme puissant dcriture de script base dvnements et peut tre tendu en utilisant nimporte quel langage .NET". Aptana
n

http://www.aptana.com/

Cet IDE de dveloppement web bas sur Java est libre et multiplate-forme. Disposant de fonctionnalits ddition du code standard et labores, il inclut la documentation complte de lAPI de jQuery et dispose de son propre dbogueur JavaScript fond sur Firebug.

Annexe C
Fermetures en JavaScript
Tout au long de cet ouvrage, nous avons rencontr de nombreuses mthodes jQuery qui prennent des fonctions en argument. Nos exemples ont donc cr, invoqu et pass des fonctions. Si une comprhension lmentaire des mcanismes internes de JavaScript suffit mettre en uvre cette possibilit, les effets secondaires de nos actions peuvent parfois sembler tranges si nous ne matrisons pas les caractristiques du langage. Dans cette annexe, nous allons tudier un aspect des fonctions des plus sotrique, quoique rpandu, appel fermetures (closures). Notre prsentation sarticulera autour de nombreux petits exemples de code qui nous serviront afficher des messages. Au lieu dutiliser des mcanismes de journalisation propres au navigateur, comme console.log() pour Firefox, ou de crer une suite de botes de dialogue alert(), nous nous servirons dune courte mthode de plugin :
jQuery.fn.print = function(message) { return this.each(function() { $('<div class="result" />') .text(String(message)) .appendTo($(this).find('.results')); }); };

Aprs avoir dfini cette mthode, nous invoquons $('#example').print('salut') pour ajouter le texte "salut" <div id="example">.

C.1

Fonctions internes

JavaScript a la chance de faire partie des langages de programmation qui prennent en charge les fonctions internes. De nombreux langages de programmation classiques, comme C, placent toutes les fonctions dans une mme porte au niveau suprieur. En revanche, les langages qui reconnaissent les fonctions internes permettent de placer de petites fonctions utilitaires l o elles sont requises, vitant ainsi la pollution de lespace de noms.

374

jQuery

Une fonction interne nest rien dautre quune fonction dfinie lintrieur dune autre fonction, par exemple :
function outerFn() { function innerFn() { } }

Dans cet exemple, la fonction interne innerFn() est dfinie lintrieur de la porte de outerFn(). Autrement dit, lappel innerFn() nest valide que depuis outerFn(). Le code suivant gnre une erreur JavaScript :
function outerFn() { $('#example-2').print('Fonction externe'); function innerFn() { $('#example-1').print('Fonction interne'); } } $('#example-1').print('innerFn():'); innerFn();

Pour viter lerreur, nous pouvons invoquer innerFn() partir de outerFn() :


function outerFn() { $('#example-2').print('Fonction externe'); function innerFn() { $('#example-2').print('Fonction interne'); } innerFn(); } $('#example-2').print('outerFn():'); outerFn();

Voici la sortie obtenue :


outerFn(): Fonction externe Fonction interne

Cette technique se rvle particulirement utile pour les petites fonctions effectuant une tche unique. Par exemple, les algorithmes rcursifs sans API rcursive sexpriment gnralement mieux avec une fonction interne. La grande vasion Le mystre spaissit lorsque les rfrences de fonctions sont utilises. Certains langages, comme Pascal, ne prennent en charge les fonctions internes que dans le but de masquer du code. Ces fonctions sont enterres jamais avec leurs fonctions parents. En revanche, JavaScript nous permet de passer des fonctions comme sil sagissait de nimporte quel autre type de donnes. Autrement dit, les fonctions internes peuvent chapper leurs geliers. La voie de lvasion peut prendre de nombreuses directions. Par exemple, supposons que la fonction soit affecte une variable globale :

Annexe C

Fermetures en JavaScript

375

var globalVar; function outerFn() { $('#example-3').print('Fonction externe'); function innerFn() { $('#example-3').print('Fonction interne'); } globalVar = innerFn(); } $('#example-3').print('outerFn():'); outerFn(); $('#example-3').print('globalVar():'); globalVar();

Lappel outerFn() aprs la dfinition de la fonction modifie la variable globale globalVar, qui fait alors rfrence innerFn(). Autrement dit, lappel suivant globalVar() fonctionne exactement comme un appel interne innerFn(), et les instructions daffichage sont excutes :
outerFn(): Fonction externe globalVar(): Fonction interne

Notez quun appel innerFn() depuis lextrieur de outerFn() gnre toujours une erreur ! Mme si la fonction sest vade par lintermdiaire de la rfrence enregistre dans la variable globale, le nom de la fonction reste enferm dans la porte de outerFn(). Une rfrence de fonction peut galement schapper de sa fonction parent en passant par la valeur de retour :
function outerFn() { $('#example-4').print('Fonction externe'); function innerFn() { $('#example-4').print('Fonction interne'); } return innerFn(); } $('#example-4').print('var fnRef = outerFn():'); var fnRef = outerFn(); $('#example-4').print(fnRef():'); fnRef();

Dans ce cas, aucune variable nest modifie par outerFn(), qui retourne la place une rfrence innerFn(). Linvocation de outerFn() retourne cette rfrence, qui est enregistre puis invoque, pour produire nouveau le mme message :
var fnRef = outerFn(): Fonction externe fnRef(): Fonction interne

Puisque les fonctions internes peuvent tre invoques au travers dune rfrence, mme aprs que la fonction nest plus dans la porte, JavaScript doit garder disponibles les fonctions rfrences aussi longtemps quil est possible de les invoquer. Chaque varia-

376

jQuery

ble qui fait rfrence la fonction est gre par le systme dexcution de JavaScript. Ensuite, lorsque la dernire a disparu, le ramasse-miettes de JavaScript entre en scne et libre la mmoire auparavant occupe. Porte de variable Les fonctions internes peuvent videmment possder leurs propres variables, dont la porte est alors limite la fonction elle-mme :
function outerFn() { function innerFn() { var innerVar = 0; innerVar++; $('#example-5').print('innerVar = ' + innerVar); } return innerFn(); } var fnRef = outerFn(); fnRef(); fnRef(); var fnRef2 = outerFn(); fnRef2(); fnRef2();

Chaque fois que la fonction est appele, que ce soit au travers dune rfrence ou par un autre moyen, une nouvelle variable innerVar est cre, incrmente, puis affiche :
innerVar innerVar innerVar innerVar = = = = 1 1 1 1

linstar de nimporte quelle autre fonction, les fonctions internes peuvent faire rfrence des variables globales :
var globalVar = 0; function outerFn() { function innerFn() { globalVar++; $('#example-6').print('globalVar = ' + globalVar); } return innerFn(); } var fnRef = outerFn(); fnRef(); fnRef(); var fnRef2 = outerFn(); fnRef2(); fnRef2();

Dans ce cas, la fonction incrmente la variable lors de chaque appel :


globalVar globalVar globalVar globalVar = = = = 1 2 3 4

Annexe C

Fermetures en JavaScript

377

Quen est-il des variables locales de la fonction parent ? Puisque la fonction interne hrite de la porte de son parent, ces variables peuvent galement tre utilises :
function outerFn() { var outerVar = 0; function innerFn() { outerVar++; $('#example-7').print('outerVar = ' + outerVar); } return innerFn(); } var fnRef = outerFn(); fnRef(); fnRef(); var fnRef2 = outerFn(); fnRef2(); fnRef2();

Les appels notre fonction ont alors un comportement plus intressant :


outerVar outerVar outerVar outerVar = = = = 1 2 1 2

Nous obtenons un mlange des deux effets prcdents. Les appels innerFn() par lintermdiaire de chaque rfrence incrmentent indpendamment outerVar. Notez que le deuxime appel outerFn() ne rinitialise pas la valeur de outerVar, mais cre la place une nouvelle instance de outerVar lie la porte du deuxime appel de fonction. En consquence, aprs les premiers appels, une autre invocation de fnRef() afficherait la valeur 3 et un autre appel fnRef2() afficherait galement 3. Les deux compteurs sont totalement spars. Lorsquune rfrence dans une fonction interne trouve un moyen de sortir de la porte dans laquelle la fonction a t dfinie, cela cre une fermeture sur cette fonction. Les variables qui ne sont ni des paramtres ni des variables locales la fonction interne sont appeles variables libres, et lenvironnement dun appel la fonction externe les ferme. Le fait que la fonction fasse rfrence une variable locale de la fonction externe accorde un sursis la variable. La mmoire nest pas libre lorsque la fonction se termine, puisquelle peut encore tre ncessaire la fermeture.

C.2

Interactions entre fermetures

Lorsquil existe plusieurs fonctions internes, des fermetures peuvent avoir des effets difficiles anticiper. Supposons que nous associions notre fonction dincrmentation une autre fonction, qui ajoute deux :

378

jQuery

function outerFn() { var outerVar = 0; function innerFn1() { outerVar++; $('#example-8').print('(1) outerVar = ' + outerVar); } function innerFn2() { outerVar += 2; $('#example-8').print('(2) outerVar = ' + outerVar); } return {'fn1': innerFn1, 'fn2': innerFn2}; } var fnRef = outerFn(); fnRef.fn1(); fnRef.fn2(); fnRef.fn1(); var fnRef2 = outerFn(); fnRef2.fn1(); fnRef2.fn2(); fnRef2.fn1();

Nous retournons des rfrences aux deux fonctions en utilisant une mappe (une autre manire pour une rfrence une fonction interne de svader de sa fonction parent). Les deux fonctions sont invoques par lintermdiaire des rfrences :
(1) (2) (1) (1) (2) (1) outerVar outerVar outerVar outerVar outerVar outerVar = = = = = = 1 3 4 1 3 4

Les deux fonctions internes font rfrence la mme variable locale et partagent donc le mme environnement de fermeture. Lorsque innerFn1() incrmente de 1 la variable outerVar, elle fixe sa nouvelle valeur initiale pour lappel innerFn2(), et vice versa. Cependant, nous voyons une fois encore que tout appel ultrieur outerFn() cre de nouvelles instances de ces fermetures, avec un nouvel environnement de fermeture correspondant. Les amateurs de programmation oriente objet remarqueront que nous avons en ralit cr un nouvel objet, avec les variables libres jouant le rle des variables dinstance et les fermetures, celui des mthodes dinstance. Les variables sont galement prives, car il est impossible de les rfrencer directement en dehors de la porte englobante. Nous avons donc de relles donnes prives orientes objet.

C.3

Fermetures dans jQuery

Les mthodes jQuery que nous avons rencontres prennent souvent au moins une fonction en argument. Pour des raisons pratiques, nous utilisons des fonctions anonymes afin que le comportement de la fonction soit dfini l o il est requis. Autrement dit, ces fonctions se trouvent rarement dans lespace de noms de premier niveau ; il sagit gnralement de fonctions internes, qui peuvent donc facilement crer des fermetures.

Annexe C

Fermetures en JavaScript

379

Arguments de $(document).ready() Pratiquement tout le code que nous crivons avec jQuery finit par se retrouver dans une fonction passe en argument $(document).ready(). Cela nous permet dtre certains que le DOM a t charg avant que le code ne soit excut, ce qui est gnralement indispensable pour du code jQuery intressant. Lorsquune fonction est cre et passe .ready(), une rfrence cette fonction est enregistre dans lobjet jQuery global. Cette rfrence est ensuite invoque ultrieurement lorsque le DOM est prt. Puisque la construction $(document).ready() se trouve gnralement au sommet de la structure du code, cette fonction ne fait pas rellement partie dune fermeture. En revanche, puisque notre code est habituellement crit dans cette fonction, tout le reste constitue une fonction interne :
$(document).ready(function() { var readyVar = 0; function innerFn() { readyVar++; $('#example-9').print('readyVar = ' + readyVar); } innerFn(); innerFn(); });

Cet exemple ressemble aux nombreux exemples prcdents, except que, dans ce cas, la fonction externe est la fonction de rappel passe $(document).ready(). Puisque innerFn() est dfinie lintrieur de cette fonction et quelle fait rfrence readyVar, qui se trouve dans la porte de la fonction de rappel, innerFn() et son environnement crent une fermeture. Nous pouvons le constater en remarquant que la valeur de la variable readyVar persiste entre les appels la fonction :
readyVar = 1 readyVar = 2

Le fait que la plupart du code jQuery se trouve dans le corps dune fonction se rvle utile. En effet, cela permet dviter certains conflits sur lespace de noms. Par exemple, cest grce cette caractristique que nous pouvons utiliser jQuery.noConflict() pour rendre le raccourci $ dautres bibliothques, tout en tant capable de le dfinir localement pour lutiliser dans $(document).ready(). Gestionnaires dvnements La construction $(document).ready() englobe gnralement le reste de notre code, y compris laffectation des gestionnaires dvnements. Puisque les gestionnaires sont des fonctions, il sagit donc de fonctions internes. Puisque ces fonctions internes sont enregistres et invoques ultrieurement, elles peuvent crer des fermetures. Un simple gestionnaire de click illustrera ce propos :

380

jQuery

$(document).ready(function() { var counter = 0; $('#example-10 a.add').click(function() { counter++; $('#example-10').print('counter = ' + counter); return false; }); });

Puisque la variable counter est dclare dans le gestionnaire .ready(), elle nest disponible quau code jQuery prsent dans ce bloc, non au code extrieur. Cependant, elle peut tre rfrence par le code du gestionnaire de click, qui incrmente cette variable et affiche sa valeur. Une fermeture tant cre, la mme instance de counter est rfrence chaque clic sur le lien. Cela signifie que les messages affichent une suite de valeurs incrmentes au lieu de 1 :
counter = 1 counter = 2 counter = 3

Des gestionnaires dvnements peuvent partager leurs environnements de fermeture, comme nimporte quelle autre fonction :
$(document).ready(function() { var counter = 0; $('#example-11 a.add').click(function() { counter++; $('#example-11').print('counter = ' + counter); return false; }); $('#example-11 a.subtract').click(function() { counter--; $('#example-11').print('counter = ' + counter); return false; }); });

Puisque ces deux fonctions font rfrence la mme variable counter, les oprations dincrmentation et de dcrmentation associes aux deux liens oprent sur la mme variable :
counter counter counter counter = = = = 1 2 1 0

Les exemples prcdents ont utilis des fonctions anonymes, comme nous en avons lhabitude dans notre code jQuery. Du point de vue de la construction des fermetures, cela ne fait aucune diffrence ; les fermetures peuvent provenir de fonctions anonymes ou de fonctions nommes. Par exemple, nous pouvons crire une fonction anonyme qui affiche lindice dun lment dans un objet jQuery :

Annexe C

Fermetures en JavaScript

381

$(document).ready(function() { $('#example-12 a').each(function(index) { $(this).click(function() { $('#example-12').print('index = ' + index); return false; }); }); });

Puisque la fonction la plus interne est dfinie dans la fonction de rappel de .each(), le code cre autant de fonctions quil y a de liens. Chacune de ces fonctions est attache au gestionnaire de click dun des liens. Lenvironnement de fermeture des fonctions comprend index, puisquil sagit dun paramtre de la fonction de rappel de .each(). Le fonctionnement est identique celui que lon obtiendrait si le gestionnaire de click tait crit sous forme dune fonction nomme :
$(document).ready(function() { $('#example-13 a').each(function(index) { function clickHandler() { $('#example-13').print('index = ' + index); return false; } $(this).click(clickHandler); }); });

La version fonde sur la fonction anonyme est simplement un peu plus courte. Lemplacement de la fonction nomme reste cependant important :
$(document).ready(function() { function clickHandler() { $('#example-14').print('index = ' + index); return false; } $('#example-14 a').each(function(index) { $(this).click(clickHandler); }); });

Cette version gnre une erreur JavaScript lors du clic sur un lien, car index ne se trouve pas dans lenvironnement de fermeture de clickHandler(). Il sagit dune variable libre, qui nest donc pas dfinie dans ce contexte.

C.4

Dangers lis aux fuites de mmoire

En JavaScript, la mmoire est gre en utilisant la technique dite du ramasse-miettes. Il se distingue en cela des langages de bas niveau, comme C, qui obligent les programmeurs rserver des blocs de mmoire et les librer explicitement lorsquils ne sont plus utiliss. Dautres langages, comme Objective-C, aident le programmeur en mettant en uvre un mcanisme de comptage des rfrences, qui lui permet de connatre le nombre de composants du programme qui utilisent une zone de mmoire prcise et de

382

jQuery

la nettoyer lorsquelle nest plus utilise. A contrario, JavaScript est un langage de haut niveau qui soccupe lui-mme de ces aspects. Ds quun nouvel lment rsidant en mmoire, comme un objet ou une fonction, est rencontr dans le code JavaScript, une zone mmoire lui est rserve. Lorsque lobjet est pass dune fonction lautre et affect des variables, dautres parties du code y font rfrence. JavaScript conserve une trace de ces pointeurs et, lorsque le dernier a disparu, la mmoire occupe par lobjet est libre. Prenons lexemple de la chane de pointeurs illustre la Figure C.1.
Figure C.1
Objets relis par des pointeurs.

Lobjet A possde une proprit qui pointe sur B, et une proprit de B pointe sur C. Mme si lobjet A est le seul qui contient une variable dans la porte courante, les trois objets doivent rester en mmoire en raison des diffrents pointeurs. En revanche, lorsque A nest plus dans la porte, par exemple la fin de la fonction dans laquelle il est dclar, il peut tre collect par le ramasse-miettes. Ensuite, plus aucun pointeur ne vise lobjet B, qui peut donc tre libr. Enfin, le mme raisonnement sapplique C. La Figure C.2 prsente une chane de rfrence plus complexe traiter. Nous avons ajout une proprit lobjet C pour faire rfrence lobjet B. Dans ce cas, mme lorsque A est libr, il existe toujours un pointeur sur B venant de C. Cette rfrence circulaire ncessite un traitement particulier de la part de JavaScript, qui doit remarquer que la boucle est isole des variables dans la porte.
Figure C.2
Rfrences circulaires sur des objets.

Rfrences circulaires accidentelles Les fermetures peuvent conduire la cration de rfrences circulaires. Puisque les fonctions sont des objets qui doivent tre conservs en mmoire, toutes les variables qui se trouvent dans leur environnement de fermeture sont galement conserves :
function outerFn() { var outerVar = {}; function innerFn() { alert(outerVar); }

Annexe C

Fermetures en JavaScript

383

outerVar.fn = innerFn; return innerFn; };

Dans cet exemple, un objet nomm outerVar est cr et rfrenc depuis le code de la fonction interne innerFn(). Ensuite, une proprit de outerVar qui pointe sur innerFn() est cre, et innerFn() est retourne. Cela cre une fermeture sur innerFn(), qui fait rfrence outerVar, qui fait son tour rfrence innerFn(). Cependant, la boucle peut tre encore plus insidieuse :
function outerFn() { var outerVar = {}; function innerFn() { alert('Salut'); } outerVar.fn = innerFn; return innerFn; };

Dans cette nouvelle version, nous avons modifi innerFn() pour quelle ne fasse plus rfrence outerVar. Toutefois, la boucle est toujours l ! Mme si outerVar nest pas rfrence partir de innerFn(), elle se trouve toujours dans lenvironnement de fermeture de innerFn(). Toutes les variables dans la porte de outerFn() sont implicitement rfrences par innerFn() du fait de la fermeture. Par consquent, les fermetures peuvent trs facilement conduire des rfrences circulaires. Fuites de mmoire dans Internet Explorer En gnral, toutes ces considrations ne constituent pas un problme, car JavaScript est en mesure de dtecter ces boucles et de les nettoyer lorsquelles deviennent orphelines. En revanche, Internet Explorer a quelques difficults grer une classe particulire de rfrences circulaires. Lorsquune boucle comprend des lments du DOM et des objets JavaScript normaux, IE est incapable de librer les uns ou les autres car ils sont pris en charge par des gestionnaires de mmoire diffrents. Ces boucles sont libres uniquement la fermeture du navigateur, ce qui peut mener une consommation excessive de la mmoire au fil du temps. Le simple gestionnaire dvnements suivant cre une telle boucle :
$(document).ready(function() { var div = document.getElementById('foo'); div.onclick = function() { alert('Salut'); }; });

Laffectation du gestionnaire de click cre une fermeture avec div dans un environnement de fermeture. Mais div possde une rfrence de retour vers la fermeture, au travers de la proprit onclick. Ainsi, la boucle ne peut pas tre libre par Internet Explorer, mme lorsque linternaute passe une autre page.

384

jQuery

Les bonnes nouvelles crivons prsent le mme code, mais avec des constructions jQuery normales :
$(document).ready(function() { var $div = $('#foo'); $div.click(function() { alert('Salut'); }); });

Mme si une fermeture est toujours cre, avec le mme type de boucle que prcdemment, ce code ne conduit pas une fuite de mmoire dans IE. En effet, jQuery est au courant des potentialits de fuites et libre manuellement tous les gestionnaires dvnements quil affecte. Tant que nous employons les mthodes jQuery de liaison dvnements pour nos gestionnaires, nous navons pas craindre les fuites provoques par cet idiome particulirement rpandu. Pourtant, cela ne signifie pas que nous soyons sauvs. Nous devons toujours faire attention lors de la manipulation des lments du DOM. La liaison dobjets JavaScript des lments du DOM peut toujours provoquer des fuites de mmoire dans Internet Explorer. jQuery fait simplement en sorte que ce cas soit moins frquent. Voil pourquoi jQuery met notre disposition un autre outil pour viter ces fuites. Au Chapitre 7, nous avons vu que la mthode .data() permet dassocier des informations aux lments du DOM, la manire des proprits expando. Puisque ces donnes ne sont pas enregistres directement dans une proprit expando (jQuery utilise une mappe interne pour enregistrer les donnes avec des identifiants quil cre), une rfrence circulaire nest jamais forme et nous vitons le problme des fuites de mmoire. Ds quune proprit expando semble constituer un mcanisme de stockage pratique pour des donnes, il faut envisager lutilisation de .data() pour plus de scurit.

C.5

En rsum

Les fermetures JavaScript sont une caractristique puissante du langage. Elles sont gnralement plutt utiles pour masquer des variables un autre code, ce qui vite les conflits sur les noms de variables. Puisque des fonctions sont souvent passes en argument des mthodes dans jQuery, la cration de fermetures par inadvertance est frquente. En les matrisant, nous pouvons crire du code plus efficace et plus concis, et, avec un peu de soin et grce laide des protections intgres jQuery, nous pouvons viter les problmes de fuites de mmoire auxquels elles peuvent mener.

Annexe D
Rfrence rapide
Cette annexe se veut une rfrence rapide lAPI de jQuery, notamment pour les expressions de slection et les mthodes. Ce sujet est abord de manire plus dtaille dans le livre jQuery Reference Guide et sur le site de la documentation de jQuery http://docs.jquery.com.

D.1

Expressions de slection

La fonction $() de jQuery sert rechercher des lments sur la page, pour les manipuler ensuite. Elle prend en argument une expression de slection, sous la forme dune chane de caractres dont la syntaxe sapparente celle de CSS. Les expressions de slection ont t dtailles au Chapitre 2 et sont recenses au Tableau D.1.
Tableau D.1 : Les expressions de slection

Slecteur
* #id lment .classe a, b a b a > b a + b a ~ b :first

Correspondance Tous les lments. Llment dont lidentifiant est celui indiqu. Tous les lments du type indiqu. Tous les lments de la classe indique. Les lments qui correspondent a ou b. Les lments b qui sont des descendants de a. Les lments b qui sont des enfants de a. Les lments b qui viennent juste aprs a. Les lments b qui sont des frres de a. Le premier lment dans le jeu rsultant.

386

jQuery

Tableau D.1 : Les expressions de slection (suite)

Slecteur
:last :not(a) :even :odd :eq(indice) :gt(indice) :lt(indice) :header :animated :contains(texte) :empty :has(a) :parent :hidden :visible [attr] [attr=valeur] [attr!=valeur] [attr^=valeur] [attr$=valeur] [attr*=valeur] :nth-child(indice) :nth-child(even) :nth-child(odd)

Correspondance Le dernier lment dans le jeu rsultant. Tous les lments du jeu rsultant qui ne correspondent pas a. Les lments dindice pair dans le jeu rsultant (commence 0). Les lments dindice impair dans le jeu rsultant (commence 0). Llment dindice indiqu dans le jeu rsultant (commence 0). Tous les lments du jeu rsultant qui viennent aprs (suprieurs ) lindice indiqu (commence 0). Tous les lments du jeu rsultant qui viennent avant (infrieurs ) lindice indiqu (commence 0). Les lments dintitul, comme <h1> ou <h2>. Les lments en cours danimation. Les lments qui contiennent le texte indiqu. Les lments sans nuds enfants. Les lments dont un lment descendant correspond a. Les lments qui ont des nuds enfants. Les lments masqus, que ce soit par CSS ou ceux de type <input
type="hidden" />.

Linverse de :hidden. Les lments qui possdent lattribut attr. Les lments dont lattribut attr est gal valeur. Les lments dont lattribut attr est diffrent de valeur. Les lments dont lattribut attr commence par valeur. Les lments dont lattribut attr se termine par valeur. Les lments dont lattribut attr contient la sous-chane valeur. Les lments qui sont lenfant dindice indiqu de leur lment parent (commence 1). Les lments qui sont un enfant dindice pair de leur lment parent (commence 1). Les lments qui sont un enfant dindice impair de leur lment parent (commence 1).

Annexe D

Rfrence rapide

387

Tableau D.1 : Les expressions de slection (suite)

Slecteur

Correspondance

:nth-child(formule) Les lments qui sont un enfant dindice n de leur parent (commence 1). La formule est de la forme an+b pour les entiers a et b. :first-child :last-child :only-child :input :text :password :radio :checkbox :submit :image :reset :button :file :enabled :disabled :checked :selected

Les lments qui sont le premier enfant de leur parent. Les lments qui sont le dernier enfant de leur parent. Les lments qui sont le seul enfant de leur parent. Tous les lments <input>, <select>, <textarea> et <button>. Les lments <input> de type text. Les lments <input> de type password. Les lments <input> de type radio. Les lments <input> de type checkbox. Les lments <input> de type submit. Les lments <input> de type image. Les lments <input> de type reset. Les lments <input> de type button et les lments <button>. Les lments <input> de type file. Les lments de formulaire activs. Les lments de formulaire dsactivs. Les cases cocher et les boutons radios slectionns. Les lments <option> slectionns.

D.2

Mthodes de parcours du DOM

Aprs avoir cr un objet jQuery avec $(), nous pouvons modifier la collection des lments correspondants en invoquant les mthodes de parcours du DOM. Elles ont t prsentes en dtail au Chapitre 2 et sont recenses au Tableau D.2.
Tableau D.2 : Les mthodes de parcours du DOM

Mthode
.filter(slecteur) .filter(rappel)

Retourne un objet jQuery qui contient Les lments slectionns qui correspondent au slecteur indiqu. Les lments slectionns pour lesquels la fonction rappel retourne true.

388

jQuery

Tableau D.2 : Les mthodes de parcours du DOM (suite)

Mthode
.eq(indice) .slice(dbut, [fin]) .not(slecteur) .add(slecteur) .find(slecteur) .contents()

Retourne un objet jQuery qui contient Llment slectionn qui se trouve lindice indiqu (commence 0). Les lments slectionns qui se trouvent dans la plage dindices indique (commence 0). Les lments slectionns qui ne correspondent pas au slecteur indiqu. Les lments slectionns et tout lment supplmentaire qui correspond au slecteur indiqu. Les lments descendants qui correspondent au slecteur. Les nuds enfants (y compris les nuds de texte).

.children([slecteur]) Les nuds enfants, avec filtrage facultatif par le slecteur indiqu. .next([slecteur]) .nextAll([slecteur]) .prev([slecteur]) .prevAll([slecteur])

Le frre qui vient immdiatement aprs chaque lment slectionn, avec filtrage facultatif par le slecteur indiqu. Tous les frres qui suivent chaque lment slectionn, avec filtrage facultatif par le slecteur indiqu. Le frre qui vient immdiatement avant chaque lment slectionn, avec filtrage facultatif par le slecteur indiqu. Tous les frres qui prcdent chaque lment slectionn, avec filtrage facultatif par le slecteur indiqu.

.siblings([slecteur]) Tous les frres, avec filtrage facultatif par le slecteur indiqu. .parent([slecteur]) .parents([slecteur]) .closest(slecteur) .offsetParent() .andSelf() .end() .map(rappel)

Le parent de chaque lment slectionn, avec filtrage facultatif par le slecteur indiqu. Tous les anctres, avec filtrage facultatif par le slecteur indiqu. Le premier lment qui correspond au slecteur, en partant de llment slectionn et en remontant larborescence du DOM. Le parent positionn (par exemple de manire relative ou absolue) du premier lment slectionn. Les lments slectionns, ainsi que le jeu dlments slectionns prcdent plac sur la pile interne de jQuery. Le jeu prcdent dlments slectionns dans la pile interne de jQuery. Le rsultat de la fonction rappel lorsquelle est invoque sur chaque lment slectionn.

Annexe D

Rfrence rapide

389

D.3

Mthodes dvnement

Pour ragir aux actions de lutilisateur, nous devons enregistrer nos gestionnaires en utilisant les mthodes dvnement. Notez que de nombreux vnements du DOM sappliquent uniquement certains types dlments ; ces subtilits ne sont pas dtailles dans cette section. Les mthodes dvnement ont t dtailles au Chapitre 3 et sont recenses au Tableau D.3.
Tableau D.3 : Les mthodes dvnement

Mthode
.ready(gestionnaire) .bind(type, [donnes], gestionnaire) .one(type, [donnes], gestionnaire)

Description
gestionnaire sera invoqu lorsque le DOM et CSS seront intgralement chargs. gestionnaire sera invoqu lorsque lvnement type sera envoy llment. gestionnaire sera invoqu lorsque lvnement type sera

envoy llment. La liaison est supprime aprs lappel de gestionnaire.

.unbind([type], [gestion- Supprime les liaisons sur llment (pour lvnement type, le naire]) gestionnaire particulier ou toutes les liaisons). .live(type, gestionnaire) gestionnaire sera invoqu lorsque lvnement type sera

envoy llment, avec utilisation de la dlgation dvnement.


.die(type, [gestionnaire]) .blur(gestionnaire) .change(gestionnaire) .click(gestionnaire) .dblclick(gestionnaire) .error(gestionnaire) .focus(gestionnaire) .keydown(gestionnaire)

Supprime les liaisons sur llment prcdemment li avec .live().


gestionnaire sera invoqu lorsque llment perdra le focus du clavier. gestionnaire sera invoqu lorsque llment perdra le focus du clavier et que sa valeur aura t change. gestionnaire sera invoqu lors dun clic sur llment. gestionnaire sera invoqu lors dun double-clic sur llment. gestionnaire sera invoqu lorsque llment recevra un vnement derreur (dpend du navigateur). gestionnaire sera invoqu lorsque llment recevra le focus du clavier. gestionnaire sera invoqu lorsquune touche sera enfonce alors que llment possde le focus du clavier.

390

jQuery

Tableau D.3 : Les mthodes dvnement (suite)

Mthode
.keypress(gestionnaire) .keyup(gestionnaire) .load(gestionnaire) .mousedown(gestionnaire)

Description
gestionnaire sera invoqu lorsquune touche sera appuye alors que llment possde le focus du clavier. gestionnaire sera invoqu lorsquune touche sera relche alors que llment possde le focus du clavier. gestionnaire sera invoqu lorsque le chargement de llment sera termin. gestionnaire sera invoqu lorsque le bouton de la souris sera enfonc au-dessus de llment.

.mouseenter(gestionnaire) gestionnaire sera invoqu lorsque le pointeur de la souris

entrera dans llment. Le bouillonnement na pas deffet.


.mouseleave(gestionnaire) gestionnaire sera invoqu lorsque le pointeur de la souris

quittera llment. Le bouillonnement na pas deffet.


.mousemove(gestionnaire) .mouseout(gestionnaire) .mouseover(gestionnaire) .mouseup(gestionnaire) .resize(gestionnaire) .scroll(gestionnaire) .select(gestionnaire) .submit(gestionnaire) .unload(gestionnaire) .hover(entre, sortie) .toggle(gestionnaire1, gestionnaire2,...) gestionnaire sera invoqu lorsque le pointeur de la souris se dplacera au-dessus de llment. gestionnaire sera invoqu lorsque le pointeur de la souris quittera llment. gestionnaire sera invoqu lorsque le pointeur de la souris entrera dans llment. gestionnaire sera invoqu lorsque le bouton de la souris sera relch au-dessus de llment. gestionnaire sera invoqu lorsque llment sera redimensionn. gestionnaire sera invoqu lorsque la position de dfilement de llment changera. gestionnaire sera invoqu lorsque le texte dans llment sera slectionn. gestionnaire sera invoqu lorsque llment (un formulaire) sera envoy. gestionnaire sera invoqu lorsque llment sera retir de la mmoire. entre sera invoqu lorsque la souris entrera dans llment et sortie lorsque la souris en sortira. gestionnaire1 sera invoqu lors du premier clic de souris sur llment, gestionnaire2 sera invoqu lors du deuxime clic, et ainsi de suite pour les clics suivants.

Annexe D

Rfrence rapide

391

Tableau D.3 : Les mthodes dvnement (suite)

Mthode

Description par dfaut pour lvnement.

.trigger(type, [donnes]) Invoque lvnement type sur les lments et excute laction .triggerHandler(type, [donnes]) .blur() .change() .click() .dblclick() .error() .focus() .keydown() .keypress() .keyup() .select() .submit()

Invoque lvnement type sur le premier lment, sans excuter les actions par dfaut ni les vnements par dlgation. Dclenche lvnement blur. Dclenche lvnement change. Dclenche lvnement click. Dclenche lvnement dblclick. Dclenche lvnement error. Dclenche lvnement focus. Dclenche lvnement keydown. Dclenche lvnement keypress. Dclenche lvnement keyup. Dclenche lvnement select. Dclenche lvnement submit.

D.4

Mthodes deffet

Les mthodes deffet sont utilises pour raliser des animations sur les lments du DOM. Elles ont t dtailles au Chapitre 4 et sont recenses au Tableau D.4.
Tableau D.4 : Les mthodes deffet

Mthode
.show() .hide() .show(vitesse, [rappel]) .hide(vitesse, [rappel])

Description Affiche les lments slectionns. Masque les lments slectionns. Affiche les lments slectionns, avec animation de la hauteur, de la largeur et de lopacit. Masque les lments slectionns, avec animation de la hauteur, de la largeur et de lopacit.

.toggle([vitesse], [rappel]) Affiche ou masque les lments slectionns. .slideDown([vitesse], [rappel])

Affiche les lments slectionns, avec un effet de glissement.

392

jQuery

Tableau D.4 : Les mthodes deffet (suite)

Mthode
.slideUp([vitesse], [rappel]) .slideToggle([vitesse], [rappel])

Description Masque les lments slectionns, avec un effet de glissement. Affiche ou masque les lments slectionns, avec un effet de glissement. vers une opacit totale.

.fadeIn([vitesse], [rappel]) Affiche les lments slectionns, avec un effet de fondu .fadeOut([vitesse], [rappel]) .fadeTo(vitesse, opacit, [rappel]) .animate(attributs, [vitesse], [easing], [rappel])

Masque les lments slectionns, avec un effet de fondu vers une transparence totale. Ajuste lopacit des lments slectionns. Effectue une animation personnalise des attributs CSS indiqus.

.animate(attributs, options) Interface simplifie de la mthode .animate(), pour le

contrle de la file danimation.


.stop([viderFile], [allerAFin]) .queue() .queue(rappel) .queue(nouvelleFile) .dequeue()

Arrte lanimation en cours, puis dmarre les ventuelles animations places dans la file. Rcupre la file danimation associe au premier lment correspondant. Ajoute la fonction rappel la fin de la file. Remplace la file par une nouvelle. Excute lanimation suivante de la file.

D.5

Mthodes de manipulation du DOM

Les mthodes de manipulation du DOM ont t dtailles au Chapitre 5 et sont recenses au Tableau D.5.
Tableau D.5 : Les mthodes de manipulation du DOM

Mthode
.attr(cl) .attr(cl, valeur) .attr(cl, fn)

Description Obtient la valeur de lattribut cl. Affecte valeur lattribut cl. Affecte lattribut cl le rsultat de la fonction fn (invoque sur chaque lment slectionn).

Annexe D

Rfrence rapide

393

Tableau D.5 : Les mthodes de manipulation du DOM (suite)

Mthode
.attr(mappe) .removeAttr(cl) .addClass(classe) .removeClass(classe) .toggleClass(classe) .hasClass(classe) .html() .html(valeur) .text() .text(valeur) .val() .val(valeur) .css(cl) .css(cl, valeur) .css(mappe) .offset() .position()

Description Fixe les valeurs des attributs, donns sous forme de couples cl-valeur. Supprime lattribut cl. Ajoute la classe indique chaque lment slectionn. Retire la classe indique chaque lment slectionn. Retire la classe indique si elle est prsente, sinon lajoute, pour chaque lment slectionn. Retourne true si lun des lments slectionns possde la classe indique. Rcupre le contenu HTML du premier lment slectionn. Fixe le contenu HTML de chaque lment slectionn valeur. Rcupre le contenu textuel de tous les lments slectionns dans une seule chane. Fixe le contenu textuel de chaque lment slectionn valeur. Rcupre lattribut value du premier lment slectionn. Fixe lattribut value de chaque lment valeur. Rcupre lattribut CSS cl. Fixe lattribut CSS cl valeur. Fixe les valeurs des attributs CSS donns sous forme de couple cl-valeur. Rcupre les coordonnes haute et gauche, en pixels, du premier lment slectionn, relativement la page. Rcupre les coordonnes haute et gauche, en pixels, du premier lment slectionn, relativement llment retourn par .offsetParent(). Rcupre la position de dfilement vertical du premier lment slectionn. Fixe valeur la position de dfilement vertical de tous les lments slectionns. Rcupre la position de dfilement horizontal du premier lment slectionn.

.scrollTop() .scrollTop(valeur) .scrollLeft()

394

jQuery

Tableau D.5 : Les mthodes de manipulation du DOM (suite)

Mthode
.scrollLeft(valeur) .height() .height(valeur) .width() .width(valeur) .innerHeight() .innerWidth()

Description Fixe valeur la position de dfilement horizontal de tous les lments slectionns. Rcupre la hauteur du premier lment slectionn. Fixe valeur la hauteur de tous les lments slectionns. Rcupre la largeur du premier lment slectionn. Fixe valeur la largeur de tous les lments slectionns. Rcupre la hauteur du premier lment slectionn, en incluant lespacement, mais sans les bordures. Rcupre la largeur du premier lment slectionn, en incluant lespacement, mais sans les bordures. incluant lespacement, les bordures et, facultativement, les marges.

.outerHeight([avecMarges]) Rcupre la hauteur du premier lment slectionn, en

.outerWidth([avecMarges])

Rcupre la largeur du premier lment slectionn, en incluant lespacement, les bordures et, facultativement, les marges. Insre du contenu la fin de chaque lment slectionn. Insre les lments slectionns la fin des lments qui correspondent au slecteur. Insre du contenu au dbut de chaque lment slectionn. Insre les lments slectionns au dbut des lments qui correspondent au slecteur. Insre du contenu aprs chaque lment slectionn. Insre les lments slectionns aprs chaque lment qui correspond au slecteur. Insre du contenu avant chaque lment slectionn. Insre les lments slectionns avant chaque lment qui correspond au slecteur. Enveloppe chaque lment slectionn dans contenu. Enveloppe tous les lments slectionns dans contenu. Enveloppe le contenu de chaque lment slectionn dans contenu.

.append(contenu) .appendTo(slecteur) .prepend(contenu) .prependTo(slecteur) .after(contenu) .insertAfter(slecteur) .before(contenu) .insertBefore(slecteur) .wrap(contenu) .wrapAll(contenu) .wrapInner(contenu)

Annexe D

Rfrence rapide

395

Tableau D.5 : Les mthodes de manipulation du DOM (suite)

Mthode
.replaceWith(contenu) .replaceAll(slecteur) .empty() .remove([slecteur]) .clone([true|false]) .data(cl) .data(cl, valeur) .removeData(cl)

Description Remplace les lments slectionns par contenu. Remplace les lments qui correspondent au slecteur par les lments slectionns. Retire les nuds enfants de chaque lment slectionn. Retire du DOM les nuds slectionns, avec filtrage facultatif par le slecteur. Effectue une copie de tous les lments slectionns, en incluant facultativement les gestionnaires dvnements. Obtient llment de donnes cl associ au premier lment slectionn. Affecte valeur llment de donnes cl associ chaque lment slectionn. Supprime llment de donnes cl associ chaque lment slectionn.

D.6

Mthodes AJAX

Grce aux mthodes AJAX, nous pouvons obtenir des informations partir du serveur sans actualiser la page. Ces mthodes ont t dtailles au Chapitre 6 et sont recenses au Tableau D.6.
Tableau D.6 : Les mthodes AJAX

Mthode
$.ajax(options)

Description Effectue une requte AJAX en utilisant les options fournies. Cette mthode de bas niveau est gnralement appele par dautres mthodes plus commodes.

.load(url, [donnes], [rap- Effectue une requte AJAX sur url et place la rponse dans pel]) les lments slectionns. $.get(url, [donnes], [rap- Effectue une requte AJAX sur url en utilisant la mthode pel], [typeRetour]) GET. $.getJSON(url, [donnes], [rappel]) $.getScript(url, [rappel])

Effectue une requte AJAX sur url, en traitant la rponse comme une structure de donnes JSON. Effectue une requte AJAX sur url, en valuant la rponse comme du code JavaScript.

396

jQuery

Tableau D.6 : Les mthodes AJAX (suite)

Mthode
$.post(url, [donnes], [rappel], [typeRetour])

Description Effectue une requte AJAX sur url en utilisant la mthode POST. termine.

.ajaxComplete(gestionnaire) gestionnaire sera invoqu lorsquune requte AJAX sera .ajaxError(gestionnaire) .ajaxSend(gestionnaire) .ajaxStart(gestionnaire) .ajaxStop(gestionnaire) .ajaxSuccess(gestionnaire) $.ajaxSetup(options) .serialize() .serializeArray() $.param(mappe) gestionnaire sera invoqu lorsquune requte AJAX se

terminera par une erreur.


gestionnaire sera invoqu lorsquune transaction AJAX dbutera. gestionnaire sera invoqu lorsquune transaction AJAX

dbutera alors quaucune autre nest active.


gestionnaire sera invoqu lorsquune transaction AJAX se terminera alors quaucune autre nest active. gestionnaire sera invoqu lorsquune transaction AJAX se

terminera avec succs. Fixe les options par dfaut de toutes les transactions AJAX ultrieures. Encode les valeurs dun ensemble de contrles de formulaire dans une chane de paramtres. Encode les valeurs dun ensemble de contrles de formulaire dans une structure de donnes JSON. Encode une mappe de valeurs dans une chane de paramtres.

D.7

Autres mthodes

Les mthodes utilitaires recenses au Tableau D.7 nentrent pas dans les catgories prcdentes, mais elles sont souvent trs utiles dans les scripts jQuery.
Tableau D.7 : Autres mthodes utilitaires

Mthode ou proprit
$.support $.each(collection, rappel)

Description Retourne une mappe de proprits qui indiquent les fonctionnalits et les standards pris en charge par le navigateur. Itre sur une collection, en excutant la fonction de rappel sur chaque lment.

Annexe D

Rfrence rapide

397

Tableau D.7 : Autres mthodes utilitaires (suite)

Mthode ou proprit
$.extend(cible, ajout,...) $.grep(tableau, rappel, [inverser]) $.makeArray(objet) $.map(tableau, rappel) $.inArray(valeur, tableau)

Description Modifie lobjet cible en lui ajoutant les proprits provenant des autres objets indiqus. Filtre le tableau en utilisant la fonction de rappel pour les comparaisons. Convertit un objet en un tableau. Construit un nouveau tableau compos des rsultats retourns par la fonction de rappel invoque sur chaque lment. Dtermine si la valeur se trouve dans le tableau.

$.merge(tableau1, tableau2) Fusionne le contenu de deux tableaux. $.unique(tableau) $.isFunction(objet) $.trim(chane) $.noConflict([extreme]) .hasClass(nomClasse) .is(slecteur) .each(rappel) .length .get() .get(indice) .index(lment)

Retire du tableau tous les lments du DOM redondants. Dtermine si lobjet est une fonction. Supprime les espaces la fin de la chane. Redonne $ la dfinition quil avait avant le chargement de jQuery. Dtermine si un lment slectionn possde la classe indique. Dtermine si un lment slectionn correspond lexpression de slection indique. Itre sur les lments slectionns, en excutant la fonction de rappel sur chacun deux. Obtient le nombre dlments slectionns. Obtient un tableau des nuds du DOM qui correspondent aux lments slectionns. Obtient le nud du DOM qui correspond llment slectionn dindice indiqu. Obtient lindice du nud du DOM indiqu au sein du jeu des lments slectionns.

Index
Symboles $(), fonction propos 14-15, 86-87 constituants de base classe 14 itration explicite, viter 15 fonctionnalit 86 lien de retour au dbut 86 mthodes dinsertion, utiliser 87 $(document).ready(), gestionnaire dvnements 30 A .addClass(), mthode 63 AHAH, propos 111 AJAX propos 109 compatibilit avec 109 dlgation dvnement 136 donnes, charger 110-124 fichiers XML 109 fonctions dobservation 132-136 fragment HTML, charger 141-143 gestionnaires de click, ajouter 136 lier nouveau 136 JavaScript 109 mthodes .ajaxStart() 133, 134 .ajaxStop() 133 de bas niveau 139-140 liste 395-396 .load() 143 options par dfaut, modifier 140-143 supplmentaires 139-143 scurit 137-139 XMLHttpRequest, objet 109 .ajaxForm(), mthode 305 .ajaxStart(), mthode 133, 134 .ajaxSubmit(), mthode 306 .animate(), mthode 71 Animations personnalises, crer mthodes .animate() 71 .fadeIn() 72 .fadeToggle() 72 proprits modifier 72-75 positionnement CSS 74-75 Anonymes, fonctions 11 Attributs autres que de classe, modifier 84-86 de classe, manipuler 83-87 .each(), mthode 85 Auto-compltion (dans les formulaires compacts) propos 226-234 clavier, naviguer au 229-231 code ct serveur 226-227 contre recherche dynamique 233-234 dans le navigateur 227-228 liste de suggestions, masquer 233 mcanisme intgr au navigateur 228 objectif 226 texte dun champ de recherche, fixer 228229

400

Index

Auto-compltion (dans les formulaires compacts) (suite) touches de direction 231-232 Entre 232 B .bind(load), syntaxe 296 Blogs A List Apart 365 Ajaxian 363 conception web par Dustin Diaz 364 dveloppements Internet par Robert Nyman 363 JavaScript ant 363 John Resig 363 jQuery 363 didacticiels 363 programmation/dveloppement web 364 ressources JavaScript de Matt Snider 364 scripts pour le DOM 364 sites de Christian Heilmann 364 utilisations labores du DOM 364 Bouillonnement dvnement 290 C Carrousel dimages animation de glissement, ajouter 282-284 anomalies dues aux actions de lutilisateur, viter 282 code final 297-300 cycle 280, 281 icnes daction, afficher 284-287 images, agrandir 287-297 propos 287 animation 293-294 animations, reporter 294-296 badge 292 badge, sur la couverture agrandie 290 bouton Fermer 290-291 couverture agrandie, fermer 289-290 lment singleton, crer 292 indicateur de chargement, ajouter 297

images, changer 280-287 implmenter 276-300 page web, prparer 277-280 rvision du style en Javascript 279-280 Chargement de la page, tches $(document).ready(), utiliser 30 code concision 32 planifier lexcution 29-30 collisions, empcher 32-33 effectuer 29-33 .noConflict(), mthode 32 scripts multiples 30-32 .clone(), mthode 98 Code, concision $(), fonction 32 .ready(), fonction 32 Communication client-serveur bote outils AJAX de jQuery 130 formulaire, construire 131 requtes GET 126-130 POST 130 CSS propos 13 modification en ligne .addClass(), mthode 63 .css(), mthode 61-65 lments button 62, 64, 65 objets littraux 62 rfrence fiche de Mezzoblue 362 page daccueil du W3C 362 position is everything 362 slecteurs propos 15 combinateur denfant 17 lments de liste, styler 17-18 pseudo-classe de ngation 18 utiliser 16, 17 .css(), mthode 61-65

Index

401

D .data(), mthode 155 .dequeue(), mthode 78 .dialog(), mthode 314 Document XML, charger 120-124 DOM propos 2, 13 lments 335 accder 27 bouillonnement dvnement 46 bouillonnement dvnement, inconvnients 46-47 capture dvnement 45 hirarchie 45 exemple 13 mthodes de manipulation liste 392-395 utiliser 105-106 mthodes de parcours 337-340 propos 24 cellule de catgorie, styler 25-26 chanage 26-27 chanage, avantage 26 effet bnfique 340 effet secondaire 339 lments du DOM, accder 27 fonction de filtrage 24 liste 387-388 pile interne jQuery 340 Donnes tabulaires propos 145 paginer 146-173 trier 146-173 Donnes, AJAX amlioration progressive 110 extraire de document XML 125 fichiers JavaScript 125 fichiers JSON 125 fragments HTML 125 HTML, ajouter 111-114 liens, associer des gestionnaires dvnements 110

objets JavaScript manipuler 114-120 obtenir 114-116 E .each(), mthode 85 .effect(), mthode 309-310 Effets composs 69-70 en file dattente 75 fonction de rappel 79-81 fondu en dgrad sur le prompteur de nouvelles 272-274 jeu dlments multiple, manipuler 78-79, 82 unique, manipuler 75-78, 82 mthodes 42 .dequeue(), utiliser 78 liste 391-392 non deffet, ajouter 77 multiples, appliquer 75-78 simultans, crer 75-82 lments citations de passages 100 styler 104-105 contexte lier 93-95 marquer 93-95 numroter 93-95 copier 98-105 dplacer 89-96 insrer 87-89 jQuery, premiers pas 101 mthodes .clone() 98, 100 .wrap() 97-98 notes de bas de page ajouter 95-96 marqueurs 95 opacit 285 rgles CSS, appliquer 92 de style, appliquer 100

402

Index

vnements bouillonnement 290 inconvnients 46-47 composs 42-44 lments du DOM, hirarchie 45 vnement click 43 fonctions avances, afficher 42-43 fonctions avances, masquer 42-43 .hover(), mthode 42, 44 .toggle(), mthode 42 du clavier (dans le slecteur de style) ajouter 56-59 focus clavier 57 .keyCode, proprit 57 keydown 57 keyup 57 keydown 57 keyup 57 lis un espace de noms 5 mthodes abrges 41 liste 389-391 plugins pour 328 slecteur de style 33-41 traiter 33-41 event, objet accder 48 actions par dfaut 49-50 dlgation dvnement 50-52 exemple 50, 52 mthodes .is() 51 .preventDefault() 50 .stopPropagation() 49 .target, proprit 48 utiliser 48 Expressions de slection crer 353-354 liste 385-387 paramtres element 353 index 353 matches 354 set 354 pseudo-classe, ajouter 353 utiliser 353, 354

F .fadeIn(), mthode 72 .fadeIn(slow), mthode 68 .fadeOut, mthode 69 .fadeToggle(), mthode 72 Fermetures 11, 373 arguments de $(document).ready() 379 dans jQuery 378-381 fonctions anonymes dans jQuery 378 exemple 380 gestionnaires dvnements, affecter 379381 interagir avec 377-378 mappe utilise 378 Firefox, outils pour barre doutils du dveloppeur 368 Firebug 367 caractristiques 367 testeur dexpression rgulire 368 Venkman 368 Fonctions $(), propos 14-15, 86-87 anonymes 11 globales ajouter lespace de noms de jQuery 330 avantage 331 exemple 330 mthodes utilitaires, crer 332 multiples, ajouter 330-331 internes propos 373-377 affecter une variable globale 374 avantages 373 environnement dexcution JavaScript 376 porte des variables 376-377 ramasse-miettes 376 utilisation 374 valeur de retour 375 lambda 11 .ready() 32

Index

403

Form, plugin propos 305-307 mthodes .ajaxForm() 305, 306 .ajaxSubmit() 306 options beforeSubmit 305 sucess 306 target 305 Formulaires amlioration progressive 200-207 compacts propos 222-236 auto-compltion 226-234 champ de recherche, code 234-236 libell dun champ 223 libell, masquer 224 libell, styler 223 solutions aux problmes 224, 225 donnes numriques boucle .each() 241 bouton Retirer, ajouter 247-252 boutons, ajouter 247 boutons, modifier 248 calculs numriques 240-247 chiffres aprs la virgule, grer 243-244 frais de livraison, calculer 246 informations de livraison, modifier 252-255 lignes, supprimer 250 masque de saisie 239 monnaie, mettre en forme 241-242 monnaie, parser 241-242 panier dachat, code 255-257 panier dachat, structure de la table 236-239 sous-total, calculer 245 taxes, arrondir 245-246 validation de la saisie 239 messages dun champ expression rgulire 205-206 lgende, insrer 206-207 modifier 203, 205 styles, dfinir 207 plugins pour 317-318 slecteurs personnaliss 23-24

style 200-207 propos 200 case cocher, manipuler 217-219 case Cocher tout, crer 217-219 formulaire de contact 220-222 groupe, amliorer 200 informations personnelles, embellir 207-209 lgende, appliquer 202-203 messages dun champ, modifier 203207 validation ajouter 209-217 champs 210-213 ct client, avantages 210 expression rgulire 214 formats dentre, implmenter 213215 rgles 209 tches effectues par le code 214 vrifier 215-217 Fuites de mmoire, dangers des problme dInternet Explorer 383 rfrences circulaires accidentelles 382383 G Gestionnaire dvnements 10 espace de noms dun vnement, utiliser 53 lier nouveau 53-55 retirer 52-55 pass en argument 31 GNU Public License 4 Graphiques, plugins pour 325-326 H .hide(), mthode 66 attributs de style en ligne, fixer 66 lments, restaurer 66 exemple 66, 67 fonctionnalits 66 inconvnients 68

404

Index

.hide(vitesse), mthode 68 .hover(), mthode 42, 44 I Images, plugins pour 322-323 Info-bulles 181-186 .insertAfter(), mthode 87 .insertBefore(), mthode 87 Internet Explorer, outils pour DebugBar 369 Developer Toolbar 368 Drip 369 Microsoft Visual Web Developer 369 .is(), mthode 51 J JavaScript compresseurs de code embellisseur de code 361 JSMin 361 YUI Compressor 361 objet $.getJSON(), dfinir 116 $.getJSON(), en tant que fonction globale 116 $.getJSON(), en tant que mthode de classe 116 jQuery global 116 manipuler 114-120 retrouver 114-116 retrouver, avec $.getJSON() 116 script, excuter 118-120 pagination ajouter 165-171 boutons de pagination, activer 167-168 donnes dvnement personnalises, ajouter 168 numro de page actuelle, mettre en exergue 169-170 oprations de tri, avec la slection de page 170-171 slecteur de page, afficher 166-167

rfrences bote outils JavaScript 361 Centre de dveloppement Mozilla 360 Dev.opera 360 Quirksmode 360 rfrence JScript de MSDN 360 tri alphabtique de base 149-153 appliquer 157 balises de regroupement des lignes 149 colonne, mettre en exergue 160-161 croissant, autoriser 161 .data(), mthode 155 dcroissant, autoriser 161 donnes 157-159 exemple 147 expando 155 inverser 161-163 par permutation 154 plugins 154 jQuery $(), fonction 14-15 propos 1 actions 2 ajouter 9-11 architecture de plugins 303 document HTML, prparer 6-8 documentation API de jQuery 359 jQuery graphique 360 navigateur dans lAPI de jQuery 359 visionneuse de lAPI de jQuery avec Adobe AIR 360 wiki jQuery 359 lments, insrer 87-89 fonction de rappel 79-81 fonctionnalits actions multiples, autoriser 4 connaissances en CSS, exploiter 3 couche dabstraction 3 itration implicite 3 plugins 3 schma de chanage 4 frameworks de dveloppement web 365

Index

405

intrts 2-3 AJAX 3 mthodes .animate() 71 dobjet, ajouter 333-337 de manipulation du DOM 105-106 de parcours du DOM 24-27 .insertAfter() 87 .insertBefore() 87 projet jQuery 1.1 5 jQuery 1.1.3 5 jQuery 1.2 5 jQuery 1.2.6 5 jQuery 1.3 5 jQuery UI 5 phase de dveloppement public 4 rfrences expressions de slection 385-387 mthodes AJAX 395-396 mthodes deffet 391-392 mthodes dvnement 389-391 mthodes de manipulation du DOM 392-395 mthodes de parcours du DOM 387388 mthodes diverses 396-397 ressources en ligne 359-365 blogs 362-365 compresseurs de code JavaScript 361 documentation jQuery 359-360 rfrence CSS 362 rfrence JavaScript 360-361 slecteurs CSS 15-18 personnaliss 20-24 tlcharger 5-6 texte dun pome, ajouter 9 utiliser 6 jQuery UI, plugin propos 307-315 bouton Effacer les messages, ajouter 315 composants dinteraction 310-312 tendre 312 .dialog(), options de la mthode 314

Dialog, widget 312-315 effets 308-310 animations sur la classe 309 animations sur la couleur 308 .effect(), mthode 309-310 explode 309 fonctions deasing labor 309 thme, appliquer 314 ThemeRoller 315 jQuery, objet 9 code, excuter 9-11 itration implicite 9-11 mthodes, ajouter chanage 336-337 contexte dobjet, examiner 333-336 nouvelle classe, injecter 9 JSON, propos 115 JSONP, propos 138 K .keyCode, proprit 57 keydown, vnement 57 keyup, vnement 57 L lambda, fonctions 11 Lightbox, plugins pour 323-325 Lignes dune table effet de bandes 174-177 mthodes labores 177-178 mettre en exergue 173-181 action de lutilisateur 178-181 .load(), mthode 143 M Mthodes abrges .animate() 342 avantages 342 dvnements 340-344

406

Index

Mthodes (suite) abrges lments, afficher 342 lments, masquer 342 personnalises 343 .addClass() 63 AJAX de bas niveau propos 139 possibilits 140 .ajaxForm() 305 .ajaxStart() 133, 134 .ajaxSubmit() 306 .animate() 71 .clone() 98 .css() 61-65 .data() 155 .dequeue() 78 .dialog() 314 diverses, liste 396-397 .each() 85 .effect() 309-310 .fadeIn() 72 .fadeToggle() 72 .hover() 42, 44 .insertAfter() 87 .insertBefore() 87 .is() 51 .load() 143 .noConflict() 32 .preventDefault() 50 .stopPropagation() 49 .toggle() 42 .wrap() 97-98 MIT License 4 Modulo (%), oprateur 266 MSDN JScript, propos 360 N .noConflict(), mthode 32 O Opera, outils pour, Dragonfly 370

Outils Aptana 371 Charles 371 Fiddler 371 Firebug Lite 370 NitobiBug 370 pour Firefox 367-368 pour Internet Explorer 368-369 pour Opera 370 pour Safari 369-370 TextMate pour jQuery 371 P Pagination code 171-173 ct serveur 164-165 propos 164 tri et pagination 164-165 JavaScript 165-171 Paramtres de mthode propos 344 fonction de rappel 349-351 employer 349-351 mappes 347-348 ombre sur un bloc 344 par dfaut, personnaliser 351-352 simples 346 valeurs par dfaut 348-349 PHP, langage de script 126 Plugins catalogue 303 dvelopper conventions de nommage 356 expression de slection, ajouter 353354 fonctions globales, ajouter 329-332 interfaces de mthodes 356-357 mthodes abrges, ajouter 340-344 mthodes de parcours du DOM 337340 paramtres de mthodes 344-352 rgles dcriture 355-357 style de documentation 357

Index

407

pour les vnements 328 hoverIntent 328 Live Query 328 pour les formulaires 317-318 Autocomplete 317 Jeditable 318 Masked Input 318 Validation 317 pour les graphiques 325-326 Flot 326 Sparklines 326 pour les images 322-323 Jcrop 322 Magnify 323 pour les tables 319-321 Flexigrid 320 jqGrid 320 Tablesorter 320 pour lightbox 323-325 BlockUI 325 FancyBox 323 jqModal 325 Thickbox 324 rechercher 303-304 utiliser 304-305 Pointeurs 382 .preventDefault(), mthode 50 Prompteur de nouvelles propos 260 code final 274-276 dgradation lgante, scnario 260 effet de fondu en dgrad 272-274 flux, obtenir 261-264 dun domaine diffrent 271-272 gestionnaire de russite 261 indicateur de chargement, ajouter 271272 prparer 264-265 page web 260-261 problme dutilisabilit, rsoudre 268270 rotation des titres, fonction 265-268

R Ramasse-miettes 381 .ready(), fonction 32 Recherche dynamique 233 Rfrences circulaires 382 S Safari, outils pour propos 369-370 inspecteur web 369 menu Dveloppement 369 Scurit balise HTML <iframe>, utiliser 138 donnes distantes charger 137 format JSONP 138-139 Slecteur de style boutons Colonne troite, activer 36 Grande police, activer 34 Par dfaut, activer 36 code, excuter 40 contexte dvnement, exploiter 40 de gestionnaire 36-39 vnements du clavier, ajouter 56-59 remanier 39 Slecteurs dattribut propos 18-20 classes, ajouter 19 liens, styler 19-20 styles, dfinir 19 personnaliss 20-24 propos 20-24 effet de bande 21-23 numrotation partir de un 20 numrotation partir de zro 20 pseudo-classe CSS, syntaxe 20 .show(), mthode 66 exemple 66, 67 fonctionnalits 66 inconvnients 68

408

Index

.show(fast), mthode 68 .show(normal), mthode 68 .show(slow), mthode 68 .show(vitesse), mthode 68 .stopPropagation(), mthode 49 Styles cellule de catgorie 25-26 liens 19-20 lignes alternes 21-23 Systme de comptage des rfrences 381 T Tables aspect, modifier clickable, classe 182 code JavaScript 195-197 filtrer 189-195 info-bulle 181-186 info-bulle, fonction showTooltip() 185 info-bulle, masquer 183 info-bulle, placer 183 info-bulle, positionner 182 info-bulle, style du texte 184 lignes, mettre en exergue 173-181 sections, dvelopper 187-189 sections, rduire 187-189 filtrage dvelopper 194-195 effet de bandes 193-194 implmenter 190-192 options, extraire du contenu 191-192 rduire 194-195 retirer 192 plugins pour 319-321 .toggle(), mthode 42

Tri propos 146 alphabtique en JavaScript appliquer 156-157 comparateur, utiliser 150-153 dgradation lgante, exemple 152 mthode JavaScript .sort(), utiliser 150 plugins, modifications 154 ct serveur 146-147 actualisation de la page, liminer 147 amlioration progressive, exemple 147 chane de requte, utiliser 146 en JavaScript 147-163 et pagination, code 171-173 .trigger(), mthode dgradation lgante, implmenter 56 page dvelopper 55 rduire 55 raccourcis 56 V Venkman, outil pour Firefox 368 W W3C, propos 362 .wrap(), mthode 97-98 X (X)HTML page daccueil du W3C 362 rfrence 361-362 XML Path (XPath) 18 XMLHttpRequest, objet 109

Rfrence

jQuery

Simplifiez et enrichissez vos dveloppements JavaScript

Pour mettre en uvre des sites interactifs attrayants, les dveloppeurs se tournent vers des bibliothques JavaScript, qui leur permettent dautomatiser les tches courantes et de simplifier les plus complexes. jQuery, la plus populaire dentre elles, est particulirement apprcie des dveloppeurs tant pour sa cohrence conceptuelle que ses performances. Dans cet ouvrage, les auteurs partagent leurs connaissances, leur exprience et leur passion pour jQuery afin de vous aider comprendre comment cette bibliothque fonctionne et vous permettre den tirer le meilleur parti. Si vos prcdentes tentatives de dveloppement JavaScript vous ont laiss perplexe, ils vous aideront franchir les obstacles dresss par AJAX, les vnements, les effets et les fonctionnalits avances du langage JavaScript. Ce livre vous apportera tous les outils dont vous avez besoin pour rester lavant-garde du dveloppement web.

TABLE DES MATIRES

Premiers pas Slecteurs vnements Effets Manipulation du DOM AJAX Manipulation des tableaux Manipulation des formulaires Carrousels et prompteurs Utilisation des plugins Dveloppement de plugins Ressources en ligne Outils de dveloppement Fermetures en JavaScript Rfrence rapide

propos des auteurs Jonathan Chaffer, directeur technique dune agence web du Michigan, participe activement au projet Drupal, qui a adopt jQuery comme framework JavaScript. Karl Swedberg, dveloppeur web, est membre de lquipe du projet jQuery et contributeur actif la liste de discussion jQuery.

Programmation Dveloppement web

Niveau : Tous niveaux Configuration : Multiplate-forme

Pearson Education France 47 bis, rue des Vinaigriers 75010 Paris Tl. : 01 72 74 90 00 Fax : 01 42 05 22 17 www.pearson.fr

ISBN : 978-2-7440-4142-6

Vous aimerez peut-être aussi