SerenityNetworks Posted July 25, 2015 Share Posted July 25, 2015 I'm stuck on terminating my script, which right now just counts seconds. I can click the 'start' button, the script runs, and the seconds elapsed are displayed in the form. But I need to be able to stop the script using the 'stop' button and I can't figure out how to do so. (Note: When the script stops, I wish for the last elapsed value to remain displayed in the form.) What is the code I need to have the script stop as desired? Thanks in advance, Andrew <form id="myTimer" action="#"> <fieldset> <label> <span>Seconds:</span> <input type="text" name="secCounter" /> </label> </fieldset> <fieldset> <button type="submit">Start</button> <button type="reset">Stop</button> </fieldset> </form> <script type="text/javascript"> // timer function timer(form, stop) { if(stop) {exit();} else { var start = new Date().getTime(), time = 0, elapsed = '0.0'; function instance() { time += 100; elapsed = Math.floor(time / 100) / 10; if(Math.round(elapsed) == elapsed) { elapsed += '.0'; } form.secCounter.value = elapsed; var diff = (new Date().getTime() - start) - time; window.setTimeout(instance, (100 - diff)); } window.setTimeout(instance, 100); } function exit() { alert("This is not working. The script still runs."); return;} }; // start timer document.getElementById('myTimer').onsubmit = function() { timer(this); return false; }; // stop timer document.getElementById('myTimer').onreset = function() { timer(this, stop); return false; }; </script> Link to comment Share on other sites More sharing options...
Ingolme Posted July 25, 2015 Share Posted July 25, 2015 You should call clearTimeout() when you want to stop an existing timer. Here's how it works: var timeout = setTimeout(callback, time);// When you want to stop the timer:clearTimeout(timeout); With your current code, you'll have to watch carefully for the scope of the variables you're using. If you use var timeout right inside the function it won't be accessible to clearTimeout() outside the function. Link to comment Share on other sites More sharing options...
SerenityNetworks Posted July 25, 2015 Author Share Posted July 25, 2015 Okay, sorry, I've made several attempts to understand and apply, but I'm not getting how to use what you've shown. (I'm just a noob hobbyist.) Would you please provide me with a bit more guidance? Thanks, Andrew Link to comment Share on other sites More sharing options...
davej Posted July 25, 2015 Share Posted July 25, 2015 When I look at your code I am confused by the stop variable that is passed below. Where is stop declared and set equal to true? // stop timer document.getElementById('myTimer').onreset = function() { timer(this, stop); return false; }; Link to comment Share on other sites More sharing options...
Ingolme Posted July 25, 2015 Share Posted July 25, 2015 Indent your code properly Don't use <form> tags if you're not submitting anything to the server Keep track of all your timers You have to use proper DOM methods to access elements like getElementById() and getElementsByTagName(). formName.elementName is non-standard and may not work properly in all browsers. Check the Javascript reference for any functions that may help you. Number.toFixed() simplifies the code a lot. Try to keep your code as short and simple as possible. Read this and try to understand how it works. It should be easy because I've cut out a whole lot of stuff from it and made it really short: <fieldset> <label> <span>Seconds:</span> <input type="text" id="counter"> </label></fieldset><fieldset> <button id="start">Start</button> <button id="stop">Stop</button></fieldset><script type="text/javascript">// HTML elements we're going to usevar startButton = document.getElementById("start");var stopButton = document.getElementById("stop");var display = document.getElementById("counter");var timeout; // Global variable for clearTimeout();var startTime; // Global variable to remember when the timer started// Event handlersdocument.getElementById('start').onclick = start;document.getElementById('stop').onclick = stop;// Start the timerfunction start() { startTime = (new Date()).getTime(); instance();} // Stop the timer function stop() { if(timeout) { clearTimeout(timeout); timeout = null; }}// Call this on each intervalfunction instance() { elapsed = ((new Date()).getTime() - startTime) / 1000; display.value = elapsed.toFixed(1); // Remember that "display" was defined at the beginning of the script timeout = window.setTimeout(instance, 50);}</script> Link to comment Share on other sites More sharing options...
SerenityNetworks Posted July 25, 2015 Author Share Posted July 25, 2015 Thank you very much. I will not have the opportunity to examine the scripting until late tomorrow, but for sure I will study it. I just didn't want to wait until tomorrow to reply. Thank you again. I learn a lot by studying the differences between code that's simply (or partially) working and code that's been optimized. Andrew Link to comment Share on other sites More sharing options...
davej Posted July 26, 2015 Share Posted July 26, 2015 I've been playing with this, and it certainly does seem to be tricky to use a closure in this manner. Here is some code... <div> <fieldset> <label> <span>Seconds:</span> <input type="text" id="secCounter" /> </label> </fieldset> <fieldset> <button id="startbtn">Start</button> <button id="stopbtn">Stop</button> </fieldset></div><script type="text/javascript"> 'strict'; function starttimer() { var start, time, elapsed, itimer; function restarttimer(){ if (itimer==undefined || itimer==null){ start = new Date().getTime(); time = 0; elapsed = '0.0'; itimer = setTimeout(instance, 100); } } function stoptimer(){ clearTimeout(itimer); itimer = null; console.log('stop'); } function instance() { if (itimer != null){ time += 100; elapsed = Math.floor(time / 100) / 10; if(Math.round(elapsed) == elapsed) { elapsed += '.0'; } document.getElementById('secCounter').value = elapsed; var diff = (new Date().getTime() - start) - time; itimer = setTimeout(instance, (100 - diff)); } console.log('time='+time+' elapsed='+elapsed+' start='+start+' diff='+diff); } document.getElementById('stopbtn').onclick = function() { stoptimer(); } document.getElementById('startbtn').onclick = function() { restarttimer(); } restarttimer(); } // end of starttimer document.getElementById('startbtn').onclick = function(){ starttimer(); } </script> Link to comment Share on other sites More sharing options...
Ingolme Posted July 26, 2015 Share Posted July 26, 2015 I wouldn't use a closure for that. If I wasn't trying to keep the code similar to his original idea I would have created an object Timer and given it properties and methods. Link to comment Share on other sites More sharing options...
SerenityNetworks Posted July 26, 2015 Author Share Posted July 26, 2015 I haven't yet had a chance to look at the scripts you have provided. I've got two projects I'm working on that use timers. What I'm trying to do with this script is avoid the slow drift from real time that we get if the JavaScript timer is used exclusively. So I'm trying to keep the time 'real' by referencing the host's system clock. For this particular project I'm making a metronome that I can use to keep a steady pace when running. I'll have a field where I can enter the pace I want to run and then the script will play a sound at that pace (ex: 180 beats/sounds per minute). I'll have a couple other features, such as displaying the elapsed time, but simply playing a sound at 'n' beats per minute is the main function. (Yes, the accuracy of the JavaScript timer would be fine for this metronome purpose, but I'm trying to learn how to keep sync with real time for use in my other project where I need a timer that syncs with video playback.) Does knowing any of this help with how that solution would be coded? Thanks again, Andrew Link to comment Share on other sites More sharing options...
davej Posted July 26, 2015 Share Posted July 26, 2015 Creating a metronome in Javascript might be difficult. I couldn't find the audio play() function but it exists. (Foxy: I don't know why the overly paranoid closure approach seems to be so heavily promoted.) Link to comment Share on other sites More sharing options...
Ingolme Posted July 26, 2015 Share Posted July 26, 2015 You're going to have an error of anything up to 100 milliseconds if you use Javascript's traditional timers setInterval() and setTimeout(). If you use requestAnimationFrame() you can reduce the error to under 16 milliseconds. The script from my post will have these tiny errors in the exact moment a beat is played, but it will be generally synchronized because it's using the Date() object. I chose an interval of 50 milliseconds because your timer only needed a precision of 100 milliseconds. From my experience, these are not enough for music playback. Recently I have been working on a music application in Javascript and during my search for perfect timing I came across this great article: http://www.html5rocks.com/en/tutorials/audio/scheduling/ Link to comment Share on other sites More sharing options...
SerenityNetworks Posted July 26, 2015 Author Share Posted July 26, 2015 For the metronome project, I'm just wanting to play a short (< 1 second) sound. For temporary (experimentation) use I've just been using what I've put in the 1st code block below. It works on a Windows machine, but I'm sure I'll have to change it up to reference a regular sound file for use on my phone. Perhaps something like I have in the second code block. function beep() { var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU="); snd.play();} <audio id="sound1" src="images/this.wav"></audio><audio id="sound2" src="images/that.wav"></audio><button onclick="playSound1()">Sound 1</button><br /><button onclick="playSound2()">Sound 2</button><br /><script type="text/javascript">var audio1 = document.getElementById('sound1');var audio2 = document.getElementById('sound2');function playSound1(){ audio1.play(); }function playSound2(){ audio2.play(); }</script> Link to comment Share on other sites More sharing options...
Ingolme Posted July 26, 2015 Share Posted July 26, 2015 Just for the sake of avoiding giving the garbage collector too much work, I would move the var snd = new Audio() line outside the function so that it can be reused over and over. I think mobile devices should be able to accept data URLs, but I haven't tested before. Link to comment Share on other sites More sharing options...
SerenityNetworks Posted July 26, 2015 Author Share Posted July 26, 2015 Thanks for the tip (on the var snd = new Audio() line). I'll do so. (Not sure that I'm going to get to look at all the code provided now until maybe tomorrow night. It might not be until late this week. My granddaughter is in town and time is precious. I don't want anyone to think I'm blowing off the advice if I don't get back for a while.) Link to comment Share on other sites More sharing options...
SerenityNetworks Posted July 27, 2015 Author Share Posted July 27, 2015 I'm stumped on triggering the sound. I set a global variable ("interval") to remember the previous time value. With the next iteration of the instance function I subtract the current time from the previously calculated time and if it equals 1 then I play the beep. But it's not working as I need. If I set the calculation to be performed on whole numbers ("interval = elapsed.toFixed(0)") then it works, but due to rounding the beeps are noticeably irregular. But if I set the calculation to be performed at toFixed(1) or toFixed(2) then I never get a beep. I'd think with the instance function being recalculated every 50 milliseconds that I should be able to play a beep accurate to 1/100 second without any problems. What am I missing &/or is there a better way to call the beep? Thanks again, Andrew // Call this on each interval function instance() { elapsed = ((new Date()).getTime() - startTime) / 1000; if (elapsed.toFixed(1) - interval == 1) { beep(); } display.value = elapsed.toFixed(1); // Remember that "display" was defined at the beginning of the script interval = elapsed.toFixed(1); //Global variable timeout = window.setTimeout(instance, 50); } Link to comment Share on other sites More sharing options...
Ingolme Posted July 28, 2015 Share Posted July 28, 2015 Have you checked the error console? toFixed() turns the number into a string, you should only do that if you want to display the value in a readable format. It would be best if you operate in milliseconds instead of dividing by 1000. You only divide by 1000 to show a more understandable number to the user, but it's far more efficient to operate in the environment Javascript uses. // Global variablesvar timeSinceLastBeep;var timeStarted;// Function to be called on each framefunction instance() { // What time is it? var now = (new Date()).getTime(); // Make sure the timing variables have a value if(!timeStarted) timeStarted = now; if(!timeSinceLastBeep) timeSinceLastBeep = now; // Determine how much time has passed since the last beep var elapsed = now - timeSinceLastBeep; // If the amount of time that has passed is one second or longer then play a beep and remember when the beep was played. if(elapsed >= 1000) { beep(); // Set "timeSinceLastBeep" to the time when the beep should have played to prevent synchronization errors then = now - (timeSinceLastBeep % 1000); } // Show the amount of time that has passed var timeSinceStart = (now - timeStarted) / 1000; display.value = timeSinceStart.toFixed(1); // Call the next frame timeout = window.setTimeout(instance, 50);} Link to comment Share on other sites More sharing options...
vmars316 Posted June 10, 2016 Share Posted June 10, 2016 Creating a metronome in Javascript might be difficult. I couldn't find the audio play() function but it exists. (Foxy: I don't know why the overly paranoid closure approach seems to be so heavily promoted. This may not be timely , but here is site of many .js metronomes , http://www.metronomebot.com/talking-metronome-in-four.html Link to comment Share on other sites More sharing options...
vmars316 Posted June 10, 2016 Share Posted June 10, 2016 Indent your code properly Don't use <form> tags if you're not submitting anything to the server Keep track of all your timers You have to use proper DOM methods to access elements like getElementById() and getElementsByTagName(). formName.elementName is non-standard and may not work properly in all browsers. Check the Javascript reference for any functions that may help you. Number.toFixed() simplifies the code a lot. Try to keep your code as short and simple as possible. Read this and try to understand how it works. It should be easy because I've cut out a whole lot of stuff from it and made it really short: <fieldset> <label> <span>Seconds:</span> <input type="text" id="counter"> </label></fieldset><fieldset> <button id="start">Start</button> <button id="stop">Stop</button></fieldset><script type="text/javascript">// HTML elements we're going to usevar startButton = document.getElementById("start");var stopButton = document.getElementById("stop");var display = document.getElementById("counter");var timeout; // Global variable for clearTimeout();var startTime; // Global variable to remember when the timer started// Event handlersdocument.getElementById('start').onclick = start;document.getElementById('stop').onclick = stop;// Start the timerfunction start() { startTime = (new Date()).getTime(); instance();} // Stop the timer function stop() { if(timeout) { clearTimeout(timeout); timeout = null; }}// Call this on each intervalfunction instance() { elapsed = ((new Date()).getTime() - startTime) / 1000; display.value = elapsed.toFixed(1); // Remember that "display" was defined at the beginning of the script timeout = window.setTimeout(instance, 50);}</script> Actually , I am searching for Posts that contain 'requestAnimationFrame' . Just curious , why do people show code all on one line ? To me it is downright unpleasant , to have to copy/paste , then edit code into one statement per line . Or is there a way to unravel code into multilines , that I don't know about ? Thanks Link to comment Share on other sites More sharing options...
Ingolme Posted June 10, 2016 Share Posted June 10, 2016 The forum software updated and caused everything to break in older posts. Link to comment Share on other sites More sharing options...
vmars316 Posted June 11, 2016 Share Posted June 11, 2016 Oh , I see . Thanks Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now