2010-03-01 4 views
153

В JQuery, можно ли вызвать обратный вызов или триггера событие после вызова .each() (или любого другого типа итеративного обратного вызова) имеют заполненную ,Вызова функции JQuery после .each() завершила

Например, я хотел бы это «замирание и удалить», чтобы завершить

$(parentSelect).nextAll().fadeOut(200, function() { 
    $(this).remove(); 
}); 

, прежде чем делать какие-то расчеты и вставив новые элементы после $(parentSelect). Мои вычисления неверны, если существующие элементы все еще видны jQuery и спят/задерживают какое-то произвольное количество времени (200 для каждого элемента), в лучшем случае, как хрупкое решение.

я могу легко .bind() необходимую логику для обратного вызова события, но я не знаю, как чисто вызвать .trigger() после предыдущей итерации имеет завершенного. Очевидно, я не могу вызвать триггер внутри итерации, поскольку он срабатывает многократно.

В случае $.each(), я рассмотрел вопрос о добавлении чего-то в конец аргумента данных (который я вручную искал в теле итерации), но я бы не хотел, чтобы меня это принудили, поэтому я надеялся, был и другой элегантный способ контролировать поток в отношении итеративных обратных вызовов.

+1

Я правильно понимаю, что это не столько «.each()», что вы хотите закончить, а скорее все анимации, запущенные вызовом «.each()»? – Pointy

+0

Это хороший момент. С этим конкретным вопросом, да, меня в основном беспокоит завершение «.each()» ... но я думаю, что вы поднимаете еще один жизнеспособный вопрос. –

ответ

140

Альтернатива ответа @ телевизор:

var elems = $(parentSelect).nextAll(), count = elems.length; 

elems.each(function(i) { 
    $(this).fadeOut(200, function() { 
    $(this).remove(); 
    if (!--count) doMyThing(); 
    }); 
}); 

Обратите внимание, что .each() сама синхронного — утверждение, что следует вызову .each() будет выполняться только после того, как .each() вызова завершен. Однако асинхронные операции начались с в .each(). Итерация, конечно же, продолжится по-своему. Вот в чем проблема: призывы к стиранию элементов - анимация с таймером, и они продолжаются в своем собственном темпе.

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

Это четырехлетний ответ (на данный момент в 2014 году). Современный способ сделать это, вероятно, потребует использования механизма «Отсрочка/обещание», хотя приведенное выше простое и должно работать нормально.

+4

Мне нравится это изменение, хотя я бы сделал сравнение с 0 вместо логического отрицания (--count == 0), так как вы отсчитываете. Для меня это делает цель более четкой, хотя она имеет тот же эффект. – tvanfosson

+0

Как оказалось, ваш первоначальный комментарий к вопросу был на месте. Я спрашивал о .each() и думал, что это то, что я хотел, но как вы и tvanfosson, а теперь patrick указали - это был последний fadeOut, который меня действительно интересовал. Я думаю, мы все согласны с тем, что ваш пример (вместо этого индексов), вероятно, является самым безопасным. –

+0

@ A.DIMO Что значит «слишком сложно»? Какая часть сложна? – Pointy

1

Если вы готовы сделать это несколько шагов, это может сработать. Тем не менее, это зависит от анимации, заканчивающейся в порядке. Я не думаю, что это должно быть проблемой.

var elems = $(parentSelect).nextAll(); 
var lastID = elems.length - 1; 

elems.each(function(i) { 
    $(this).fadeOut(200, function() { 
     $(this).remove(); 
     if (i == lastID) { 
      doMyThing(); 
     } 
    }); 
}); 
+0

Это сработало бы - хотя, не то, что я надеялся. Наивно, я ищу что-то вроде elems.each (foo, bar), где foo = 'функция применяется к каждому элементу' и bar = 'обратный вызов после завершения всех итераций. Я задам вопрос немного больше времени, чтобы узнать, попадаем ли мы в какой-то темный угол jQuery :) –

+0

AFAIK - вам нужно вызвать его, когда завершается «последняя» анимация, и поскольку они выполняются асинхронно, вам нужно будет сделать это в обратном вызове анимации. Мне нравится версия @ Pointy о том, что я написал лучше, поскольку у нее нет зависимости от заказа, а не то, что я думаю, что это будет проблемой. – tvanfosson

19

JavaScript работает синхронно, поэтому все, что вы разместите после each(), не будет работать до тех пор, пока each() не будет завершен.

Рассмотрим следующий тест:

var count = 0; 
var array = []; 

// populate an array with 1,000,000 entries 
for(var i = 0; i < 1000000; i++) { 
    array.push(i); 
} 

// use each to iterate over the array, incrementing count each time 
$.each(array, function() { 
    count++ 
}); 

// the alert won't get called until the 'each' is done 
//  as evidenced by the value of count 
alert(count); 

Когда предупреждение называется, количество будет равно 1000000. потому, что предупреждение не будет работать, пока each() не будет сделано.

+4

Проблема в приведенном примере заключается в том, что fadeOut помещается в очередь анимации и не выполняет синхронно. Если вы используете «каждый» и планируете каждую анимацию отдельно, вам все равно придется запускать событие после анимации, а не каждую из них. – tvanfosson

+3

@tvanfosson - Да, я знаю. Согласно тому, что хочет сделать Лютер, ваше решение (и Pointy's) похоже на правильный подход. Но из комментариев Лютера, как ... ** Наивно, я ищу что-то вроде elems.each (foo, bar), где foo = 'функция применяется к каждому элементу' и bar = 'callback после завершения всех итераций *. * * ... он кажется настойчивым, что это проблема 'each(). Вот почему я бросил этот ответ в микс. – user113716

+0

Вы правы Патрик. Я (неправильно) понял, что .each() планировал или выполнял очереди своих обратных вызовов. Я думаю, что обращение к fadeOut косвенно привело меня к такому выводу. tvanfosson и Pointy представили ответы на проблему fadeOut (это то, чем я действительно был на самом деле), но ваш пост действительно немного меня понял. Ваш ответ на самом деле лучше всего отвечает на исходный вопрос, как указано, в то время как Pointy и tvanfosson отвечали на вопрос, который я пытался спросить. Хотел бы я взять два ответа. Спасибо, что выбрали этот ответ в микс :) ​​ –

0

насчет

$(parentSelect).nextAll().fadeOut(200, function() { 
    $(this).remove(); 
}).one(function(){ 
    myfunction(); 
}); 
+0

Это определенно вызывает myfunction() однажды (и вводит меня в другую концепцию jQuery), но то, что я искал, было гарантией «когда» это будет выполняться - а не только, что он будет работать один раз. И, как упоминает Патрик, эта часть оказывается довольно легкой. То, что я действительно искал, было способом вызвать что-то после последней логики fadeOut, которая, я не думаю, что .one() гарантирует. –

+0

Я думаю, что цепь делает это - так как первая часть работает до последней части. –

0

Вы должны стоять в очереди на остальную часть вашего запроса для его работы.

var elems = $(parentSelect).nextAll(); 
var lastID = elems.length - 1; 

elems.each(function(i) { 
    $(this).fadeOut(200, function() { 
     $(this).remove(); 
     if (i == lastID) { 
      $j(this).queue("fx",function(){ doMyThing;}); 
     } 
    }); 
}); 
5

Я нашел много ответов, касающихся массивов, но не с объектом json. Мое решение состояло в том, чтобы просто перебирать объект один раз, увеличивая счетчик, а затем, итерации через объект для выполнения кода, вы можете увеличить второй счетчик. Затем вы просто сравниваете два счетчика и получаете свое решение. Я знаю, что это немного неуклюже, но пока я не нашел более элегантного решения. Это мой пример кода:

var flag1 = flag2 = 0; 

$.each(object, function (i, v) { flag1++; }); 

$.each(object, function (ky, val) { 

    /* 
     Your code here 
    */ 
    flag2++; 
}); 

if(flag1 === flag2) { 
    your function to call at the end of the iteration 
} 

Как я уже сказал, это не самый изящный, но это работает и работает хорошо, и я не нашел лучшее решение только пока.

Приветствия, JP

145

Хорошо, это может быть немного постфактум, но .promise() также должны добиться того, что вы после этого.

Promise documentation

Пример из проекта я работаю над:

$('.panel') 
    .fadeOut('slow') 
    .promise() 
    .done(function() { 
     $('#' + target_panel).fadeIn('slow', function() {}); 
    }); 

:)

141

Это, вероятно, до конца, но я думаю, что этот код работает ...

$blocks.each(function(i, elm) { 
$(elm).fadeOut(200, function() { 
    $(elm).remove(); 
}); 
}).promise().done(function(){ alert("All was done"); }); 
0

Я встречаюсь с той же проблемой, и я решил с решением, как следующая треска е:

var drfs = new Array(); 
var external = $.Deferred(); 
drfs.push(external.promise()); 

$('itemSelector').each(function() { 
    //initialize the context for each cycle 
    var t = this; // optional 
    var internal = $.Deferred(); 

    // after the previous deferred operation has been resolved 
    drfs.pop().then(function() { 

     // do stuff of the cycle, optionally using t as this 
     var result; //boolean set by the stuff 

     if (result) { 
      internal.resolve(); 
     } else { 
      internal.reject(); 
     } 
    } 
    drfs.push(internal.promise()); 
}); 

external.resolve("done"); 

$.when(drfs).then(function() { 
    // after all each are resolved 

}); 

Решение решает следующие задачи: для синхронизации асинхронных операций начались в .each() итерации, используя отсроченный объект.

Смежные вопросы