2010-01-31 17 views
14

Este es mi código, se invoca a SetOpacity con valores erróneos, ¿por qué?setTimeout y problema de función anónima

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++; 
     } 
    }   
} 

Respuesta

42

Esto debería funcionar:

for (var i = startOpacity; i <= endOpacity; i++) { 
    (function(opacity) { 
     setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30); 
    })(i); 
    timer++; 
} 

Esto funciona de la siguiente manera:

  • bucle interno crea una función anónima (function(...){...}) e inmediatamente la llama con el parámetro (es por eso que hay e paréntesis alrededor function(){}, por lo que se puede llamar la adición () al final y pasar parámetros)
  • parámetros pasados ​​a esta función anónima (i valor, que es opacity función dentro de) la hora local de esta función en el anonimato, por lo que no cambian en los próximos pasos de bucle, y se puede safelly pasar a otra función anónima (esto en setTimeout)

su versión original no funcionó porque:

  • su función pasó a setTimeout sostiene ref de acuerdo con la variable i (sin valor), y obtiene valor cuando se llama a esta función, que no está en el momento de agregarlo a setTimeout
  • el valor de esta variable se cambia en bucle, y antes de que llegue primero setTimeout se pone endOpacity valor (último valor de for bucle)

Desafortunadamente JavaScript solamente tiene ámbito de la función, por lo que no va a funcionar si se crea la variable dentro del bucle y asignar nuevo valor real, porque siempre hay algo de var dentro de la función, esas variables se crean en el momento de la ejecución de la función (y obtienen el valor undefined de forma predeterminada). La única (fácil) forma de crear un nuevo ámbito es crear una función (puede ser anónima) y crear nuevas variables dentro de ellos (los parámetros son variables también)

+0

+1 - También comencé con una función anónima. mirando de nuevo, supongo que el tuyo es más elegante. – Kobi

+0

¿Puedes explicarlo con (i)? – ronik

+0

@ronik Actualicé mi respuesta – MBO

5

Este es un problema de cierre. Para cuando ejecuta la función, i ya está en endOpacity. Esto funcionará, creando otro cierre:

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++; 
     } 
    }   
} 
+0

Todavía obtengo resultados erróneos – ronik

+2

¿Has probado este? 'var opacity' está todavía en el mismo ámbito que' i', por lo que aún debería romperse, por lo que puedo decir. –

+0

@ Shtééf - tienes razón. Trabajando en ello. – Kobi

1

Kobi tiene la idea correcta sobre el problema. Sin embargo, sugiero que uses un intervalo.

He aquí un ejemplo: (Su función setOpacity sigue siendo el mismo, lo dejé aquí.)

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); 
} 
+0

gracias, pero ¿cómo hacerlo con setTimeout? – ronik

+0

La solución de MBO también funcionará. –

1

Este es un ejemplo que utilicé con jquery. "menuitem" es la clase de elemento y jquery comprueba la clase "RecentlyOut" para ver si necesita volver a subir una copia de seguridad.

El código habla por sí mismo.

$(".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); 
} 
    ); 
Cuestiones relacionadas