Pausing a loop: How to re-start where it left off

I have a simple looping function which displays images on a web page sequentially. When the page loads the images are all positioned absolutely in the the same place, creating a ‘stack’, and opacities are set to zero. The JS function loops through the stack, changing the opacities in turn.

That works, no problem. Now I want to be able to pause the sequence by mousing over the current image, and restart on mouse-out. Here’s my code:


	var l_timer;
	var i = 0;

//	This Fn is called on page load: (the number of images loaded varies from page to page)
	function switchImages() {
		var imgdiv = document.getElementById('imgdiv');
		var imgstack = imgdiv.getElementsByTagName('img');
		var len = imgstack.length;

//	Set up pause and re-start actions:
		$(imgdiv ).mouseenter(function() {
			clearTimeout(l_timer);
 		}).mouseleave(function() {
			l_timer = setTimeout(function() {rotate(imgstack, len, i)}, 5000);
		});

// Start the looping function initially:
		rotate(imgstack, len, i);
	}

// Looping function:
	function rotate(imgstack, len, i) {
		img = imgstack[i];
		if (i < len - 1) {
			i++;
		} else {
			i = 0;
		}
		imgnext = imgstack[i];
		$(img).animate({opacity: 0}, 2000); // fade out current image (see note)
		$(imgnext).animate({opacity: 1}, 2000); // fade in next image
		l_timer = setTimeout(function() {rotate(imgstack, len, i)}, 5000);
	}

Note: Image height varies, so it is necessary to fade out each image in turn as the loop
 progresses, because it may not be covered by the next image.

There’s no problem pausing (say at i = n), but when I re-start (mouse-leave) there appears to be a long delay before the loop resumes. What’s actually happening is that the loop is starting again from i = 0, but the image at ‘i = n’ has opacity = 1, and remains displayed until the loop has advanced far enough to fade it out.

I think this happens because the value if ‘i’ when the ‘mouseleave’ function is created is zero. I would like it to be updated to ‘n’ (the value at the pause point), but I can’t work out how to achieve this. (If I’ve understood the code correctly, the ‘mouseleave’ function is only created when the ‘mouseenter’ is triggered ?? If that’s so why doesn’t it pick up the current value of ‘i’ ?)

I suspect it’s all to do with ‘closure’ (or lack thereof). Any suggestions, please ?

The scripts can be seen (not) working here

I think there are a number of ways of keeping i as n. Probably the easiest is to have a variable in the main code (so is visible to all functions) which keeps track of n, maybe called ‘imageNSelected’ or something. When i changes, also update imageNSelected, and on the mouse out, set i as imageNSelected.

Might be more elegant ways, but this is pre-breakfast :stuck_out_tongue:

Hello RT,
Thank you. I tried something along those lines, but without success. The problem is that once the ‘rotate’ function is running the ‘action’ is no longer passing through the ‘switchImages’ function.

I also tried monitoring ‘i’ from within the ‘mouseleave’ function, but it remains at zero.

I cannot be the first person to have this requirement, so there will be a solution out there. Most timeouts seem to resume where they leave off, the complication here lies in the passing of parameters.

If you post a simple html doc with that replicates the error and I can copy, paste and test, I might have a go tonight.

i is a global variable and does not need passing to the function
also the mouseleave timeout delay doesnot need to be as long

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>
  <title></title>
</head>

<body>
<script type="text/javascript">
/*<![CDATA[*/
	var l_timer;
	var i = 0;

//	This Fn is called on page load: (the number of images loaded varies from page to page)
	function switchImages() {
		var imgdiv = document.getElementById('imgdiv');
		var imgstack = imgdiv.getElementsByTagName('img');
		var len = imgstack.length;

//	Set up pause and re-start actions:
		$(imgdiv ).mouseenter(function() {
			clearTimeout(l_timer);
 		}).mouseleave(function() {
			l_timer = setTimeout(function() {rotate(imgstack, len)}, 200);
		});

// Start the looping function initially:
		rotate(imgstack, len);
	}

// Looping function:
	function rotate(imgstack, len) {
		img = imgstack[i];
		if (i < len - 1) {
			i++;
		} else {
			i = 0;
		}
		imgnext = imgstack[i];
		$(img).animate({opacity: 0}, 2000); // fade out current image (see note)
		$(imgnext).animate({opacity: 1}, 2000); // fade in next image
		l_timer = setTimeout(function() {rotate(imgstack, len)}, 5000);
	}


/*]]>*/
</script>
</body>

</html>

Hello vwphillips,
Thank you. That has worked. I had previously tried not passing ‘i’ and got error messages. Possibly because I didn’t omit it (i.e. not pass it) everywhere.