Javascript setTimeout, Loops and Closure

房东的猫 提交于 2019-12-11 13:16:51

问题


The following code has me confused. My goal is to fade an HTML element from one opacity level to another, over a period of time. Below is my dimScreen() method, used to create a darkened overlay and fade it in.

var overlay;

function dimScreen()
{
   if(!overlay)
   {
      overlay = document.createElement('div');
      overlay.setAttribute('id', 'overlay');
      overlay.style.width = '100%';
      overlay.style.height = '100%';
      overlay.style.zindex = '1000';
      overlay.style.background = '#000013';
      overlay.style.position = 'fixed';
      overlay.style.left = '0';
      overlay.style.top = '0';
      overlay.style.opacity = '.0';

      document.body.appendChild(overlay);
      fade('overlay', 0, 75, 200);
   }
}

Here is my working fade function:

function fade(eID, startOpacity, stopOpacity, duration)
{
   var speed = Math.round(duration / 100);
   var timer = 0;
   if (startOpacity < stopOpacity)
   {     // fade in
      for (var i=startOpacity; i<=stopOpacity; i++)
      {
         setTimeout("setOpacity('"+eID+"',"+i+")", timer * speed);
         timer++;
      } return;
   }
   for (var i=startOpacity; i>=stopOpacity; i--)
   {     // fade out
      setTimeout("setOpacity('"+eID+"',"+i+")", timer * speed);
      timer++;
   }
}

And my working setOpacity:

function setOpacity(eID, opacityLevel)
{
   var eStyle = document.getElementById(eID).style;
   eStyle.opacity = opacityLevel / 100;
   eStyle.filter = 'alpha(opacity='+opacityLevel+')';
}

I would like to, however, change my fade() function to user a closure in the form of:

setTimeout(function(){setOpacity(eID,i)}, timer * speed);

I could then pass in the actual overlay div, instead of the id 'overlay'. When I do this, however, rather than animating a gradual fade-in of the overlay div, it just goes right to the last opacity value with no transition. Why does this happen?

I have a feeling this is something simple that I am missing because I've been looking at it for so long. Any help would be appreciated!


回答1:


It's too late and still too hot to think much here right now. (2.30am, 28°C) Forgive me if my explanation falls short of expectations.

Basically, because you construct text strings to pass to setTimeout, each call gets the correct value for i. Since to pass an actual element, you can't use the string trick, you end up using the same value of i for each call to setOpacity.

As your question title indicates you understand, you need to use closures to get it to work right. I can't find(only followed 2 or 3 links) a good tute/S.O question that explains it in a way that seems relevant and clear.

Hope the code will be of use.

var overlay;
function dimScreen()
{
   if(!overlay)
   {
      overlay = document.createElement('div');
      overlay.setAttribute('id', 'overlay');
      overlay.style.width = '100%';
      overlay.style.height = '100%';
      overlay.style.zindex = '1000';
      overlay.style.background = '#000013';
      overlay.style.position = 'fixed';
      overlay.style.left = '0';
      overlay.style.top = '0';
      overlay.style.opacity = '.0';

      document.body.appendChild(overlay);
      myFade(overlay, 0, 75, 2000);
   }
}
function myFade(element, startOpacity, stopOpacity, duration)
{
   var speed = Math.round(duration / 100);
   var timer = 0;
   if (startOpacity < stopOpacity)
   {     // fade in
      for (var i=startOpacity; i<=stopOpacity; i++)
      {
        (
            function()
            {
                var myI = i;
                setTimeout(function(){mySetOpacity(element,myI)}, timer * speed);
            }
        )()
         timer++;
      }
   }

   else
   for (var i=startOpacity; i>=stopOpacity; i--)
   {     // fade out
      for (var i=startOpacity; i<=stopOpacity; i++)
      {
        (
            function()
            {
                var myI = i;
                setTimeout(function(){mySetOpacity(element,myI)}, timer * speed);
            }
        )()
         timer++;
      }
   }
}

function mySetOpacity(el, opacityLevel)
{
   var eStyle = el.style;
   eStyle.opacity = opacityLevel / 100;
   eStyle.filter = 'alpha(opacity='+opacityLevel+')';
}


来源:https://stackoverflow.com/questions/15363417/javascript-settimeout-loops-and-closure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!