web-dev-qa-db-fra.com

Plusieurs gestionnaires d'événements JS sur un seul élément

Je travaille avec une application Web existante, dans l'application, il existe une variété de boutons d'envoi sur différents formulaires, certains utilisant une publication http régulière, certains définissant une fonction onClick et certains liant un gestionnaire d'événements js au bouton en utilisant une classe sur le élément.

Ce que je veux faire, c'est lier un autre gestionnaire d'événements à ces boutons en ajoutant simplement une classe aux boutons, mais ce que je veux déterminer, c'est que le nouveau gestionnaire d'événements sera garanti d'être exécuté, ou l'une des actions de soumission de formulaire pourrait-elle se produire avant qu'il ne signifie que ma nouvelle fonction n'est pas atteinte.

Le scénario d'exemple est que je veux ajouter une classe à ces boutons qui les redirige tous vers une fonction js commune qui enregistre simplement l'utilisation de certaines API. Existe-t-il un risque que la fonction de journalisation ne soit pas appelée car le formulaire soumis a quitté la page?

Je n'ai pas fait beaucoup de développement js, et j'ai pu tester cela 100 fois et juste avoir de la chance avec le tir.


Ci-dessous se trouve du code avec lequel j'ai testé pour l'un des exemples - encore une fois, je ne demande pas comment lier plusieurs événements, la question est de ma compréhension de la spécification et si l'exécution de tous les gestionnaires est garantie.

$(document).ready(function(){
    $('.testingBtn').click(function() {
        window.location.replace("http://stackoverflow.com");
    });
    $( ".testingBtn" ).click(function(){
        alert('submitting!');
    });
});


<input class="testingBtn" type="submit" id="submitform" value="Complete Signup" />

Comme vu ci-dessus, je peux lier les multiples événements, et dans cet exemple, simplement dirigé vers une autre URL, mais cela pourrait être une form.submit() etc. Dans mes tests, l'alerte a toujours été déclenchée en premier, mais suis-je juste avoir de la chance avec les conditions de course?

26
rhinds

Dans JS, vous n'avez pas vraiment le contrôle sur l'ordre d'appel des gestionnaires d'événements, mais avec une délégation prudente et des écouteurs bien placés, il est possible .

La délégation est l'une des fonctionnalités les plus puissantes du modèle d'événement. Comme vous le savez peut-être ou non: dans JS, un événement est transmis au sommet du dom, d'où il se propage jusqu'à l'élément sur lequel l'événement doit être appliqué. Il va de soi, par conséquent, qu'un écouteur d'événement attaché à l'objet global appellera son gestionnaire avant vers un écouteur qui a été attaché à l'élément lui-même .

window.addEventListener('click',function(e)
{
    e = e || window.event;
    var target = e.target || e.srcElement;
    console.log('window noticed you clicked something');
    console.log(target);//<-- this is the element that was clicked
}, false);//<-- we'll get to the false in a minute

Il est important de noter que nous avons réellement accès à l'objet événement dans les gestionnaires. Dans ce cas, nous avons laissé l'objet événement intact, il continuera donc à se propager vers la cible, en descendant, il pourrait rencontrer quelque chose comme ceci:

document.getElementById('container').addEventListener('click', function(e)
{
    e = e || window.event;
    var target = e.target || e.srcElement;
    if (target.tagName.toLowerCase() !== 'a' || target.className.match(/\bclickable\b/))
    {
        return e;//<return the event, unharmed
    }
    e.returnValue = false;
    if (e.preventDefault)
    {
        e.preventDefault();
    }
}, false);

Maintenant, ce gestionnaire sera appelé après que l'écouteur au niveau de la fenêtre ait appelé son assistant. Cette fois, l'événement est modifié si l'élément cliqué n'avait pas la classe clickable, ou si l'élément est un lien. L'événement est annulé, mais il continue de vivre. L'événement est toujours libre de se propager plus bas dans le dom, donc nous pourrions rencontrer quelque chose comme:

document.getElmentById('form3').addEventListener('click',function(e)
{
     e = e || window.event;
     if (e.returnValue === false || e.isDefaultPrevented)
     {//this event has been changed already
         //do stuff, like validation or something, then you could:
         e.cancelBubble = true;
         if (e.stopPropagation)
         {
             e.stopPropagation();
         }
     }
}, false);

Ici, en appelant stopPropagation, l'événement est supprimé. Il ne peut pas se propager plus loin dans le dom jusqu'à sa cible à moins que l'événement n'ait déjà été modifié. Sinon, l'objet événement se déplace plus bas dans le DOM, comme si de rien n'était.

Une fois qu'il atteint son nœud cible, l'événement entre dans sa deuxième phase: la phase bulle. Au lieu de se propager dans les profondeurs du DOM, il remonte, au niveau supérieur (jusqu'à l'objet global, où il a été envoyé ... d'où il est venu et tout cela).

Dans la phase bulle, les mêmes règles s'appliquent que dans la phase de propagation, et inversement. L'objet événement rencontrera les éléments les plus proches de l'élément cible en premier et l'objet global en dernier.

Il y a beaucoup de diagrammes pratiques et clairs pour cela ici . Je ne peux pas dire mieux que le bon vieux mode, alors je vous suggère de lire ce qu'ils ont à dire là-bas.

Conclusion: lorsque vous traitez avec 2 écouteurs d'événements, attachez-les tous les deux à un niveau différent pour les trier en file d'attente comme vous le souhaitez.

Si vous voulez garantir que les deux sont appelés, arrêtez uniquement l'événement de se propager dans le gestionnaire qui sera appelé en dernier.

Lorsque vous avez deux écouteurs, attachés au même élément/objet pour le même événement, je n'ai jamais rencontré de situation où l'écouteur qui était attaché en premier, n'était pas également appelé en premier.

Ça y est, je vais me coucher, en espérant avoir du sens

51
Elias Van Ootegem

jQuery rend cela facile.

$(document).on('click', '.someclass', function() {
  doStuff();
});

$(document).on('click', '.someclass', function() {
  doMoreStuff();
});

Les gestionnaires tireront ensuite tous les deux au clic. jQuery conserve une file d'attente de gestionnaires pour vous. Et gère les clics sur le document qui correspondent à un sélecteur de votre choix afin qu'ils puissent être déclenchés peu importe quand vos boutons sont créés.

9
Alex Wayne

J'ai/avait un problème similaire à celui-ci. Cependant, je ne peux pas affecter l'ordre/déléguer les événements de "clic" préexistants (ajoutés par le framework Wicket). Mais j'ai encore besoin d'exécuter un nouvel événement personnalisé avant l'un des événements "clic" ou "changement" gérés par le framework.

Heureusement, plusieurs événements sont effectivement exécutés dans l'ordre. Le "mousedown" et le "mouseup" se produisent avant le "clic".
http://en.wikipedia.org/wiki/DOM_events

$(document).on('mousedown', function (event) {
  event = event || window.event
  var target = event.target || event.srcElement;
  console.log(target + ' before default event'); // Hold mouse button down to see this message in console before any action is executed
});

OR

$(document).on('mouseup', function (event) {
  event = event || window.event
  var target = event.target || event.srcElement;
  alert(target + ' before default event'); // You may not notice this event fires when the page changes unless this is an alert
});

Cela permettra à la journalisation d'être effectuée (par exemple via ajax) avant que l'événement réel ne soit exécuté, par exemple un changement de page via le lien (ajax).

Bien sûr, vous devrez peut-être disposer de moyens plus sophistiqués pour détecter ce que la gestion supplémentaire des événements doit être effectuée, mais vous pouvez utiliser par exemple les informations "cibles" pour cela. => Ce script surveille tout sur la page, car c'est ainsi que j'ai besoin que cela soit fait.

4
Ville Myrskyneva