Callback dari .animate () dipanggil dua kali jquery


103

Karena saya menambahkan beberapa scrollTop-animasi, beberapa bagian dari panggilan balik saya dipanggil dua kali:

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

Sepertinya hanya mengulangi .show(), setidaknya saya tidak memiliki kesan bahwa load()atau .fadeIn()dipanggil untuk kedua kalinya juga. The .show()akan diulang segera setelah selesai untuk pertama kalinya. Menyetel kecepatan animasi scrollTop 0tidak membantu!

Saya berasumsi ini ada hubungannya dengan antrian animasi, tetapi saya tidak tahu cara menemukan solusi dan terutama mengapa hal ini terjadi.

Jawaban:


162

animatememanggil callback-nya sekali untuk setiap elemen di set yang Anda panggil animate:

Jika diberikan, start, step, progress, complete, done, fail, dan alwayscallback disebut pada basis per-elemen ...

Karena Anda menganimasikan dua elemen ( htmlelemen, dan bodyelemen), Anda mendapatkan dua callback. (Bagi siapa pun yang bertanya - tanya mengapa OP menganimasikan dua elemen, itu karena animasi berfungsi bodydi beberapa browser tetapi di htmlbrowser lain.)

Untuk mendapatkan satu callback saat animasi selesai, animatedokumen mengarahkan Anda menggunakan promisemetode untuk mendapatkan janji untuk antrean animasi, lalu menggunakan thenuntuk mengantrekan callback:

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

(Catatan: Kevin B menunjukkan hal ini dalam jawabannya ketika pertanyaan pertama kali diajukan. Saya tidak melakukannya sampai empat tahun kemudian ketika saya melihat itu hilang, menambahkannya, dan ... kemudian melihat jawaban Kevin. Tolong berikan jawabannya cinta itu pantas. Saya pikir karena ini adalah jawaban yang diterima, saya harus membiarkannya masuk.)

Berikut adalah contoh yang menampilkan callback elemen individual, dan callback penyelesaian keseluruhan:

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
Ya itu alasannya dan sebenarnya saya tidak ingat mengapa saya pernah menulis html, body. Saatnya menghidupkan kembali otakku. Terima kasih
Anonim

21
Mungkin karena menganimasikan hanya html tidak akan berfungsi di Webkit, dan hanya body tidak akan berfungsi di Opera. Menggunakan keduanya akan memastikannya selalu berfungsi, tetapi akan memicu panggilan balik dua kali di Firefox. (Mungkin browser saya salah ...)
Jon Gjengset

2
htmldiperlukan untuk IE, dan panggilan balik akan diaktifkan dua kali untuk sebagian besar browser lain. Saya mencoba mencari solusi untuk masalah yang sama.
Simon Robb

9
Saya mengatasinya dengan membuat sebuah bendera: var ranOne = false; $('body,html').animate({ scrollTop: scrollTo }, scrollTime, 'swing', function () { if (ranOne) { ...action... ranOne = false; } else { ranOne = true; } }); Rasanya seperti hacky, tetapi harus menggunakan "body, html" itu agak hacky, jadi. (ech maaf karena kurangnya jeda baris, tebak komentar tidak menampilkannya)
berputar

1
Luar biasa! Saya menghabiskan waktu berjam-jam mencoba membuat animasi scrolling saya berfungsi dengan baik dengan $ ("html, body") sehingga tidak akan menyala dua kali, dan jawaban inilah yang menyelamatkan saya! Terima kasih!
Vasily Hall

172

Untuk mendapatkan satu callback untuk penyelesaian beberapa animasi elemen, gunakan objek yang ditangguhkan.

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

Lihat API jQuery untuk penjelasan mendetail tentang Objek Janji dan Ditunda .


Ini memecahkan masalah tentang apa yang ingin kita lakukan pada animasi selesai tetapi masalah sekitar dua kali panggilan dan mendapat +1 untuk itu, tetapi saya tidak tahu apakah proses animasi dipanggil dua kali atau tidak ?!
QMaster

1
Proses animasi tidak dipanggil dua kali, callback dipanggil sekali untuk setiap elemen yang dipilih. Jadi, jika Anda memilih 8 item daftar dan menganimasikannya ke kiri, callback akan dijalankan 8 kali. Solusi saya memberi Anda satu panggilan balik ketika semua 8 selesai.
Kevin B

@KevinB, solusi yang bagus, dan itu membantu menyelesaikan masalah panggilan balik saya sendiri juga. Terima kasih.
lislis

Saya pikir ini adalah jawaban yang menarik, tetapi sayangnya itu memiliki konsekuensi yang tidak diinginkan. Promise tidak hanya menunda eksekusi callback sampai elemen set jQuery menyelesaikan animasi saat ini (yang akan sangat bagus). Ini diselesaikan hanya setelah semua animasi yang tertunda dari set selesai. Jadi, jika animasi kedua disiapkan saat animasi pertama masih berjalan, callback yang dimaksudkan untuk animasi pertama tidak akan berjalan hingga animasi kedua selesai. Lihat tempat sampah ini .
hashchange

1
Itu benar, itu menunggu semua animasi saat ini selesai untuk elemen yang dipilih. Tidaklah cukup pintar untuk hanya menunggu yang dimulai dalam rantai ini (juga seharusnya tidak)
Kevin B
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.