Jump to content

Using setTimeout() in a for loop


Spunky

Recommended Posts

So, I am trying to set up what I am calling a "Guided Tour" which uses CSS tooltips/popups to explain various parts of the page that they are on. I want the "tour" to run automatically, but also give them the ability to pause and continue it at any time, as well as go to the next and previous tip as needed. I'm finding this to be a bit more complex than originally anticipated. Mostly because it seems that using setTimeout() inside a for loop isn't that straight-forward.

Let's start with my code:
 

var startG = document.getElementById("startGuide");

var guidetips = [];
guidetips[0] = document.getElementById("tip1");
guidetips[1] = document.getElementById("tip2");
guidetips[2] = document.getElementById("tip3");
guidetips[3] = document.getElementById("tip4");
guidetips[4] = document.getElementById("tip5");
guidetips[5] = document.getElementById("tip6");
guidetips[6] = document.getElementById("tip7");

startG.onclick = function(){
  guideModal.style.display = "none";
  controls.style.display = "block";
  runGuideTips();
}

function runGuideTips(){
  guidetips[0].style.opacity="100";

  for(i=0;i<guidetips.length;i++){
    if(i === 0){
      continue;
    }

    setTimeout(toggleTip(i), 3000);
  }
}

function toggleTip(i){
  return function(){guidetips[i-1].style.opacity="0";guidetips[i+1].style.opacity="100";};
}

 

When the user clicks the button to begin, I have the first tip start out by showing immediately. For that reason, I skip the first iteration. Then, each iteration after I want the prior tip to disappear and the current tip to appear.

function toggleTip() was added when I read somewhere that by the time the timeout runs, the for loop is already complete. Their solution was to send the parameter in a function. It does work a little bit better than it was before.

What it is doing right now is the first tip shows, then 3 seconds later it disappears and the last tip appears. I get an error in my Console that states: Unable to get property ' style' of undefined or null reference : referring to the function in toggleTip(). The expected result was to go through all 7 guidetips.

 

At the end of the day, I am not certain if I am using the best method to accomplish the end goal that I described. I tried to Google this type of thing because I know I've seen it done before. But I wasn't able to find anything specific on it. So any input or suggestions would be appreciated. :)

Link to comment
Share on other sites

There are very few reasons to use setTimeout inside a loop, and this is not one of them.  What you are doing is running through a loop and scheduling everything to run 3000ms after the loop.  It is not scheduling each to run 3000ms apart.  If you wanted to do that, you need to add 3000 each time through the loop, because that loop runs immediately and schedules everything to run at once.  But, like I said, this is not the place to use setTimeout inside a loop.  There's no reason to schedule everything at once.  Just schedule the next one, and when it finishes schedule the next one after that.  That way, if you want to pause, you only have to clear one timeout instead of all of them.  You should only have one timeout scheduled at a time, and one of the things that your function should do is figure out whether to schedule the next timeout.  Also, if you want to pause then you need to save the return value of setTimeout so that you can later use clearTimeout to cancel it if you want to pause.

Link to comment
Share on other sites

One solution (of many) possible.

	<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title> setTimeout Displays </title>
<!-- From: http://w3schools.invisionzone.com/topic/58090-using-settimeout-in-a-for-loop/ -->
	<style>
 .tips { display: none; }
</style>
	</head>
<body>
<button id="startGuide"> Start Guide </button>
	<div id="tip0" class="tips"> Tip 1 </div>
<div id="tip1" class="tips"> Tip 2 </div>
<div id="tip2" class="tips"> Tip 3 </div>
<div id="tip3" class="tips"> Tip 4 </div>
<div id="tip4" class="tips"> Tip 5 </div>
<div id="tip5" class="tips"> Tip 6 </div>
<div id="tip6" class="tips"> Done </div>
	<div id="debug"><br><!-- tipCnt, if used --></div>
<script>
	var startG = document.getElementById("startGuide");
	var guidetips = [];
guidetips[0] = document.getElementById("tip0");
guidetips[1] = document.getElementById("tip1");
guidetips[2] = document.getElementById("tip2");
guidetips[3] = document.getElementById("tip3");
guidetips[4] = document.getElementById("tip4");
guidetips[5] = document.getElementById("tip5");
guidetips[6] = document.getElementById("tip6");
	var tip = '', tipCnt = 0, tipTime = 1000;
	function toggleTip(){
  if (tipCnt < guidetips.length) {
    if (tipCnt > 0) { guidetips[tipCnt-1].style.display = 'none'; }
    clearTimeout(tip);
    tip = setTimeout('toggleTip()', tipTime);
    if (tipCnt < guidetips.length) {
      guidetips[tipCnt].style.display = 'block';
      tipCnt++;
    } else { guidetips[tipCnt].style.display = 'none'; }
  } else { clearTimeout(tip);  tipCnt = 0; }
	//  document.getElementById('debug').innerHTML = tipCnt;  // optional display
}
	function init() {
  document.getElementById('startGuide').addEventListener('click', function() { toggleTip(); } );
} init();
</script>
	</body>
</html>
	

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...