2010-01-31 89 views
14

這是我的代碼,SetOpacity被調用錯誤的值,爲什麼?setTimeout和匿名函數問題

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

回答

42

這應該工作:

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

這種工作方式如下:

  • 內循環你創建匿名函數(function(...){...}),並立即用參數調用它(這就是爲什麼有ar各地function(){}è括號,所以你可以把它在結尾處增加()和傳遞參數)
  • 參數傳遞給該匿名函數(i價值,這是opacity內部函數)是本地的這個匿名函數,所以他們不改變在循環的後續步驟,你可以safelly它們傳遞到另一個匿名函數(這在setTimeout

你原來的版本並沒有因爲工作:

  • 你的函數傳遞給setTimeout持有REF變量i(不是它的值),並且它在調用此函數時得到值,該值不在添加到setTimeout
  • 該變量的值在循環中得到更改,並且在您甚至獲得第一個setTimeout它得到endOpacity值(最後一個值從for環)

不幸的是JavaScript的只有功能範圍,所以如果你在循環中創建變量將無法正常工作,並指定新的實際值,因爲每當有一些var內部函數中,這些變量是在函數執行時創建的(默認值爲undefined)。創建新範圍的唯一(簡單)方法是創建函數(可能是匿名)並在其中創建新變量(參數也是變量)

+0

+1 - 我也開始使用匿名函數。再次看,我想你的更優雅。 – Kobi

+0

你能解釋一下(i)的事嗎? – ronik

+0

@ronik我更新了我的答案 – MBO

5

這是一個關閉問題。當你運行該功能時,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++; 
     } 
    }   
} 
+0

我還是得到了錯誤的結果 – ronik

+2

你測試過了嗎? 'var opacity'與'i'仍然在同一個範圍內,所以它應該仍然會破裂,從我可以說的。 –

+0

@Shtééf - 你是對的。在此工作。 – Kobi

1

科比對問題有正確的想法。不過,我建議你使用間隔。

下面是一個例子:(您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); 
} 
+0

謝謝,但如何用setTimeout做到這一點? – ronik

+0

MBO的解決方案也可以使用。 –

1

這就是我用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); 
} 
    );