Проблема setTimeout и анонимной функции
Это мой код, SetOpacity вызывается с неправильными значениями, почему?
function SetOpacity(eID, opacity){
eID.style.opacity = opacity / 100;
eID.style.filter = 'alpha(opacity=' + opacity + ')';
}
function fade(eID, startOpacity, endOpacity){
var timer = 0;
if (startOpacity < endOpacity) {
for (var i = startOpacity; i <= endOpacity; i++) {
setTimeout(function() {SetOpacity(eID, i);}, timer * 30);
timer++;
}
}
}
Ответы
Ответ 1
Это должно работать:
for (var i = startOpacity; i <= endOpacity; i++) {
(function(opacity) {
setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
})(i);
timer++;
}
Это работает следующим образом:
- внутри цикла вы создаете анонимную функцию (
function(...){...}
) и сразу вызываете ее с параметром (вот почему есть скобки вокруг function(){}
, поэтому вы можете называть его добавлением ()
в конце и параметрами прохода)
Параметры - переданные этой анонимной функции (значение
i
, которое является opacity
внутри функции) являются локальными для этой анонимной функции, поэтому они не меняются при следующих шагах цикла, и вы можете безопасно передавать их другому анонимная функция (это при setTimeout
)
Ваша оригинальная версия не сработала, потому что:
- ваша функция, переданная в
setTimeout
, содержит ссылку на переменную i
(а не ее значение), и она получает значение, когда эта функция вызывается, а не время добавления ее в setTimeout
- значение этой переменной изменяется в цикле, и до получения первого
setTimeout
оно получает значение endOpacity
(последнее значение из цикла for
)
К сожалению, JavaScript имеет только область функций, поэтому он не будет работать, если вы создаете переменную внутри цикла и назначаете новое фактическое значение, потому что всякий раз, когда есть некоторая внутренняя функция var
, эти переменные создаются во время выполнения функции ( и получите значение undefined
по умолчанию). Единственный (простой) способ создания новой области - создать функцию (может быть анонимной) и создать в ней новые переменные (параметры тоже являются переменными)
Ответ 2
Это проблема с закрытием. К моменту запуска функции i
уже находится в endOpacity
. Это будет работать, создав еще одно закрытие:
function SetOpacityTimeout(eID, opacity, timer){
setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
}
function fade(eID, startOpacity, endOpacity){
var timer = 0;
if (startOpacity < endOpacity) {
for (var i = startOpacity; i <= endOpacity; i++) {
SetOpacityTimeout(eID,i,timer);
timer++;
}
}
}
Ответ 3
Коби имеет правильную идею по проблеме. Я предлагаю вам использовать интервал вместо этого.
Вот пример: (Ваша функция SetOpacity остается неизменной, я оставил ее здесь.)
function fade(eID, startOpacity, endOpacity){
var opacity = startOpacity;
SetOpacity(eID, opacity);
var interval = window.setInterval(function(){
opacity++;
SetOpacity(eID, opacity);
// Stop the interval when done
if (opacity === endOpacity)
window.clearInterval(interval);
}, 30);
}
Ответ 4
Это пример, который я использовал с jquery. "menuitem" - это itemclass, а jquery проверяет класс "recentOut", чтобы увидеть, нужно ли ему выполнить резервное копирование.
Код говорит сам за себя.
$(".menuitem").mouseenter(
function(){
$(this).addClass("over").removeClass("out").removeClass("recentlyOut");
$(this).children(".sub").slideDown();
});
$(".menuitem").mouseleave(
function(){
$(this).addClass("out").addClass("recentlyOut").removeClass("over");
setTimeout(function()
{
var bool = $(".recentlyOut").hasClass("over");
if (!bool)
{
$(".recentlyOut").removeClass("recentlyOut").children(".sub").slideUp();
}
}
, 400);
}
);