144 votes

les gestionnaires d’événements jQuery s’exécutent toujours dans l’ordre qu’ils sont tenus - aucun moyen de contourner cela ?

Il peut être souvent embêtant que jQuery gestionnaires d'événements toujours exécuter dans l'ordre où ils ont été tenus. Par exemple:

$('span').click(doStuff1);
$('span').click(doStuff2);

en cliquant sur la durée de entraînera doStuff1() de feu, suivie par doStuff2().

À l'époque, j'lier doStuff2(), je voudrais l'option pour lier avant de doStuff1(), mais il ne semble pas être un moyen facile de faire cela.

Je suppose que la plupart des gens diraient, il suffit d'écrire le code comme ceci:

$('span').click(function (){
    doStuff2();
    doStuff1();
});

Mais c'est juste un exemple simple, dans la pratique, il n'est pas toujours commode de le faire.

Il ya des situations où vous souhaitez lier un événement, et que l'objet qui vous lie a déjà des événements. Et dans ce cas, vous pouvez simplement le nouvel événement à feu avant de tout autres évènements.

Alors, quelle est la meilleure façon d'atteindre cet objectif en jQuery?

123voto

Anurag Points 66470

Mise À Jour De Réponse

jQuery changé le lieu de où les événements sont stockés dans le 1.8. Maintenant vous savez pourquoi c'est une mauvaise idée pour déconner avec interne de l'Api :)

La nouvelle interne de l'API pour l'accès à des événements d'un objet DOM est disponible par le biais du global objet jQuery, et ne sont pas liés à chaque instance, et il faut un élément du DOM comme premier paramètre, et une clé ("événements" pour nous) comme second paramètre.

jQuery._data(<DOM element>, "events");

Alors, voici le code modifié pour jQuery 1.8.

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.on(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    this.each(function() {
        var handlers = $._data(this, 'events')[name.split('.')[0]];
        // take out the handler we just inserted from the end
        var handler = handlers.pop();
        // move it at the beginning
        handlers.splice(0, 0, handler);
    });
};

Et voici une aire de jeux.


Réponse Originale À Cette Question

@Sean l'a découvert, jQuery expose tous les gestionnaires d'événements par l'intermédiaire d'un élément de l' data interface. Plus précisément element.data('events'). En utilisant cela, vous pouvez toujours écrire un plugin simple par lequel vous pouvez insérer tout gestionnaire d'événements à une position spécifique.

Voici un plugin simple qui ne fait juste que, pour insérer un gestionnaire au début de la liste. Vous pouvez facilement étendre ce pour insérer un élément à une position donnée. C'est juste de la matrice de la manipulation. Mais depuis je n'ai pas vu de jQuery source et ne veulent pas manquer l'jQuery magie de passe, j'ai l'habitude d'ajouter le gestionnaire d'aide bind d'abord, puis remaniement du tableau.

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.bind(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    var handlers = this.data('events')[name.split('.')[0]];
    // take out the handler we just inserted from the end
    var handler = handlers.pop();
    // move it at the beginning
    handlers.splice(0, 0, handler);
};

Ainsi, par exemple, pour ce balisage il travail comme (exemple ici):

<div id="me">..</div>

$("#me").click(function() { alert("1"); });
$("#me").click(function() { alert("2"); });    
$("#me").bindFirst('click', function() { alert("3"); });

$("#me").click(); // alerts - 3, then 1, then 2

Cependant, depuis .data('events') n'est pas une partie de leur API publique pour autant que je sais, une mise à jour de jQuery pourrait casser votre code si la représentation sous-jacente des événements attachés changements à partir d'un tableau de quelque chose d'autre, par exemple.

Avertissement: Depuis, tout est possible :), voici votre solution, mais je voudrais encore err sur le côté de la refactorisation de votre code existant, juste essayer de se rappeler l'ordre dans lequel ces éléments étaient attachés peuvent sortir de la main comme vous continuez à ajouter de plus en plus de ces commandé des événements.

35voto

RussellUresti Points 3469

Vous pouvez faire un espace de noms personnalisé des événements.

Ensuite, lorsque vous devez déclencher eux vous pouvez choisir l’ordre.

ou

En outre, déclencher juste cliquer devrait déclencher tous deux dans l’ordre qu’ils ont été liés... donc vous pouvez toujours faire

13voto

Sean Vieira Points 47080

Une très bonne question ... j'ai été intrigué donc j'ai fait un peu de creuser; pour ceux qui sont intéressés, c'est ici où je suis allé, et que je suis venu avec.

En regardant le code source de jQuery 1.4.2 j'ai vu ce bloc entre les lignes 2361 et 2392:

jQuery.each(["bind", "one"], function( i, name ) {
    jQuery.fn[ name ] = function( type, data, fn ) {
        // Handle object literals
        if ( typeof type === "object" ) {
            for ( var key in type ) {
                this[ name ](key, data, type[key], fn);
            }
            return this;
        }

        if ( jQuery.isFunction( data ) ) {
            fn = data;
            data = undefined;
        }

        var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
            jQuery( this ).unbind( event, handler );
            return fn.apply( this, arguments );
        }) : fn;

        if ( type === "unload" && name !== "one" ) {
            this.one( type, data, fn );

        } else {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                jQuery.event.add( this[i], type, handler, data );
            }
        }

        return this;
    };
});

Il y a beaucoup de choses intéressantes qui se passe ici, mais la partie qui nous intéresse est entre les lignes 2384 et 2388:

else {
    for ( var i = 0, l = this.length; i < l; i++ ) {
        jQuery.event.add( this[i], type, handler, data );
    }
}

Chaque fois que nous appelons bind() ou one() nous sommes en fait de faire un appel à l' jQuery.event.add() ... donc, nous allons jeter un oeil à ce que (les lignes de 1557 à 1672, si vous êtes intéressé)

add: function( elem, types, handler, data ) {
// ... snip ...
        var handleObjIn, handleObj;

        if ( handler.handler ) {
            handleObjIn = handler;
            handler = handleObjIn.handler;
        }

// ... snip ...

        // Init the element's event structure
        var elemData = jQuery.data( elem );

// ... snip ...

        var events = elemData.events = elemData.events || {},
            eventHandle = elemData.handle, eventHandle;

        if ( !eventHandle ) {
            elemData.handle = eventHandle = function() {
                // Handle the second event of a trigger and when
                // an event is called after a page has unloaded
                return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
                    jQuery.event.handle.apply( eventHandle.elem, arguments ) :
                    undefined;
            };
        }

// ... snip ...

        // Handle multiple events separated by a space
        // jQuery(...).bind("mouseover mouseout", fn);
        types = types.split(" ");

        var type, i = 0, namespaces;

        while ( (type = types[ i++ ]) ) {
            handleObj = handleObjIn ?
                jQuery.extend({}, handleObjIn) :
                { handler: handler, data: data };

            // Namespaced event handlers
                    ^
                    |
      // There is is! Even marked with a nice handy comment so you couldn't miss it 
      // (Unless of course you are not looking for it ... as I wasn't)

            if ( type.indexOf(".") > -1 ) {
                namespaces = type.split(".");
                type = namespaces.shift();
                handleObj.namespace = namespaces.slice(0).sort().join(".");

            } else {
                namespaces = [];
                handleObj.namespace = "";
            }

            handleObj.type = type;
            handleObj.guid = handler.guid;

            // Get the current list of functions bound to this event
            var handlers = events[ type ],
                special = jQuery.event.special[ type ] || {};

            // Init the event handler queue
            if ( !handlers ) {
                handlers = events[ type ] = [];

                   // ... snip ...

            }

                  // ... snip ...

            // Add the function to the element's handler list
            handlers.push( handleObj );

            // Keep track of which events have been used, for global triggering
            jQuery.event.global[ type ] = true;
        }

     // ... snip ...
    }

À ce moment, j'ai réalisé que la compréhension de ce qui se passait à durer plus de 30 minutes ... j'ai donc cherché à Stackoverflow pour

jquery get a list of all event handlers bound to an element

et trouvé cette réponse pour parcourir lié aux événements:

//log them to the console (firebug, ie8)
console.dir( $('#someElementId').data('events') );

//or iterate them
jQuery.each($('#someElementId').data('events'), function(i, event){

    jQuery.each(event, function(i, handler){

        console.log( handler.toString() );

    });

});

Les tests que dans Firefox, je vois que l' events objet dans l' data attribut de chaque élément a un [some_event_name] d'attribut (click dans notre cas) pour ce qui est attatched un tableau d' handler objets, dont chacun a un guid, d'un espace de nom, un type et un gestionnaire. "Donc", je pense", on devrait théoriquement être en mesure d'ajouter des objets construits de la même manière à l' [element].data.events.[some_event_name].push([our_handler_object); ... "

Et puis je vais terminer la rédaction de mes découvertes ... et de trouver une bien meilleure réponse posté par RusselUresti ... qui me présente à quelque chose de nouveau que je ne savais pas à propos de jQuery (même si j'ai été regarder dans le visage).

Qui est la preuve que Stackoverflow est le meilleur de questions-réponses sur le site internet, au moins, à mon humble avis.

Donc je poste ceci dans un souci de postérité ... et en marquant un wiki de la communauté, depuis RussellUresti déjà répondu à la question tellement bien.

5voto

Chris Chilvers Points 3882

Le principe de la norme est événement distinct des gestionnaires ne devrait pas dépendre de l’ordre qu’ils sont appelés. Si elles ne dépendent pas dans l’ordre de que ne pas être séparés.

Dans le cas contraire, vous enregistrez un gestionnaire d’événements comme étant « d’abord » et quelqu'un d’autre puis enregistre leur gestionnaire d’événements comme « première » et vous êtes de retour dans le même désordre comme avant.

4voto

Dunstkreis Points 96

.Data("Events") a été retirée de la version 1.9 et 2.0beta, donc tu ne peux pas plus compter sur ces solutions.

http://jQuery.com/Upgrade-Guide/1.9/#Data-quot-Events-quot-

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X