web-dev-qa-db-fra.com

Modèle d'événement JQuery et prévention des gestionnaires en double

Encore une fois, je veux charger une page qui contient son propre script dans un div en utilisant $ ("divid"). Load (...). Le problème auquel je suis confronté est lié aux événements. Disons que nous déclenchons ("singe") à partir de la page parent et sur la page chargée, nous lions ("singe") et faisons simplement une alerte ("lié singe"). Si la même méthode de chargement est appelée plusieurs fois, la liaison est appelée plusieurs fois. Maintenant, je pouvais simplement le délier avant de le lier, ou vérifier le nombre de gestionnaires avant la liaison, puis ne pas le lier pour empêcher cela. Aucune des deux options n'est évolutive comme si je voulais plus tard me lier à ce déclencheur dans une autre "sous-page" (une page chargée dans un div).

Idéalement, ce que je veux faire, c'est vérifier si le gestionnaire que je suis sur le point d'ajouter existe déjà, mais je VEUX toujours utiliser des gestionnaires anonymes ... (en demandant un peu avec cette dernière demande, je pense). Actuellement, j'ai une solution de contournement en utilisant des méthodes prédéfinies/nommées, puis en vérifiant cela avant la liaison.

// Found this on StackOverflow
function getFunctionName(fn)
{
 var rgx = /^function\s+([^\(\s]+)/
 var matches = rgx.exec(fn.toString());
 return matches ? matches[1] : "(anonymous)"
}

function HandlerExists(triggerName, handlerName) {
        exists = false;
        if ($(document).data('events') !== undefined) {
            var event = $(document).data('events')[triggerName];
            if(event !== undefined)
            {
                $.each(event, function(i, handler) {
                    alert(handlerName);
                    if (getFunctionName(handler) == handlerName) {
                        exists = true;
                    }
                });
            }
        }
        return exists;
    }

Je pense que c'est une façon assez grossière de procéder, mais cela semble fonctionner. Je fais juste ce qui suit avant la liaison comme suit:

if (!HandlerExists("test", "theMethod")) {
    $(document).bind("test", theMethod);
}

Quelqu'un a-t-il une solution plus élégante? par exemple, existe-t-il un moyen de vérifier qu'un script particulier est chargé? afin que je puisse utiliser getScript () pour charger les js de la page enfant lors du premier chargement, puis simplement ne pas le charger lors des chargements suivants (et simplement déclencher un déclencheur qui serait géré par le js préexistant).

34
Mr AH

Empêchez la liaison en double en utilisant l'espace de noms d'événement de jQuery

Il existe en fait deux façons différentes d'empêcher les doublons. On passe simplement le gestionnaire d'origine dans le délier, MAIS s'il s'agit d'une copie et non dans le même espace en mémoire, il ne se déliera pas, l'autre moyen populaire (en utilisant des espaces de noms) est un moyen plus sûr d'y parvenir.

Il s'agit d'un problème courant avec les événements. Je vais donc expliquer un peu les événements jQuery et l'utilisation de l'espace de noms pour éviter les liaisons en double.



RÉPONSE: (Court et droit au but)


// bind handler normally
$('#myElement').bind('myEvent', myMainHandler);

// bind another using namespace
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler);

// unbind the unique and rebind the unique
$('#myElement').unbind('myEvent.myNamespace').bind('myEvent.myNamespace', myUniqueHandler);
$('#myElement').bind('myEvent.myNamespace', myUniqueHandler);

// trigger event
$('#myElement').trigger('myEvent');

// output
myMainHandler() // fires once!
myUniqueHandler() // fires once!



EXEMPLE DE RÉPONSE: (Explication détaillée complète)


Créons d'abord un exemple d'élément auquel se lier. Nous utiliserons un bouton avec l'ID de #button. Créez ensuite 3 fonctions qui peuvent et seront utilisées comme gestionnaires pour se lier à l'événement:

function exampleOne () nous lierons avec un clic. function exampleTwo () nous lierons à un espace de noms du clic. function exampleThree () nous lierons à un espace de nom du clic, mais délierons et lierons plusieurs fois sans jamais supprimer les autres liaisons, ce qui empêche la duplication des liaisons sans supprimer aucune des autres méthodes liées.

Exemple de démarrage: (Créer un élément à lier et certaines méthodes pour être nos gestionnaires)

<button id="button">click me!</button>


// create the example methods for our handlers
function exampleOne(){ alert('One fired!'); }
function exampleTwo(){ alert('Two fired!'); }
function exampleThree(){ alert('Three fired!'); }

Lier l'exemple Un clic:

$('#button').bind('click', exampleOne); // bind example one to "click" 

Maintenant, si l'utilisateur clique sur le bouton ou appelle $ ('# bouton'). Trigger ('clic'), vous obtiendrez l'alerte "One Fired!";

Liez exampleTwo à un espace de noms de clic: "le nom est arbitraire, nous utiliserons myNamespace2"

$('#button').bind('click.myNamespace2', exampleTwo);

La chose intéressante à ce sujet est que nous pouvons déclencher le "clic" qui déclenchera exampleOne () ET exampleTwo (), ou nous pouvons déclencher "click.myNamespace2" qui ne déclenchera que exampleTwo ()

Liez exampleThree à un espace de noms de click: "encore une fois, le nom est arbitraire tant qu'il est différent de l'espace de noms de exampleTwo, nous utiliserons myNamespace3"

$('#button').bind('click.myNamespace3', exampleThree);

Maintenant, si le "clic" est déclenché, les trois méthodes d'exemple seront déclenchées, ou nous pouvons cibler un espace de noms spécifique.

METTEZ TOUT ENSEMBLE POUR ÉVITER LA DOUBLE

Si nous devions continuer à lier exampleThree () comme ceci:

$('#button').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').bind('click.myNamespace3', exampleThree);

Ils seraient renvoyés trois fois, car chaque fois que vous appelez bind, vous l'ajoutez au tableau d'événements. Donc, vraiment simple. Déliez simplement cet espace de noms avant la liaison, comme ceci:

$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);
$('#button').unbind('click.myNamespace3').bind('click.myNamespace3', exampleThree); 
$('#button').bind('click.myNamespace3', exampleThree);

Si la fonction de clic est déclenchée, exampleOne (), exampleTwo () et exampleThree () ne sont déclenchés qu'une seule fois.

Pour tout regrouper dans une fonction simple:

var myClickBinding = function(jqEle, handler, namespace){
    if(namespace == undefined){
        jqEle.bind('click', handler);
    }else{
        jqEle.unbind('click.'+namespace).bind('click.'+namespace, handler);
    }
}   

Résumé:

les espaces de noms d'événement jQuery permettent la liaison à l'événement principal, mais permettent également de créer et d'effacer des espaces de noms enfants sans affecter les espaces de noms frères ou parents qui, avec une réflexion créative très minimale, permettent d'éviter les liaisons en double.

Pour plus d'explications: http://api.jquery.com/event.namespace/

53
eli geske

Une excellente réponse de

lier l'événement une seule fois

copyPastedInfo :

si vous pouvez l'appliquer, vous voudrez probablement jeter un œil à event.preventDefaultt et event.stopPropagation OR dissocier et lier à chaque fois, dans la méthode comme

function someMethod()
{
  $(obj).off('click').on('click', function {});
}
26
user1028880

Ummm que diriez-vous d'utiliser one()? http://api.jquery.com/one/

Ou suis-je complètement incompris?

7
prodigitalson

Ce n'est pas une réponse complète, mais j'ai rapidement trouvé comment réparer tout mon code avec des changements minimes. Dans ma classe js "de base" (c'est un objet qui est utilisé sur chaque page pour les connexions de bouton standard par nom de classe, etc.), j'ai ajouté la méthode suivante qui vérifie les gestionnaires de dupe:

BindOnce: function(triggerName, fn) {
    function handlerExists(triggerName, theHandler) {
        function getFunctionName(fn) {
            var rgx = /^function\s+([^\(\s]+)/
            var matches = rgx.exec(fn.toString());
            return matches ? matches[1] : "(anonymous)"
        }
        exists = false;
        var handlerName = getFunctionName(theHandler);
        if ($(document).data('events') !== undefined) {
            var event = $(document).data('events')[triggerName];
            if (event !== undefined) {
                $.each(event, function(i, handler) {
                    if (getFunctionName(handler) == handlerName) {
                        exists = true;
                    }
                });
            }
        }
        return exists;
    }
    if (!handlerExists(triggerName, fn)) {
        $(document).bind(triggerName, fn);
    }
},

Ensuite, je l'invoque simplement à la place de la méthode bind!

$.mystuff.BindOnce("TheTrigger", OnTriggered)

Notez que vous ne pouvez pas utiliser les méthodes anon ici car elles seraient simplement appelées 1, 2, 3 etc. et la seule façon de vérifier les dupes serait avec une toString () sur la méthode, ce qui serait assez lent dans une application complexe

6
Mr AH

Environ cent ans trop tard, mais si vous n'utilisez pas de fonctions anonymes, vous pouvez le faire beaucoup plus simplement en utilisant d'abord la liaison:

$.fn.eventWillOnlySubscribeOnce = function () {
    return this.each(function () {
        var oneHandler = (function () {
             HANDLER CODE
        });

    $(this).unbind("submit", oneHandler);
    $(this).bind("submit", oneHandler);
});
};

Cette implémentation fonctionnera dans Firefox 4, mais pas dans de nombreux autres navigateurs - car la variable de gestionnaire est créée à chaque fois de sorte que la dissociation ne peut pas trouver de gestionnaire correspondant à dissocier. Déplacer le gestionnaire dans la portée parent résout le problème: c'est-à-dire.

var oneHandler = (function () {
    HANDLER CODE
});

$.fn.eventWillOnlySubscribeOnce = function () {
    return this.each(function () {
    $(this).unbind("submit", oneHandler);
    $(this).bind("submit", oneHandler);
});
};

On peut supposer que Firefox fait une optimisation intelligente, ce qui signifie que le gestionnaire est maintenu comme une variable constante quelque part car il ne change jamais.

1
Massif