Animation speeds up each time 'start' button clicked

I’m learning Javascript from Sitepoint’s ‘Simply Javascript’ book. Just read the 1st animation example, which suggests one could add a ‘start’ & ‘stop’ (id=“stopit”) button to control the simple sprite animation. My Javascript looks like this (note that it works in tandem with the core.js that comes with the book; see the ZIP file i’ve attached):

var Robot =
{
	init: function() // initiate via core.js
	{
		Robot.div = document.getElementById("robot"); // the div containing the background sprite
		Robot.frameHeight = 150;
		Robot.frames = 10;
		Robot.offsetY = 0;
    
		{
			var start = document.getElementById("start"); // the start button
			Core.addEventListener(start, "click", Robot.clickStart, false); // add listener via core.js
			var stopit = document.getElementById("stopit"); // the stop button
			Core.addEventListener(stopit, "click", Robot.clickStop, false); // add listener via core.js
		}
	},
  
	clickStart: function() // the animation function
	{
		Robot.offsetY -= Robot.frameHeight;
		
		if (Robot.offsetY <= -Robot.frameHeight * Robot.frames)
		{
			Robot.offsetY = 0;
		}
	
		Robot.div.style.backgroundPosition = "0 " + Robot.offsetY + "px";
	
		Robot.timer = setTimeout(Robot.clickStart, 75);
	
	},
	clickStop: function() // the killswitch
	{
		if (Robot.timer)
		{
			clearTimeout(Robot.timer);
		}
		
		Robot.offsetY = 0;
		Robot.div.style.backgroundPosition = "0 " + Robot.offsetY + "px"; // return the animation to the 1st frame
		alert ("Aborted \\u2014 must sleep now. Zzzz \\u2026");
	}
};

Core.start(Robot); //initiate via core.js

Oddly, clicking on the ‘start’ button more than once speeds up the animation, more with each successive click. Clicking the ‘stop’ button will only finally stop the animation if it’s done the same number of times as the ‘start’ button was clicked.

Anyone know what causes this?

Thanks,

–cz

Logic Ali’s original solution solution is simple and works. I thought I had already tried that yesterday without success, but apparently I hadn’t.

Also thanks for the 2nd solution + example script – it’s always good to know another way to do something.

–cz


if (Robot.timer)
{
 clearTimeout(Robot.timer);
}

I would say this block from the clickStop function should be copied into the beginning of the clickStart function.

My apologies, I should have been clearer.

Whenever a timer is started, you need to first check that the timer doesn’t already exist. If it does already exist you need to either replace it, but stopping the timer and starting a new one, or by doing nothing and allowing the exisiting timer to carry on as per normal.

The OP’s script does that already and it doesn’t work. Re-assigning a timeout or interval to a variable does nothing except store an integer that can be used as an address by clearTimeout/clearInterval. It does not cancel any pending timer.


<html>
<head>
<title>Interval Overwrite Test</title>
</head>
<body>

<div id='t' style='position:absolute;top:100px;left:0'>:-)</div>
<script type='text/javascript'>

var timer, x = 0 ;

function a()
{
 document.getElementById('t').style.left = (x+=2) % 500 + 'px';
   
 timer = setTimeout(a, 400);  
 
 return false; 
}

function stopAll( t )
{
 for( var i=1; i<=t; i++)  
  clearTimeout( i ); 
  
 return false; 
}


</script>

<p>
<a href='#' onclick="return a()">START</a> <- click repeatedly
<p>
<a href='#' onclick="return stopAll( timer )">STOP all timers</a>

</body>
</html>

Yes, each time you click on start you end up creating another event timer. So by the 3rd click you’ll have three event timers all firing off individually, which is why things then go three times as fast.

A way around this is to assign the event timer to a single known variable, so that multiple instances only replace the old one that was there previously. Commonly you would assign the timer to a named property on the object itself.

Another alternative is to disable the start button as soon as it’s pressed.