การเรียกกลับของ. animate () เรียกสองครั้ง jquery


104

เนื่องจากฉันเพิ่มscrollTop-animation บางส่วนของการโทรกลับของฉันถูกเรียกสองครั้ง:

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

ดูเหมือนว่าจะทำซ้ำ.show()อย่างน้อยฉันก็ไม่มีความรู้สึกว่าload()หรือถูก.fadeIn()เรียกเป็นครั้งที่สองด้วย .show()ได้รับซ้ำเร็วที่สุดเท่าที่จะเสร็จสิ้นเป็นครั้งแรก การตั้งค่าความเร็วภาพเคลื่อนไหว scrollTop 0ไม่ได้ช่วยอะไรเลย!

ฉันคิดว่ามันมีบางอย่างเกี่ยวข้องกับคิวภาพเคลื่อนไหว แต่ฉันไม่สามารถหาวิธีแก้ปัญหาได้โดยเฉพาะอย่างยิ่งว่าทำไมสิ่งนี้จึงเกิดขึ้น

คำตอบ:


163

animateเรียกการโทรกลับหนึ่งครั้งสำหรับแต่ละองค์ประกอบในชุดที่คุณเรียกanimateใช้:

หากจัดมาที่start, step, progress, complete, done, failและalwaysเรียกกลับจะเรียกว่าบนพื้นฐานต่อองค์ประกอบ ...

เนื่องจากคุณกำลังเคลื่อนไหวสององค์ประกอบ ( htmlองค์ประกอบและbodyองค์ประกอบ) คุณจะได้รับการเรียกกลับสองครั้ง (สำหรับใครที่สงสัยว่าเหตุใดOP จึงเคลื่อนไหวสององค์ประกอบนั่นเป็นเพราะแอนิเมชั่นทำงานบนbodyเบราว์เซอร์บางตัว แต่บนhtmlเบราว์เซอร์อื่น)

หากต้องการรับการโทรกลับเพียงครั้งเดียวเมื่อภาพเคลื่อนไหวเสร็จสมบูรณ์animateเอกสารจะชี้ให้คุณทราบโดยใช้promiseวิธีการรับสัญญาสำหรับคิวภาพเคลื่อนไหวจากนั้นใช้thenเพื่อจัดคิวการโทรกลับ:

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

(หมายเหตุ: Kevin B ชี้สิ่งนี้ในคำตอบของเขาเมื่อคำถามถูกถามครั้งแรกฉันไม่ได้ทำจนกระทั่งสี่ปีต่อมาเมื่อฉันสังเกตเห็นว่ามันหายไปเพิ่มเข้าไปและ ... จากนั้นก็เห็นคำตอบของ Kevin โปรดให้คำตอบของเขา รักที่สมควรได้รับฉันคิดว่านี่คือคำตอบที่ได้รับการยอมรับฉันควรปล่อยไว้)

นี่คือตัวอย่างที่แสดงทั้งการเรียกกลับของแต่ละองค์ประกอบและการโทรกลับโดยรวม:

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
html, bodyใช่ว่าเป็นเหตุผลและที่จริงผมจำไม่ได้ว่าทำไมผมเคยเขียน ถึงเวลาเปิดสมองของฉันอีกครั้ง ขอบคุณ
ไม่ระบุชื่อ

21
อาจเป็นเพราะการสร้างภาพเคลื่อนไหวเพียง html ไม่สามารถใช้งานได้ใน Webkit และร่างกายจะไม่ทำงานใน Opera การใช้ทั้งสองอย่างจะช่วยให้มั่นใจได้ว่าจะใช้งานได้เสมอ แต่จะเรียกการโทรกลับสองครั้งใน Firefox (ฉันอาจจะเบราว์เซอร์ผิด ... )
Jon Gjengset

2
htmlจำเป็นสำหรับ IE และการเรียกกลับจะเริ่มทำงานสองครั้งสำหรับเบราว์เซอร์อื่น ๆ ส่วนใหญ่ ฉันกำลังพยายามหาวิธีแก้ปัญหาเดียวกัน
Simon Robb

9
ฉันจัดการกับมันด้วยการสร้างแฟล็ก: var ranOne = false; $('body,html').animate({ scrollTop: scrollTo }, scrollTime, 'swing', function () { if (ranOne) { ...action... ranOne = false; } else { ranOne = true; } }); มันให้ความรู้สึกแฮ็ค แต่การต้องใช้ "body, html" เป็นแบบที่แฮ็คตั้งแต่แรกดังนั้น (ขออภัยที่ไม่มีการแบ่งบรรทัดเดาว่าความคิดเห็นไม่แสดง)
ปั่น

1
เหลือเชื่อ! ฉันใช้เวลาหลายชั่วโมงในการพยายามทำให้แอนิเมชันการเลื่อนของฉันทำงานได้อย่างถูกต้องกับ $ ("html, body") เพื่อที่จะไม่เริ่มทำงานซ้ำสองครั้งและคำตอบนี้คือสิ่งที่ช่วยฉัน! ขอบคุณ!
Vasily Hall

172

หากต้องการรับการโทรกลับเพียงครั้งเดียวเพื่อให้ภาพเคลื่อนไหวหลายองค์ประกอบเสร็จสมบูรณ์ให้ใช้ออบเจ็กต์ที่เลื่อนออกไป

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

ดู jQuery API สำหรับรายละเอียดของสัญญาและวัตถุรอตัดบัญชี


ปัญหานี้ได้รับการแก้ไขเกี่ยวกับสิ่งที่เราต้องการทำในแอนิเมชั่นที่เสร็จสิ้น แต่ปัญหาเกี่ยวกับการโทรสองครั้งและได้รับ +1 สำหรับสิ่งนั้น แต่ฉันไม่รู้ว่ากระบวนการสร้างภาพเคลื่อนไหวถูกเรียกสองครั้งหรือไม่?!
QMaster

1
กระบวนการสร้างภาพเคลื่อนไหวจะไม่ถูกเรียกสองครั้งการเรียกกลับจะถูกเรียกหนึ่งครั้งสำหรับแต่ละองค์ประกอบที่เลือก ดังนั้นหากคุณเลือก 8 รายการและเคลื่อนไหวไปทางซ้ายระบบจะดำเนินการโทรกลับ 8 ครั้ง โซลูชันของฉันให้คุณโทรกลับเพียงครั้งเดียวเมื่อทั้ง 8 เสร็จสิ้น
Kevin B

@KevinB ทางออกที่ดีและช่วยแก้ปัญหาการโทรกลับของฉันเองด้วย ขอบคุณ.
lislis

ฉันคิดว่านี่เป็นคำตอบที่น่าสนใจ แต่น่าเสียดายที่มันมีผลที่ไม่คาดคิด สัญญาไม่เพียงชะลอการเรียกกลับจนกว่าองค์ประกอบของชุด jQuery จะเสร็จสิ้นภาพเคลื่อนไหวปัจจุบัน (ซึ่งจะดีมาก) จะได้รับการแก้ไขหลังจากภาพเคลื่อนไหวที่รอดำเนินการทั้งหมดของชุดเสร็จสิ้นแล้วเท่านั้น ดังนั้นหากมีการตั้งค่าภาพเคลื่อนไหวที่สองในขณะที่ภาพแรกยังทำงานอยู่การเรียกกลับสำหรับภาพเคลื่อนไหวแรกจะไม่ทำงานจนกว่าภาพเคลื่อนไหวที่สองจะจบลง ดูถังนี้
hashchange

1
ถูกต้องรอให้ภาพเคลื่อนไหวปัจจุบันทั้งหมดเสร็จสมบูรณ์สำหรับองค์ประกอบที่เลือก ไม่ฉลาดพอที่จะรอเพียงคนที่เริ่มต้นในห่วงโซ่นี้ (หรือไม่ควรเป็น)
Kevin B
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.