Wywołanie zwrotne .animate () jest wywoływane dwukrotnie jQuery

103

Ponieważ dodałem trochę scrollTopanimacji, niektóre części mojego wywołania zwrotnego są wywoływane dwukrotnie:

$('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);                    
        });
    });
});

Wydaje się, że tylko się powtarza .show(), przynajmniej nie mam wrażenia, że load()albo ktoś dzwoni .fadeIn()po raz drugi. .show()Się powtarza, jak tylko zakończy się po raz pierwszy. Ustawienie szybkości animacji scrollTop na 0nie pomogło!

Zakładam, że ma to coś wspólnego z kolejką animacji, ale nie mogę znaleźć rozwiązania, a zwłaszcza dlaczego tak się dzieje.

Anonimowy
źródło

Odpowiedzi:

162

animatewywołuje swoje wywołanie zwrotne raz dla każdego elementu w przywoływanym zestawie animate:

Jeśli podany, start, step, progress, complete, done, fail, i alwayswywołania zwrotne są nazywane na podstawie na elemencie ...

Ponieważ animujesz dwa elementy ( htmlelement i bodyelement), otrzymujesz dwa wywołania zwrotne. (Dla każdego, kto zastanawia się, dlaczego OP animuje dwa elementy, dzieje się tak dlatego, że animacja działa bodyw niektórych przeglądarkach, ale htmlw innych)

Aby uzyskać pojedyncze wywołanie zwrotne po zakończeniu animacji, animatedokumenty wskazują, że należy użyć promisemetody, aby uzyskać obietnicę kolejki animacji, a następnie użyć thendo kolejkowania wywołania zwrotnego:

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

(Uwaga: Kevin B zwrócił na to uwagę w swojej odpowiedzi, kiedy zadawano to pytanie. Dopiero cztery lata później zauważyłem, że go brakuje, dodałem go i ... potem zobaczyłem odpowiedź Kevina. Proszę, podaj jego odpowiedź uwielbiam, na to zasługuje. Pomyślałem, że skoro jest to akceptowana odpowiedź, powinienem ją zostawić).

Oto przykład pokazujący zarówno wywołania zwrotne poszczególnych elementów, jak i ogólne wywołanie zwrotne zakończenia:

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
źródło
2
Tak, to był powód i właściwie nie mogę sobie przypomnieć, dlaczego kiedykolwiek pisałem html, body. Czas z powrotem włączyć mój mózg. Dzięki
Anonimowy
21
Prawdopodobnie dlatego, że animowanie samego html nie zadziała w Webkicie, a samo body nie będzie działać w Operze. Użycie obu zapewni, że zawsze będzie działać, ale spowoduje dwukrotne wywołanie zwrotne w przeglądarce Firefox. (Być może źle zrozumiałem przeglądarki ...)
Jon Gjengset
2
htmljest wymagany dla IE, a wywołanie zwrotne zostanie uruchomione dwukrotnie w przypadku większości innych przeglądarek. Próbuję znaleźć rozwiązanie tego samego problemu.
Simon Robb,
9
Poradziłem sobie z tym, tworząc flagę: var ranOne = false; $('body,html').animate({ scrollTop: scrollTo }, scrollTime, 'swing', function () { if (ranOne) { ...action... ranOne = false; } else { ranOne = true; } }); wydaje się, że to hacky, ale konieczność używania „body, html” jest po pierwsze trochę hakerska, więc. (ech przepraszam za brak
podziałów
1
Nie do wiary! Spędziłem wiele godzin próbując, aby moja animacja przewijania działała poprawnie z $ („html, body”), tak aby nie uruchomiła się dwukrotnie, a ta odpowiedź mnie uratowała! Dzięki!
Vasily Hall
172

Aby uzyskać pojedyncze wywołanie zwrotne w celu zakończenia animacji wielu elementów, użyj obiektów odroczonych.

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

Zobacz interfejs API jQuery, aby uzyskać szczegółowy opis obiektów Promise i Deferred .

Kevin B.
źródło
To jest rozwiązany problem dotyczący tego, co chcemy zrobić po zakończeniu animacji, ale wyślij około dwukrotnie połączenie i dostałem +1 za to, ale nie wiem, czy animowany proces jest wywoływany dwukrotnie, czy nie ?!
QMaster
1
Proces animacji nie jest wywoływany dwukrotnie, wywołanie zwrotne jest wywoływane raz dla każdego wybranego elementu. Tak więc, jeśli wybierzesz 8 pozycji listy i animujesz je w lewo, wywołanie zwrotne zostanie wykonane 8 razy. Moje rozwiązanie zapewnia jedno oddzwonienie po zakończeniu wszystkich 8.
Kevin B
@KevinB, dobre rozwiązanie i pomogło rozwiązać również moje własne problemy z oddzwonieniem. Dzięki.
lislis
Pomyślałem, że to intrygująca odpowiedź, ale niestety ma niezamierzone konsekwencje. Obietnica nie tylko opóźnia wykonanie wywołania zwrotnego, dopóki elementy zestawu jQuery nie zakończą bieżącej animacji (co byłoby świetne). Rozwiązuje się go dopiero po wykonaniu wszystkich oczekujących animacji zestawu. Jeśli więc druga animacja jest skonfigurowana, gdy pierwsza jest nadal uruchomiona, wywołanie zwrotne przeznaczone dla pierwszej animacji zostanie uruchomione dopiero po zakończeniu drugiej animacji. Zobacz ten kosz .
hashchange
1
To prawda, czeka na zakończenie wszystkich bieżących animacji dla wybranych elementów. Nie wystarczy czekać tylko na tych, którzy zaczęli w tym łańcuchu (ani nie powinno być)
Kevin B