2010-01-31 19 views
14

Questo è il mio codice, SetOpacity viene richiamato con valori errati, perché?setTimeout e problema di funzione anonima

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

risposta

42

Questo dovrebbe funzionare:

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

Questo funziona come segue:

  • all'interno del loop si crea la funzione anonima (function(...){...}) e si chiama immediatamente con il parametro (ecco perché ci sono e tra parentesi intorno function(){}, così si può chiamare l'aggiunta () alla fine e passare parametri)
  • parametri passati a questa funzione anonima (i valore, che è opacity funzione all'interno) sono locali rispetto a questa funzione anonima, in modo da non cambiano a passi successivi di ciclo, e si può safelly passarli ad un'altra funzione anonima (questo a setTimeout)

la versione originale non ha funzionato perché:

  • la funzione passato al setTimeout stive ref alla variabile i (non il valore di esso) e ottiene il valore quando viene chiamata questa funzione, che non è al momento dell'aggiunta a setTimeout
  • il valore di questa variabile viene modificato in loop e prima ancora di ottenere il primo setTimeout diventa endOpacity valore (ultimo valore da for ciclo)

Purtroppo JavaScript ha solo ambito delle funzioni, in modo che non funziona se si crea variabile all'interno del ciclo e assegnare nuovo valore reale, perché ogni volta che v'è una certa var all'interno della funzione, quelle variabili vengono create al momento dell'esecuzione della funzione (e ottengono il valore undefined per impostazione predefinita). L'unico modo (facile) per creare un nuovo scope è creare funzioni (potrebbero essere anonimi) e creare nuove variabili al loro interno (anche i parametri sono variabili)

+0

+1 - Ho anche iniziato con una funzione anonima. guardando di nuovo, immagino che il tuo sia più elegante. – Kobi

+0

puoi spiegare la cosa con il (i)? – ronik

+0

@ronik Ho aggiornato la mia risposta – MBO

5

Questo è un problema di chiusura. Quando esegui la funzione, i è già allo endOpacity. Questo funzionerà, creando un'altra chiusura:

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

Ho ancora risultati errati – ronik

+2

Hai provato questo? 'var opacity' è ancora nello stesso scope di' i', quindi dovrebbe ancora rompersi, da quello che posso dire. –

+0

@ Shtééf - hai ragione. lavorando su quello. – Kobi

1

Kobi ha l'idea giusta sul problema. Ti suggerisco di usare un intervallo invece, però.

Ecco un esempio: (La funzione setOpacity rimane lo stesso, ho lasciato qui.)

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

grazie, ma come farlo con setTimeout? – ronik

+0

La soluzione di MBO funzionerà anche. –

1

Questo è l'esempio che ho usato con jquery. "menuitem" è itemclass e jquery controlla la classe "recentlyOut" per vedere se è necessario eseguire il backup.

Il codice parla da solo.

$(".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); 
} 
    ); 
Problemi correlati