Обратный вызов .animate () вызывается дважды jquery

104

Поскольку я добавил некоторую scrollTop-анимацию, некоторые части моего обратного вызова вызываются дважды:

$('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 animation-speed значения 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
    });

(Примечание: Кевин Б. указал на это в своем ответе, когда вопрос был впервые задан. Я не сделал этого до четырех лет спустя, когда заметил, что он отсутствует, добавил его и ... затем увидел ответ Кевина. Пожалуйста, дайте его ответ люблю это заслуживает. Я подумал, так как это принятый ответ, я должен оставить его.)

Вот пример, показывающий как обратные вызовы отдельных элементов, так и общий обратный вызов завершения:

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
источник
2
Да, это была причина, и на самом деле я не могу вспомнить, зачем я вообще писал html, body. Пора снова включить мой мозг. Спасибо
Anonymous
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; } }); это кажется хакерским, но необходимость использовать "body, html" - это в первую очередь хакерство, так что. (эх извините за отсутствие разрывов строк, думаю, в комментариях их не видно)
spinn
1
Невероятно! Я часами пытался заставить мою анимацию прокрутки правильно работать с $ ("html, body"), чтобы она не срабатывала дважды, и именно этот ответ меня спас! Спасибо!
Василий Холл
172

Чтобы получить один обратный вызов для завершения анимации нескольких элементов, используйте отложенные объекты.

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

Подробное описание обещаний и отложенных объектов см. В jQuery API .

Кевин Б.
источник
Это решенная проблема о том, что мы хотим сделать после завершения анимации, но проблема с двойным вызовом и получением +1 для этого, но я не знаю, вызывается ли процесс анимации дважды или нет ?!
QMaster
1
Процесс анимации не вызывается дважды, обратный вызов вызывается один раз для каждого выбранного элемента. Итак, если вы выберете 8 элементов списка и оживите их слева, обратный вызов будет выполнен 8 раз. Мое решение дает вам один обратный вызов, когда все 8 будут выполнены.
Кевин Би
@KevinB, хорошее решение, и оно помогло решить и мои собственные проблемы с обратным вызовом. Спасибо.
Лизлис
Я подумал, что это был интригующий ответ, но, к сожалению, он имел непредвиденные последствия. Обещание не просто откладывает выполнение обратного вызова до тех пор, пока элементы набора jQuery не завершат текущую анимацию (что было бы здорово). Он разрешается только после того, как будут выполнены все ожидающие анимации набора. Таким образом, если вторая анимация настроена во время работы первой, обратный вызов, предназначенный для первой анимации, не будет запущен до завершения второй анимации. Смотрите эту корзину .
hashchange
1
Это правильно, он ожидает завершения всех текущих анимаций для выбранных элементов. Недостаточно умен, чтобы ждать только тех, которые были запущены в этой цепочке (да и не должно быть)
Кевин Би