Il callback di .animate () viene chiamato due volte jquery


103

Poiché ho aggiunto un po 'di scrollTopanimazione, alcune parti della mia richiamata vengono chiamate due volte:

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

Sembra solo ripetere il .show(), almeno non ho l'impressione che anche il load()o il venga .fadeIn()chiamato una seconda volta. Il .show()ottiene ripetuto non appena ha finito per la prima volta. L'impostazione della velocità di animazione scrollTop su 0non è stata d'aiuto!

Presumo che abbia qualcosa a che fare con la coda dell'animazione, ma non riesco a capire come trovare una soluzione alternativa e soprattutto perché sta accadendo.

Risposte:


162

animatechiama la sua richiamata una volta per ogni elemento dell'insieme su cui chiami animate:

Se in dotazione, il start, step, progress, complete, done, fail, e alwayscallback vengono chiamate su una base per-elemento ...

Poiché stai animando due elementi (l' htmlelemento e l' bodyelemento), ricevi due callback. (Per chiunque si chieda perché l'OP stia animando due elementi, è perché l'animazione funziona bodysu alcuni browser ma htmlsu altri browser.)

Per ottenere una singola richiamata quando l'animazione è completa, i animatedocumenti indicano l'utilizzo del promisemetodo per ottenere una promessa per la coda dell'animazione, quindi utilizzare thenper accodare la richiamata:

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

(Nota: Kevin B lo ha sottolineato nella sua risposta quando la domanda è stata posta per la prima volta. Non l'ho fatto fino a quattro anni dopo, quando ho notato che mancava, l'ho aggiunto e ... poi ho visto la risposta di Kevin. Per favore, dai la sua risposta al l'amore che merita. Ho pensato che questa è la risposta accettata, dovrei lasciarla.)

Ecco un esempio che mostra sia i callback dei singoli elementi sia il callback di completamento complessivo:

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>


2
Sì, quella era la ragione e in realtà non riesco a ricordare perché abbia mai scritto html, body. È ora di riaccendere il mio cervello. Grazie
Anonimo

21
Probabilmente perché l'animazione solo html non funzionerà in Webkit e solo body non funzionerà in Opera. L'utilizzo di entrambi assicurerà che funzioni sempre, ma attiverà la richiamata due volte in Firefox. (Potrei aver sbagliato i browser ...)
Jon Gjengset

2
htmlè richiesto per IE e il callback verrà attivato due volte per la maggior parte degli altri browser. Sto cercando di trovare una soluzione allo stesso problema.
Simon Robb

9
Ho risolto il problema creando una bandiera: var ranOne = false; $('body,html').animate({ scrollTop: scrollTo }, scrollTime, 'swing', function () { if (ranOne) { ...action... ranOne = false; } else { ranOne = true; } }); sembra hacky, ma dover usare "body, html" è un po 'hacky in primo luogo, quindi. (ech scusa per la mancanza di interruzioni di riga, suppongo che i commenti non li mostrino)
gira il

1
Incredibile! Ho passato ore a cercare di far funzionare correttamente la mia animazione a scorrimento con $ ("html, body") in modo che non si attivasse due volte, e questa risposta è ciò che mi ha salvato! Grazie!
Vasily Hall

172

Per ottenere un singolo callback per il completamento di più animazioni di elementi, utilizzare oggetti differiti.

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

Consulta l'API jQuery per una descrizione dettagliata degli oggetti Promise e Deferred .


Questo è un problema risolto su ciò che vogliamo fare al termine dell'animazione, ma il problema è stato chiamato due volte e ottenuto +1 per questo, ma non so se il processo animato viene chiamato due volte o no ?!
QMaster

1
Il processo animato non viene chiamato due volte, il callback viene chiamato una volta per ogni elemento selezionato. Quindi, se selezioni 8 voci dell'elenco e le animi a sinistra, la richiamata verrà eseguita 8 volte. La mia soluzione ti dà una singola richiamata per quando tutti e 8 sono finiti.
Kevin B

@KevinB, buona soluzione e ha aiutato a risolvere anche i miei problemi di richiamata. Grazie.
lislis

Ho pensato che fosse una risposta intrigante, ma sfortunatamente ha conseguenze non intenzionali. La promessa non si limita a ritardare l'esecuzione del callback fino a quando gli elementi del set jQuery non hanno terminato l'animazione corrente (il che sarebbe fantastico). Viene risolto solo dopo che tutte le animazioni in sospeso del set sono state completate. Quindi, se una seconda animazione viene impostata mentre la prima è ancora in esecuzione, il callback previsto per la prima animazione non viene eseguito fino al termine della seconda animazione. Vedi questo cestino .
hashchange

1
È corretto, attende il completamento di tutte le animazioni correnti per gli elementi selezionati. Non è abbastanza intelligente aspettare solo quelli iniziati in questa catena (né dovrebbe essere)
Kevin B
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.