Der Rückruf von .animate () wird zweimal jquery aufgerufen

103

Da ich eine scrollTopAnimation hinzugefügt habe, werden einige Teile meines Rückrufs zweimal aufgerufen:

$('html, body').animate({scrollTop: '0px'}, 300,function() {
    $('#content').load(window.location.href, postdata, function() {                 
        $('#step2').addClass('stepactive').hide().fadeIn(700, function() {
            $('#content').show('slide',800);                    
        });
    });
});

Es scheint nur das zu wiederholen .show(), zumindest habe ich nicht den Eindruck, dass das load()oder das .fadeIn()auch ein zweites Mal aufgerufen wird. Das .show()wird wiederholt, sobald es zum ersten Mal beendet ist. Das Einstellen der scrollTop-Animationsgeschwindigkeit auf 0hat übrigens nicht geholfen!

Ich nehme an, es hat etwas mit der Animationswarteschlange zu tun, aber ich kann nicht herausfinden, wie man eine Problemumgehung findet und insbesondere, warum dies geschieht.

Anonym
quelle

Antworten:

162

animateruft seinen Rückruf einmal für jedes Element in der Menge auf, die Sie aufrufen animate:

Wenn geliefert, die start, step, progress, complete, done, fail, und alwaysRückrufe werden auf einer gerufenen pro-Element - Basis ...

Da Sie zwei Elemente (das htmlElement und das bodyElement) animieren , erhalten Sie zwei Rückrufe. (Wenn Sie sich fragen, warum das OP zwei Elemente animiert, funktioniert die Animation bodyin einigen Browsern, htmlin anderen Browsern jedoch.)

Um einen einzelnen Rückruf zu erhalten, wenn die Animation abgeschlossen ist, animateweisen Sie die Dokumente darauf hin, dass Sie die promiseMethode verwenden, um ein Versprechen für die Animationswarteschlange zu erhalten, und dann thenden Rückruf in die Warteschlange stellen:

$("html, body").animate(/*...*/)
    .promise().then(function() {
        // Animation complete
    });

(Anmerkung: Kevin B hat in seiner Antwort darauf hingewiesen , als die Frage zum ersten Mal gestellt wurde. Ich habe es erst vier Jahre später bemerkt, als ich bemerkte, dass sie fehlte, sie hinzugefügt und ... dann Kevins Antwort gesehen. Bitte geben Sie seiner Antwort die Liebe, die es verdient. Ich dachte, da dies die akzeptierte Antwort ist, sollte ich es belassen.)

Hier ist ein Beispiel, das sowohl die Rückrufe einzelner Elemente als auch den Rückruf zur vollständigen Fertigstellung zeigt:

jQuery(function($) {

  $("#one, #two").animate({
    marginLeft: "30em"
  }, function() {
    // Called per element
    display("Done animating " + this.id);
  }).promise().then(function() {
    // Called when the animation in total is complete
    display("Done with animation");
  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }
});
<div id="one">I'm one</div>
<div id="two">I'm two</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

TJ Crowder
quelle
2
Ja, das war der Grund und eigentlich kann ich mich nicht erinnern, warum ich jemals geschrieben habe html, body. Zeit, mein Gehirn wieder einzuschalten. Danke
Anonym
21
Wahrscheinlich, weil das Animieren von nur HTML in Webkit nicht funktioniert und nur der Körper in Opera nicht funktioniert. Wenn Sie beide verwenden, wird sichergestellt, dass es immer funktioniert, der Rückruf wird jedoch in Firefox zweimal ausgelöst. (Ich habe möglicherweise die Browser falsch verstanden ...)
Jon Gjengset
2
htmlist für den Internet Explorer erforderlich, und der Rückruf wird für die meisten anderen Browser zweimal ausgelöst. Ich versuche, eine Lösung für das gleiche Problem zu finden.
Simon Robb
9
Ich habe mich damit befasst, indem ich eine Flagge erstellt habe: var ranOne = false; $('body,html').animate({ scrollTop: scrollTo }, scrollTime, 'swing', function () { if (ranOne) { ...action... ranOne = false; } else { ranOne = true; } }); Es fühlt sich hackig an, aber "body, html" verwenden zu müssen, ist in erster Linie etwas hackig. (Ech Entschuldigung für das Fehlen von Zeilenumbrüchen, erraten Kommentare zeigen sie nicht)
Spinn
1
Unglaublich! Ich habe stundenlang versucht, meine Bildlaufanimation mit $ ("html, body") richtig funktionieren zu lassen, damit sie nicht zweimal ausgelöst wird, und diese Antwort hat mich gerettet! Vielen Dank!
Vasily Hall
172

Verwenden Sie zurückgestellte Objekte, um einen einzelnen Rückruf für die Fertigstellung von Animationen mit mehreren Elementen zu erhalten.

$(".myClass").animate({
  marginLeft: "30em"
}).promise().done(function(){
  alert("Done animating");
});

In der jQuery-API finden Sie eine detaillierte Beschreibung der Promise- und Deferred-Objekte .

Kevin B.
quelle
Dies ist ein gelöstes Problem darüber, was wir nach Abschluss der Animation tun möchten, aber ungefähr zweimal aufrufen und +1 dafür bekommen, aber ich weiß nicht, ob der Animationsprozess zweimal aufgerufen wird oder nicht?!
QMaster
1
Der Animationsprozess wird nicht zweimal aufgerufen, der Rückruf wird für jedes ausgewählte Element einmal aufgerufen. Wenn Sie also 8 Listenelemente auswählen und links animieren, wird der Rückruf achtmal ausgeführt. Meine Lösung gibt Ihnen einen einzigen Rückruf, wenn alle 8 fertig sind.
Kevin B
@ KevinB, gute Lösung, und es half auch, meine eigenen Rückrufprobleme zu lösen. Vielen Dank.
Lislis
Ich dachte, dies sei eine faszinierende Antwort, aber leider hat dies unbeabsichtigte Konsequenzen. Das Versprechen verzögert nicht nur die Ausführung von Rückrufen, bis die Elemente des jQuery-Sets die aktuelle Animation beendet haben (was großartig wäre). Es wird erst aufgelöst, nachdem alle anstehenden Animationen des Sets abgeschlossen sind. Wenn also eine zweite Animation eingerichtet wird, während die erste noch ausgeführt wird, wird der für die erste Animation vorgesehene Rückruf erst ausgeführt, nachdem die zweite Animation beendet ist. Siehe diesen Behälter .
Hashchange
1
Das ist richtig, es wartet darauf, dass alle aktuellen Animationen für die ausgewählten Elemente abgeschlossen sind. Es ist nicht klug genug, nur auf diejenigen zu warten, die in dieser Kette begonnen haben (und sollte es auch nicht sein)
Kevin B