• 10 heures
  • Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Gérer les événements

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Le JavaScript dispose d'événements. Ceux sont des appels à des fonctions lorsque qu'une action est déclenchée : elle peut être due à l'utilisateur ou au navigateur (c'est ce que vous avez vu dans la première partie).
Mais ces actions et ces appels à des fonctions peuvent aussi être créés artificiellement, pour les besoins de votre programme.

Nous allons voir dans ce chapitre comment les méthodes de jQuery, qui constituent un vrai plus, permettent de créer et de manipuler les événements.

Malgré tout cela, jQuery ne fait que le relais entre le JavaScript et vous : si vous voulez bien gérer les événements comme les mouvements de la souris ou les clics, des connaissances en événements JS sont requises.

Écouter et supprimer

Vocabulaire des événements

Quand un événement est déclenché sur un élément, et qu'il faut associer une fonction à appeler lors de ce déclenchement, on dit qu'on « écoute » un événement.

On lie, ou on attache (« to bind ») une fonction à un type d'événement bien déterminé sur un élément. L'élément en question est alors « écouté » sur ce type d'événement.
On supprime, on délie ou on détache (« to unbind ») une fonction à un type d'événement sur un élément lorsqu'on enlève l'appel à la fonction lors du déclenchement de l'événement sur cet élément.

La fonction de retour est appelée un « écouteur » d'événement (addEventListener() signifie « ajouter un écouteur d'événement » !).

Écouter

Les méthodes apprises au début du chapitre sur les animations, par exemple click() ou mouseenter(), permettent de lier un événement à une fonction : on appelle ça un écouteur d'événement.
bind() permet de généraliser cela en lui passant en premier argument une chaîne de caractères représentant tous les événements (séparés par des espaces) à lier avec la fonction passée en paramètre.

$('p,textarea')
  .bind('mouseenter focus',function(){
    $(this).css('border-color','#222');
  })
  .bind('mouseleave blur',function(){
    $(this).css('border-color','#bbb');
  });

$('q').bind('dblclick',function(){
  // Si l'attribut auteur existe...
  if($(this).attr('auteur')){
    // ... l'afficher.
    alert("L'auteur de cette citation est "+$(this).attr('auteur')+' !');
  }
});

L'avantage de cette méthode n'est peut être pas évident au début, mais vous le comprendrez vite avec les méthodes qu'on va apprendre ensuite :) .

La fonction false

Depuis la version 1.4.3, au lieu de spécifier une fonction, on peut mettre un booléen false.
Il désigne alors la fonction qui renvoie false.

$('a').bind('click',false);
// est équivalent à
$('a').bind('click',function(){
  return false;
});

Dans ce cas-là, cela supprime le comportement par défaut du navigateur qui consiste à ouvrir le lien (attribut href de la balise <a>).

Supprimer

unbind() permet d'enlever l'écouteur d'événement créé préalablement avec bind().
On peut lui passer divers arguments qui effectuent sur les éléments concernés :

  • aucun argument ( unbind()) : supprime tous les écouteurs ;

  • nom de l'événement ( unbind('click')) : supprime tous les écouteurs du type de l'événement (il peut y avoir plusieurs fonctions pour un même événement) ;

  • nom de l'événement et fonction ( unbind('click',fonction)) : supprime juste un écouteur bien précis pour ce type d'événement.

On peut, comme avec bind(), spécifier plusieurs événements.

// Affichera l'adresse du lien qu'une seule fois.
$('#contenu a:not(.interne)').click(function(){
  alert('adresse du lien : '+$(this).attr('href'));
  $(this).unbind('click');
  // le lien ne sera pas suivi
  return false;
});

$('#contenu textarea')
  .focus(zoneTexte)
  .focus(function(){
    $(this).after('<br />Clic !');
  })
  .blur(function(){
    // couleur jaune clair
    $(this).css('background-color','#ff8');
  })
  // Quand on double-cliquera, enlèvera tous les événements, sauf le 'Clic !'.
  .dblclick(function(){
    $(this).after('<br />Double Clic !');
    $(this)
      .unbind('focus',zoneTexte)
      .unbind('blur');
  });

function zoneTexte(){
  // couleur magenta clair
  $(this).css('background-color','#f8f');
}

function unbindZoneTexte(){
  // enlève tout
  $('#contenu textarea').unbind();
}
Référence à une fonction

On peut appeler unbind() avec le nom de l'événement, puis une référence à une fonction. Notez bien la distinction entre une fonction et sa référence.

Une fonction est une variable :

var maFonction = function(){
  // faire quelque chose
  alert('Blabla !');
}

Deux fonctions identiques (qui font la même chose) peuvent êtres déclarées séparément :

var maFonction2 = function(){
  // faire quelque chose
  alert('Blabla !');
}

Cependant, même si le JavaScript est un langage de script, on ne va pas s'amuser à comparer des fonctions pour voir si elles sont identiques.

Une fonction est identique à une autre si l'une est une référence à l'autre :

var maFonction3 = maFonction;

// maFonction et maFonction2 ne pourront jamais être égales
// elles ont été créées séparément en mémoire..
alert(maFonction == maFonction2); // false

// ..mais maFonction3 est une référence à maFonction
alert(maFonction == maFonction3); // true

Dans ce code, écrire le nom de la variable (sans parenthèses pour appeler la fonction) désigne la variable contenant la fonction en elle-même et non la fonction.

Ainsi pour unbind() il faut donner une référence à la fonction passée à bind(), comme dans l'exemple un peu plus au-dessus.

Enlever la fonction false

Toujours depuis la version 1.4.3, il suffit de donner false à unbind() pour enlever la fonction qui retourne false.

Les événements uniques

one() a les mêmes propriétés que bind() (et les mêmes arguments) à l'exception que l'événement ne se déclenche qu'une et une seule fois.
Tout comme avec bind(), on utilise unbind() afin de délier un événement à l'appel de la fonction.

$('a:not(.interne)').one('click',function(){
  alert('adresse du lien : '+$(this).attr('href'));
  return false;
});

Attacher plusieurs événements en même temps

Une des nouveautés de la version 1.4 est de pouvoir donner un objet en argument à bind(), contenant des couples nom de l'événement / fonction de retour attachée.

Par exemple :

$('h1')
  .bind({
      mouseenter: function(){
        $(this).css('color','#000');
      }
    , mouseleave: function(){
        $(this).css('color','#00f');
      }
    , click: function(){
        $(this).css('color','#ff0');
      }
  });

Déclencher

Le déclenchement d'un événement n'est pas réservé à l'utilisateur : vous pouvez déclencher virtuellement des événements sur des éléments sans avoir pour autant l'aval de l'utilisateur.
Il se peut même que votre code n'ait rien à voir avec l'interface utilisateur. Utiliser les événements nous amène à programmer différemment.

trigger()

trigger() permet de déclencher un événement, produisant l'action par défaut du navigateur ainsi que celle que vous avez définie.
Les méthodes utilisées plus haut qui pouvaient être appelées sans argument sont donc le raccourci d'un appel à trigger()

Par exemple $(expression).submit() est équivalent à $(expression).trigger('submit').

$(':text')
  .focus(function(){
    $(this).trigger('click');
  })
  .click(function(){
    $(this).after(' clic !');
  });

triggerHandler()

triggerHandler() agit presque de la même manière que trigger() sauf qu'elle n'actionne pas le mécanisme par défaut du navigateur (par exemple quand un champ gagne le « focus », le texte du champ est sélectionné et on peut écrire), mais appelle seulement la fonction qu'on a définie.

Autre particularité, triggerHandler() ne fait son effet que sur le premier élément de tous les éléments contenus dans l'objet jQuery.

$('#contenu :text')
  .mouseover(function(){
    // Appelle la fonction que vous avez définie
    // mais ne met pas le curseur de texte.
    $(this).triggerHandler('focus');
  })
  .focus(function(){
    $(this).after(' focus !');
  });

Résumé des événements déclenchables ou non

Événements déclenchables :
  • blur ;

  • change ;

  • click ;

  • dblclick ;

  • error ;

  • focus ;

  • keydown ;

  • keypress;

  • keyup ;

  • select ;

  • submit.

Événements non déclenchables :
  • load ;

  • mousedown ;

  • mouseout ;

  • mouseover ;

  • mousemove ;

  • mouseup ;

  • resize ;

  • unload.

Complément d'informations sur bind() : nos propres événements !

Autre aspect des événements, qui renforce l'intérêt des méthodes apprises ici (parce que dans les cas simples on peut se débrouiller sans elles), est le fait qu'on peut créer nos propres événements :

$('a')
  .bind('lien',function(){
    alert('Vous aller vers '+$(this).attr('href')+' !');
  })
  .click(function(){
    if(!$(this).hasClass('interne')){
      $(this).trigger('lien');
    }
  });

Bien évidemment, cela va de pair avec l'utilisation de trigger().
Notez que ce code peut tout aussi bien s'écrire comme cela depuis la version 1.4 :

$('a').bind({
    lien : function(){
      alert('Vous aller vers '+$(this).attr('href')+' !');
      }
  , click : function(){
      if(!$(this).hasClass('interne')){
        $(this).trigger('lien');
    }
});

Passer des arguments aux écouteurs

On peut donner des informations à trigger(), triggerHandler(), bind(), one() ainsi qu'aux méthodes événements (onclick() par exemple), des données qui seront alors accessibles dans les écouteurs (déclenchées lors d'un événement).

On distingue deux cas :

  1. Le premier, simple, correspond à ce qu'on pouvait faire avec each() (cf. fin de "Modifier la structure").
    Lors de la création d'un événement sur un élément, on peut transmettre des données qui seront exploitables dans l'écouteur ;

  2. Le second cas, nettement plus pratique, permet de déclencher un événement d'une manière plus précise.
    Ainsi dans l'écouteur, l'exploitation de ces données permettra concrètement de savoir dans quelles circonstances on a déclenché l'événement.

Voici comment on fait :

avec bind(), one() et les méthodes événements
  • Au lieu d'appeler bind() et one() avec deux arguments (le nom de l'événement, puis la fonction), on peut rajouter un troisième argument, donnees, qui vient se glisser entre le premier et le deuxième : bind('mouseover','salut',maFonction) ;

  • Au lieu d'appeler les méthodes événements (les méthodes apprises au début du chapitre sur les animations, mouseover() par exemple) avec en premier argument la fonction de retour, on peut insérer juste avant (donc la fonction de retour est deuxième) un argument donnees : mouseover('salut',maFonction).

Le premier argument de la fonction appelée lors du déclenchement d'un événement, L'objet événement, (que l'on va étudier plus tard) contient un membre data égale à donnees :

function maFonction(e){
  // salut !
  alert( e.data + ' !' );
}

Bien évidemment, donnees peut être de n'importe quel type, le plus pratique étant un objet.

Exemple :

function alerte(e){
  alert( 'Vous êtes sur ' + e.data.type + ' !' );
}

$('a').mouseenter({
  type : 'un lien'
},alerte);

$('img').bind('mouseenter',{
  type : 'une image'
},alerte);
avec trigger() et triggerHandler()

Une autre particularité de trigger() et de triggerHandler() : vous pouvez passer des arguments aux fonctions de retour passées à bind() / one() / live() !
Le second argument de ces deux méthodes est un tableau qui représente les arguments à passer aux fonctions de retour.

$('a').bind('afficherInfosLien',function(e,url,description,classe){
  alert( 'URL : ' + url
       + '\nDescription :' + description + '\n'
       + ( classe ? ( 'classe du lien : ' + classe ) : '' )
       );
});

$('a').click(function(){
  $(this).trigger('afficherInfosLien',[
    $(this).attr('href'),
    $(this).text(),
    $(this).attr('class')
  ]);
  return false;
});

J'espère que les deux exemples vous ont bien fait comprendre comment cela s'utilisait, même si l'utilité n'est pas évidente au premier abord.

Déléguer

Le principe de la délégation d'événement

Nous allons voir ici deux méthodes permettant respectivement de faire vivre des événements, et de les déléguer ; ainsi que leurs deux méthodes opposées qui délient ces événements aux appels de fonctions précédemment réalisées.

Ces deux procédés reviennent au même : le principe est de créer un écouteur sur certains éléments bien particuliers (sélectionnés au moyen d'un... sélecteur !) contenu dans un élément, et sur tous ses futurs éléments qui répondent au même sélecteur.
Comprenez par futurs éléments, ceux qui seront créés dynamiquement après.

L'avantage de la délégation d'événement est d'écouter un événement sur un élément parent, qui contient nos éléments « cibles ».
Il n'y a alors qu'un seul écouteur attaché. Attacher un écouteur à tous les éléments « cibles » peut se révéler être beaucoup plus lent.
De plus, avec la délégation, votre code marche avec les éléments que vous rajouterez après. Vous définissez donc un comportement global lorsqu'un événement est déclenché, sans se soucier quels sont les éléments concernés.

Si vous clonez un élément, même si l'argument n'est pas à true, les événements délégués seront copiés. Souvenez-vous, l'écouteur est attaché à un élément parent, et non aux éléments en question...

Événements vivants avec live()

live() a les mêmes propriétés que bind() à l'exception d'une subtile différence : si des éléments correspondant à la requête jQuery (la variable selector de l'objet jQuery) venaient à se créer plus tard, ils se verraient eux aussi liés au même événement, à la même fonction.

Donc faire $(this).live(...) n'a aucun intérêt : l'événement ne sera pas vivant !

De tout évidence, des exemples s'imposent :D :

var i = 1;

$('#contenu :text')
  .live('mouseover',function(){
    $(this).css('background-color','#fbf');
  })
  .live('mouseout',function(){
    $(this).css('background-color','#fff');
  })
  .live('click',function(){
    $(this)
      // Cloner sans copier les événements mais
      // qui copie les événements live quand même.
      .clone()
      .val(i++)
      .insertAfter(this);
  });
Variable selector

Il faut aussi que vous sachiez que live() utilise la propriété selector de l'objet jQuery en question pour savoir à quelle requête jQuery répondent les futurs éléments.
Vous pouvez vous amuser à bidouiller jQuery :

var objet = $('span');
objet.selector = 'p';

objet.live('click',fonction(){
  alert('Salut!');
});
<p>Paragraphe</p>
<span>Balise span</span>

Devinez le résultat ;) ...
Cette méthode utilise aussi la variable context, que vous n'avez pas encore rencontrée.

Événements délégués avec delegate()

Cette méthode est apparue depuis la version 1.4, et permet de réaliser la même chose que live(), sauf qu'elle ne s'utilise pas de la même façon.
delegate() prend en argument deux chaînes de caractères, la première étant les éléments concernés par la délégation de l'événement, la seconde étant l'événement en question. Le troisième argument est bien évidemment l'écouteur de l'événement en question.
delegate() s'utilise sur l'élément conteneur de nos éléments cibles.

Il faut traduire ce code : $('p').live('click',maFonction); en « Faire vivre l'événement clique de souris sur les éléments paragraphes, avec la fonction maFonction ».

Maintenant, avec delegate(), il faut lire ce code : A.delegate('p','click',maFonction); en « Déléguer l'événement clique de souris sur les éléments paragraphes contenus dans l'élément A, avec la fonction maFonction ».

Votre code est donc plus lisible avec delegate().

Image utilisateur

Principe de la délégation d'un événement

$('p').delegate('span','mouseenter',function(){
  $(this).css('color','#f00');
});

Par contre, delegate() n'utilise pas la propriété selector de l'objet jQuery en question.

Méthodes die() et undelegate()

die() est à live() ce que unbind() est à bind(). Il n'y a alors plus d'appel de l'écouteur sur l'élément parent.
Quant à undelegate(), c'est la même chose que die(), excepté que son premier argument permet de filtrer les éléments dont on veut supprimer la délégation d'un événement.

Ces deux méthodes peuvent s'utiliser sans arguments, et dans ce cas tous les événements délégués sont supprimés. Sinon, les arguments sont les mêmes qu'unbind() (que vous avez appris un peu avant), à part pour undelegate() où les arguments sont décalés d'un cran.

var i = 1;

$('#contenu :text')
  .live('mouseover',function(){
    $(this).css('background-color','#fbf');
  })
  .live('mouseout',function(){
    $(this).css('background-color','#fff');
  })
  .live('click',function(){
    $(this)
      .clone()
      .val(i++)
      .insertAfter(this);
  });

function die(){
  $('#contenu :text').die();
}

Espaces de noms

Les espaces de noms (namespaces en anglais) permettent de créer des sortes de classes pour les événements.
Il suffit de mettre un point après le nom de l'événement lors d'un appel à bind(), unbind(), one(), trigger(), triggerHandler(), live() ainsi que die().

On peut alors créer plusieurs appels à différentes fonctions pour un même type d'événement, et mieux manipuler ces différents écouteurs d'événements.
L'utilité de ces méthodes est redoublée (contrairement à juste utiliser click(), mouseover() etc..) ; l'usage de trigger() et de triggerHandler() est alors utile pour déclencher nos événements avec un espace de nom bien particulier.

Quelques règles

À noter que :

  • On peut définir plusieurs noms en même temps, par exemple bind('focus.hello.world') qui définit focus.hello et focus.world :

    $('input').bind('focus.hello.world',function(){ /* action ... */ });
    
    
    $('#focusHello').click(function(){
      $('input').trigger('focus.hello');
      // action !
    });
    
    $('#focusWorld').click(function(){
      $('input').trigger('focus.world');
      // action !
    });
    
  • Mais on ne peut pas déclencher plusieurs noms en même temps, par exemple trigger('focus.hello.world') qui ne déclenche ni focus.hello, ni focus.world :

    $('input').bind('focus.hello',function(){ /* action 1 */ });
    $('input').bind('focus.world',function(){ /* action 2 */ });
    
    
    $('#focusHelloWorld').click(function(){
      $('input').trigger('focus.hello.world');
      // rien du tout !
    });
    

    Par contre ceci marche avec unbind() !

  • Si on ne précise pas de noms, tous les noms sont déclenchés, par exemple trigger('focus') qui déclenche focus.hello et focus.world :

    $('input').bind('focus.hello',function(){ /* action 1 */ });
    $('input').bind('focus.world',function(){ /* action 2 */ });
    
    
    $('#focus').click(function(){
      $('input').trigger('focus');
      // action 1 et 2 !
    });
    
  • Si on précise un nom sans type d'événement, rien n'est déclenché, par exemple trigger('.salut') qui ne déclenche ni focus.salut ni blur.salut :

    $('input').bind('focus.salut',function(){ /* action 1 */ });
    $('input').bind('blur.salut',function(){ /* action 2 */ });
    
    
    $('#salut').click(function(){
      $('input').trigger('.salut');
      // rien du tout !
    });
    
  • Vous pouvez bien sûr utiliser vos propres types événements avec des noms, par exemple trigger('hello.world') :

    $('input').bind('hello.world',function(){ /* action ... */ });
    
    
    $('#hello').click(function(){
      $('input').trigger('hello');
      // action !
    });
    
    $('#helloWorld').click(function(){
      $('input').trigger('hello.world');
      // action !
    });
    

Utilité ?

Situation et problème

Imaginez que vous avez deux événements différents qui attribuent au click une fonction :

$('input').click(function(){
  alert('Vous avez cliqué sur un élément répondant au sélecteur "input"');
});

$(':text').click(function(){
  alert('Vous avez cliqué sur un élément répondant au sélecteur ":text"');
});

Maintenant, vous voulez pouvoir à tout moment retirer un des événements. Il faudrait alors contenir les deux fonctions dans des variables, comme ceci :

function clickTexte(){
      alert('Vous avez cliqué sur un élément répondant au sélecteur "textarea,:text"');
}

function clickChamp(){
  alert('Vous avez cliqué sur un élément répondant au sélecteur ":text"');
}


// premier événement
$('textarea,:text').click(clickTexte);
// second événement
$(':text').click(clickChamp);

// enlever le premier événement
$('textarea,:text').unbind('click',clickTexte);
// enlever le second événement
$(':text').unbind('click',clickChamp);

Vous voyez qu'il n'y a pas vraiment de problème, il faut juste stocker les fonctions (étant donné qu'on peut attribuer plusieurs fonctions pour un événement). On n'a pas utilisé ici de fonctions anonymes.

Déclarer des fonctions sur le tas

Notez que ce code marche :

// variable indéfinie
var nombre;
// affecte 8 à nombre, puis affiche 8
alert( nombre = 8 );
// affiche 8
alert( nombre );

Donc si vous voulez absolument garder la technique consistant à déclarer la fonction sur le tas (et ainsi garder la description de la fonction à côté de son attachement à l'événement), vous pouvez le faire :

var clickTexte, clickChamp;
// premier événement
$('textarea,:text').click(clickTexte = function(){
      alert('Vous avez cliqué sur un élément répondant au sélecteur "textarea,:text"');
});
// second événement
$(':text').click(clickChamp = function(){
  alert('Vous avez cliqué sur un élément répondant au sélecteur ":text"');
});

// enlever le premier événement
$('textarea,:text').unbind('click',clickTexte);
// enlever le second événement
$(':text').unbind('click',clickChamp);
Et donc ?

Tout ça pour quoi ?

Je voulais juste illustrer le fait que les espaces de noms ne sont pas indispensables : on peut très bien contourner les problèmes qu'ils résolvent. Mais s'ils ont été créés, c'est justement pour contourner proprement tous ces problèmes ! ;)

L'utilité des espaces de noms n'est pas évidente au premier abord, mais s'il vous arrive d'en avoir besoin (quand on commence à manipuler beaucoup d'événements), c'est toujours utile de savoir s'en servir.
C'est simplement beaucoup plus utile et agréable à utiliser. Ils nous facilitent la vie, et leur présence reste dans l'esprit de jQuery !

La complexité de l'objet événement, née du fait qu'il existe plusieurs navigateurs, peut-être maîtrisée en lisant des articles (sur quirksmode par exemple) ; prenez le temps de chercher des ressources sur internet, cela ne peut que vous enrichir sur le sujet ;) .

Appris dans ce chapitre : espaces de noms pour les événements, propagation des événements, objet événement, méthodes : bind(), unbind(), one(), trigger(), triggerHandler(), live(), die(), delegate(), undelegate(), Event.stopPropagation(), Event.preventDefault().

Vous êtes maintenant capables de maîtriser l'ensemble de cette bibliothèque JavaScript.

Userscripts

Maintenant que vous maîtrisez suffisamment cette bibliothèque, vous pouvez aisément vous essayez à coder des «userscripts» (ou même des bookmarklets) pour les sites qui utilisent jQuery, par exemple le Site du Zéro lui-même.
Les userscripts sont des petits scripts codés par des utilisateurs lambda qui s'exécutent après le chargement d'une page, afin de la modifier et ainsi ajouter des fonctionnalités. Les userscripts les plus connus pour le SdZ sont les ZppScripts. N'hésitez pas à jeter un coup d'œil !

jQuery hors web

Il faut aussi savoir que l'utilisation du JavaScript ne se limite pas aux pages web : on peut facilement coder des petites extensions pour Firefox par l'intermédiaire de Jetpack (module pour Firefox) avec lequel on peut utiliser jQuery (cf. documentation). D'ailleurs, certains navigateurs commencent à migrer vers un système incluant le JavaScript afin de coder des extensions.
Enfin, vous pouvez vous servir de jQuery avec Adobe AIR afin de créer des applications exécutables sur tous les systèmes d'exploitations.

Mot de la fin

Voilà... j'espère que vous aussi vous êtes fans de jQuery et que vous continuerez à découvrir sa puissance ainsi que celle de ses plugins.
Si jQuery ne vous plaît pas : vous pouvez apprendre un autre « framework » comme Prototype ou Yahoo UI.
Maintenant que vous connaissez une bibliothèque JavaScript, ce sera plus facile d'en apprendre une autre en lisant sa documentation.

  • une erreur dans le tutoriel : signalez une erreur pour faire appel à un validateur qui va la corriger. Vous pouvez aussi m'envoyer un message personnel.

  • une question sur le tutoriel, ou une envie d'y participer : n'hésitez pas à commenter le tutoriel ou poster sur topic officiel du tuto. Vous pouvez aussi y donner vos exemples, ou encore proposer des questions de Q.C.M.

  • un problème : direction le forum JavaScript en postant un sujet commençant par [jQuery].

Je tenais à remercier les zCorrecteurs, ainsi que MisterDo pour son aide sur la première partie du tutoriel : il m'a beaucoup aidé et m'a aussi suggéré une organisation du tutoriel bien plus efficace !
Les schémas de ce tutoriel ont été réalisés avec OpenOffice Draw.
N'hésitez pas à commenter le tutoriel, à bientôt !

Exemple de certificat de réussite
Exemple de certificat de réussite