.animate()的回调被两次调用


103

由于添加了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()get 的印象。在.show()得到尽快,因为它已经完成了第一次重复。0顺便说一下,将scrollTop动画速度设置为并没有帮助!

我认为它与动画队列有关,但是我不知道如何找到解决方法,尤其是为什么会这样。

Answers:


162

animate为您调用的集合中的每个元素调用一次其回调animate

如果提供的startstepprogresscompletedonefail,和always回调称为对每个元素的基础上 ...

由于您要对两个元素(html元素和body元素)设置动画,因此您将获得两个回调。(对于任何想知道OP为什么要动画两个元素的人来说,这是因为动画可以body在某些浏览器上工作,但html在其他浏览器上工作。)

要在动画完成时获得单个回调,animate文档会为您指出使用promise方法来获得动画队列的承诺,然后使用then将该回调排队:

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

(请注意:凯文B 第一次提出问题时就在他的回答中指出了这一点。直到4年后我才发现它不见了,然后才添加了,然后才看到凯文的回答。请在我认为这是可以接受的答案,我应该留在里面。)

这是一个显示单个元素回调和整体完成回调的示例:

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中不起作用,而对body而言在Opera中则不起作用。两者都使用将确保其始终有效,但是将在Firefox中触发两次回调。(我可能
误解

2
html对于IE是必需的,并且对于大多数其他浏览器,回调将触发两次。我正在尝试解决同一问题。
西蒙·罗布

9
我通过创建一个标志来处理它: var ranOne = false; $('body,html').animate({ scrollTop: scrollTo }, scrollTime, 'swing', function () { if (ranOne) { ...action... ranOne = false; } else { ranOne = true; } }); 感觉很hacky,但是首先使用“ body,html”有点hacky,所以。(非常抱歉,缺少换行符,猜测注释未显示这些注释)
2013年

1
难以置信的!我花了几个小时尝试使$(“ html,body”)的滚动动画正常工作,以使它不会触发两次,而这个答案为我节省了很多!谢谢!
瓦西里大厅

172

要获得完成多个元素动画的单个回调,请使用延迟的对象。

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

有关PromiseDeferred对象的详细说明,请参见jQuery API 。


解决了有关我们希望在完成动画时要做什么的问题,但是发出了两次调用并为此获得了+1,但是我不知道动画过程是否被调用了两次?
QMaster 2014年

1
动画过程不会被调用两次,每个选定元素的回调都会被调用一次。因此,如果选择8个列表项并将它们设置为左侧动画,则回调将被执行8次。我的解决方案为您完成所有8个操作提供了一个回调。
凯文B

@KevinB,很好的解决方案,它也帮助解决了我自己的回调问题。谢谢。
lislis 2015年

我以为这是一个有趣的答案,但不幸的是,它产生了意想不到的后果。许诺不仅会延迟回调执行,直到jQuery集的元素完成当前动画为止(这会很棒)。仅在完成所有待处理的动画集后才能解决此问题。因此,如果在第一个动画仍在运行时设置了第二个动画,则在第二个动画结束之前,不会运行用于第一个动画的回调。看到这个垃圾箱
hashchange

1
没错,它等待所有当前动画针对所选元素完成。仅等待此链中开始的那些不够聪明(也不应该如此)
Kevin B
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.