.each()の完了後にjQuery関数を呼び出す


183

jQueryでは、の呼び出し(または他のタイプの反復コールバック)が完了した後で、コールバック呼び出したり、イベントをトリガーし.each()たりできます。

たとえば、この「フェードアンド削除」を完了させたい

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

いくつかの計算を行い、の後に新しい要素を挿入する前$(parentSelect)。既存の要素がjQueryにまだ表示されていて、任意の時間(要素ごとに200)をスリープ/遅延させることが、せいぜい壊れやすい解決策のように見える場合、私の計算は正しくありません。

.bind()イベントコールバックに必要なロジックを簡単に作成できますが.trigger()、上記の反復が完了した後で、をきれいに呼び出す方法がわかりません。明らかに、反復内でトリガーを呼び出すことはできません。トリガーが複数回起動するためです。

の場合$.each()、データ引数の最後に何かを追加することを検討しました(手動で反復本体を検索します)が、それを強制されたくないので、他にエレガントなものがあることを望んでいました反復的なコールバックに関してフローを制御する方法。


1
終了したいのは「.each()」自体ではなく、「。each()」呼び出しによって起動されるすべてのアニメーションであることを正しく理解していますか?
Pointy

それは良い点です。この特定の質問については、はい、私は主に ".each()"自体の完了について懸念しています...しかし、別の実行可能な質問を提起すると思います。
Luther Baker

回答:


161

@tvの答えの代替:

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

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

それ.each()自体は同期的であることに注意してください。呼び出しの.each()後に続くステートメント.each()は、呼び出しが完了した後にのみ実行されます。ただし、反復で開始された非同期操作.each()は、当然、独自の方法で続行されます。それがここでの問題です。要素をフェードするための呼び出しはタイマー駆動のアニメーションであり、それらは独自のペースで続行します。

したがって、上記のソリューションは、フェードされている要素の数を追跡します。への各呼び出し.fadeOut()は、完了コールバックを取得します。コールバックが、含まれているすべての元の要素を介してカウントされていることに気付いた場合、すべてのフェードが終了したことを確信を持って後続のアクションを実行できます。

これは4年前の回答です(現時点では2014年)。これを行う最新の方法は、おそらく遅延/約束メカニズムを使用することになるでしょうが、上記は単純であり、うまく機能するはずです。


4
私はこの変更が好きですが、カウントダウンしているので、論理否定(--count == 0)ではなく0と比較します。私にはそれは同じ効果を持っていますが、意図をより明確にします。
tvanfosson 2010年

結局のところ、質問に対するあなたの最初のコメントは適切でした。私は.each()について尋ねていて、それが私が欲しかったものだと思っていましたが、あなたとtvanfossonと今パトリックが指摘したように、私が実際に興味を持ったのは最後のfadeOutでした。インデックス)はおそらく最も安全です。
Luther Baker

@ A.DIMO「複雑すぎる」とはどういう意味ですか?どの部分が複雑ですか?
2014年

169

おそらく遅くなるでしょうが、このコードはうまくいくと思います...

$blocks.each(function(i, elm) {
 $(elm).fadeOut(200, function() {
  $(elm).remove();
 });
}).promise().done( function(){ alert("All was done"); } );

156

わかりました、これは事実の少し後かもしれませんが、.promise()はあなたが望んでいることも達成するはずです。

Promiseのドキュメント

私が取り組んでいるプロジェクトの例:

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

:)


8
.promise()は.each()では使用できません
Michael Fever

25

JavaScriptは同期して実行されるため、その後each()に配置したものeach()はすべて、完了するまで実行されません。

次のテストを検討してください。

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);

アラートが呼び出されると、アラートeach()は完了するまで実行されないため、countは1000000になります。


8
与えられた例の問題は、fadeOutがアニメーションキューに置かれ、同期的に実行されないことです。を使用しeachて各アニメーションを個別にスケジュールする場合でも、それぞれが終了するのではなく、アニメーションの後にイベントを発生させる必要があります。
tvanfosson 2010年

3
@tvanfosson-ええ、私は知っています。Lutherが達成したいことによると、あなたのソリューション(およびPointyのソリューション)は適切なアプローチのようです。しかし、Lutherのコメントから... 単純に、私はelems.each(foo、bar)のようなものを探しています。...彼はそれがeach()問題であると主張しているようです。だから私はこの答えをミックスに投入しました。
user113716 2010年

あなたは正しいパトリックです。.each()が私のコールバックをスケジュールまたはキューイングしていることを(誤)理解しました。fadeOutを呼び出したことが間接的に私をその結論に導いたと思います。tvanfossonとPointyはどちらもfadeOut問題(私が本当に求めていた問題)に対する回答を提供してくれましたが、あなたの投稿は実際に私の理解を少し修正しています。ポインティとtvanfossonが私が尋ねようとしていた質問に答えている間、あなたの答えは実際に述べられているように元の質問に最もよく答えます。2つの答えが選べればいいのに。「この回答をミックスに投入」していただきありがとうございます:)
Luther Baker

@ user113716私は内部の関数eachが以前に呼び出されることが保証されているわけではないと思いましたalert。これについての説明を私に説明したり、指摘してもらえますか?
Maciej Jankowski 14

5

jsonオブジェクトではなく配列を処理する多くの応答が見つかりました。私の解決策は、カウンターをインクリメントしながらオブジェクトを1回反復し、オブジェクトを反復してコードを実行するときに、2番目のカウンターをインクリメントすることでした。次に、2つのカウンターを比較して、ソリューションを取得します。私はそれが少し不格好であることを知っていますが、これまでよりエレガントな解決策を見つけていません。これは私のサンプルコードです:

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
}

私が言ったように、それは最もエレガントではありませんが、それは機能し、うまく機能し、まだより良い解決策を見つけていません。

乾杯、JP


1
いいえ、これは機能しません。それはすべての.eachを起動し、次に最後のものを起動し、すべてが同時に実行されます。.eachにsetTimeoutを設定してみると、すぐにflag1 === flag2が実行されることがわかります
Michael Fever

1

あなたがそれをいくつかのステップにしたいと思っているなら、これはうまくいくかもしれません。ただし、アニメーションが順番に終了するかどうかに依存します。それは問題ではないと思います。

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

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

これはうまくいくでしょう-しかし、私が望んでいたものではありません。単純に、私はelems.each(foo、bar)のようなものを探しています。ここで、foo = '関数が各要素に適用され、bar ='すべての反復が完了した後にコールバックされます '。jQueryの暗いコーナーに出くわしたかどうかを確認するために、もう少し時間をかけて質問します:)
Luther Baker

AFAIK-「最後の」アニメーションが完了したときにトリガーする必要があり、非同期で実行されるため、アニメーションコールバックで実行する必要があります。@Pointyの方が私が書いたもののバージョンが好きです。順序に依存しないためです。問題になるとは思いません。
tvanfosson

0

どうですか

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

これは間違いなくmyfunction()を1回呼び出します(そして別のjQueryの概念を紹介します)が、私が探していたのは、いつ実行されるかを保証することです。そして、パトリックが述べているように、その部分はかなり簡単であることがわかります。私が本当に探していたのは、最後のfadeOutロジックの実行後に何かを呼び出す方法でした。これは.one()が保証するものではないと思います。
Luther Baker

最初の部分が最後の部分の前に実行されるため、チェーンはそれを行うと思います。
Mark Schultheiss

0

それが機能するためには、残りのリクエストをキューに入れる必要があります。

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

私は同じ問題に遭遇し、次のコードのような解決策で解決しました:

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

});

解決策は、次の問題を解決します。Deferredオブジェクトを使用して、.each()反復で開始された非同期操作を同期します。


前に閉じ括弧)がありません:drfs.push(internal.promise()); 少なくとも私はその前に思う..
マイケル・フィーバー

0

おそらく遅い応答ですが、これを処理するためのパッケージがあり ます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

私はこのようなものを使用しています:

$.when(
           $.each(yourArray, function (key, value) {
                // Do Something in loop here
            })
          ).then(function () {
               // After loop ends.
          });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.