web-dev-qa-db-fra.com

Invoquer une fonction jQuery après que .each () soit terminé

Dans jQuery, est-il possible de invoquer un rappel ou déclencher un événement après un appel de .each() (ou de tout autre type de rappel itératif) a terminé .

Par exemple, je voudrais que cette "fondu et supprimer" pour terminer

$(parentSelect).nextAll().fadeOut(200, function() {
    $(this).remove();
});

avant de faire des calculs et d'insérer des éléments new après la $(parentSelect). Mes calculs sont incorrects si jQuery reste visible pour les éléments existants et que dormir/retarder un laps de temps arbitraire (200 pour chaque élément) semble au mieux une solution fragile.

Je peux facilement .bind() la logique nécessaire à un rappel d'événement mais je ne suis pas sûr de savoir comment appeler proprement .trigger() après que l'itération ci-dessus a terminé. Évidemment, je ne peux pas invoquer le déclencheur à l'intérieur de l'itération, car il se déclencherait plusieurs fois.

Dans le cas de $.each(), j'ai envisagé d'ajouter quelque chose à la fin de l'argument data (que je chercherais manuellement dans le corps de l'itération) mais je détesterais être obligé de le faire, alors j'espérais qu'il y avait un autre manière élégante de contrôler le flux par rapport aux rappels itératifs.

170
Luther Baker

Une alternative à la réponse de @ tv:

var elems = $(parentSelect).nextAll(), count = elems.length;

elems.each( function(i) {
  $(this).fadeOut(200, function() { 
    $(this).remove(); 
    if (!--count) doMyThing();
  });
});

Notez que .each() est lui-même synchrone - l'instruction qui suit l'appel à .each() ne sera exécutée qu'une fois l'appel .each() terminé. Cependant, les opérations asynchrones started dans l'itération .each() continueront bien sûr à leur manière. C'est le problème ici: les appels à atténuer les éléments sont des animations pilotées par une minuterie, qui continuent à leur propre rythme.

La solution ci-dessus enregistre donc le nombre d’éléments atténués. Chaque appel à .fadeOut() reçoit un rappel d'achèvement. Lorsque le rappel signale qu'il est compté dans tous les éléments d'origine impliqués, certaines actions ultérieures peuvent être prises avec l'assurance que tout le fondu est terminé.

C'est une réponse vieille de quatre ans (à ce stade en 2014). Une façon moderne de procéder consisterait probablement à utiliser le mécanisme de différé/promesse, bien que la procédure ci-dessus soit simple et fonctionne parfaitement.

150
Pointy

C'est probablement trop tard, mais je pense que ce code fonctionne ...

$blocks.each(function(i, Elm) {
 $(Elm).fadeOut(200, function() {
  $(Elm).remove();
 });
}).promise().done( function(){ alert("All was done"); } );
154
Sébastien GRAVIER

Ok, c'est peut-être un peu plus tard, mais .promise () devrait aussi vous permettre d'atteindre vos objectifs.

Documentation Promise

Un exemple d'un projet sur lequel je travaille:

$( '.panel' )
    .fadeOut( 'slow')
    .promise()
    .done( function() {
        $( '#' + target_panel ).fadeIn( 'slow', function() {});
    });

:)

154
nbsp

JavaScript s'exécute de manière synchrone, de sorte que tout ce que vous placez après each() ne fonctionnera pas tant que each() n'est pas terminé.

Considérons le test suivant:

var count = 0;
var array = [];

// populate an array with 1,000,000 entries
for(var i = 0; i < 1000000; i++) {
    array.Push(i);
}

// use each to iterate over the array, incrementing count each time
$.each(array, function() {
    count++
});

// the alert won't get called until the 'each' is done
//      as evidenced by the value of count
alert(count);

Lorsque l'alerte est appelée, le nombre sera égal à 1000000, car l'alerte ne fonctionnera pas tant que each() n'est pas terminé.

20
user113716

J'ai trouvé beaucoup de réponses concernant les tableaux mais pas avec un objet json. Ma solution consistait simplement à parcourir l'objet une fois tout en incrémentant un compteur, puis, lors d'une itération à l'aide de l'objet pour exécuter votre code, vous pouvez incrémenter un deuxième compteur. Ensuite, il vous suffit de comparer les deux compteurs ensemble et d’obtenir votre solution. Je sais que c'est un peu maladroit, mais je n'ai pas encore trouvé de solution plus élégante. Voici mon exemple de code:

var flag1 = flag2 = 0;

$.each( object, function ( i, v ) { flag1++; });

$.each( object, function ( ky, val ) {

     /*
        Your code here
     */
     flag2++;
});

if(flag1 === flag2) {
   your function to call at the end of the iteration
}

Comme je l'ai dit, ce n'est pas le plus élégant, mais ça marche et ça marche bien et je n'ai pas encore trouvé de meilleure solution.

Salutations, JP

5
JimP

Si vous êtes prêt à faire quelques étapes, cela pourrait fonctionner. Cela dépend des animations se terminant dans l'ordre, cependant. Je ne pense pas que cela devrait être un problème.

var elems = $(parentSelect).nextAll();
var lastID = elems.length - 1;

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
           doMyThing();
        }
    });
});
1
tvanfosson

qu'en est-il de 

$(parentSelect).nextAll().fadeOut(200, function() { 
    $(this).remove(); 
}).one(function(){
    myfunction();
}); 
0
Mark Schultheiss

Peut-être une réponse tardive, mais il existe un paquet pour gérer cela https://github.com/ACFBentveld/Await

 var myObject = { // or your array
        1 : 'My first item',
        2 : 'My second item',
        3 : 'My third item'
    }

    Await.each(myObject, function(key, value){
         //your logic here
    });

    Await.done(function(){
        console.log('The loop is completely done');
    });
0
Wim Pruiksma

Vous devez mettre le reste de votre demande en file d'attente pour que cela fonctionne.

var elems = $(parentSelect).nextAll();
var lastID = elems.length - 1;

elems.each( function(i) {
    $(this).fadeOut(200, function() { 
        $(this).remove(); 
        if (i == lastID) {
            $j(this).queue("fx",function(){ doMyThing;});
        }
    });
});
0
Elyx0

Je rencontre le même problème et j'ai résolu avec une solution comme le code suivant:

var drfs = new Array();
var external = $.Deferred();
drfs.Push(external.promise());

$('itemSelector').each( function() {
    //initialize the context for each cycle
    var t = this; // optional
    var internal = $.Deferred();

    // after the previous deferred operation has been resolved
    drfs.pop().then( function() {

        // do stuff of the cycle, optionally using t as this
        var result; //boolean set by the stuff

        if ( result ) {
            internal.resolve();
        } else {
            internal.reject();
        }
    }
    drfs.Push(internal.promise());
});

external.resolve("done");

$.when(drfs).then( function() {
    // after all each are resolved

});

La solution résout le problème suivant: synchroniser les opérations asynchrones démarrées dans l'itération .each () à l'aide de l'objet Deferred.

0
Roberto AGOSTINO