this may be a small problem, but it seems that I can't find a solution anywhere. I will try to explain the issue the best way I can.
I have a simple timer function:
let [milliseconds,seconds,minutes] = [0,0,0];
let int = null;
const start = () => {
if(int!==null){
clearInterval(int);
}
int = setInterval(displayTimer,10);
}
const reset = () => {
clearInterval(int);
[milliseconds,seconds,minutes] = [0,0,0];
document.querySelector('.timerDisplay').innerHTML = '00 : 00';
}
function displayTimer(){
milliseconds+=10;
if(milliseconds == 1000){
milliseconds = 0;
seconds++;
if(seconds == 60){
seconds = 0;
minutes++;
}
}
let m = minutes < 10 ? "0" + minutes : minutes;
let s = seconds < 10 ? "0" + seconds : seconds;
document.querySelector('.timerDisplay').innerHTML = `${m} : ${s}`;
}
And when start and reset functionalities are separated in two different buttons, they work like a charm.
<button onClick={reset} id="pauseTimer">Reset</button>
<button onClick={start} id="startTimer">Start</button>
I am encountering an issue when I try to put both of them in a single function, like this:
const newGame = () => {
reset()
//some other code
start()
}
So the newGame function should (in theory) reset the timer and start the new one. Instead, timer resets to zero for a split second, but the "old one" keeps counting, while the new one starts from 00:00, so the numbers overlap. The more I fire newGame, the more overlaps I get.
My question is, how do I prevent this?
I've tried using a useState to store the timer, still didn't work.
Thank you!
I cleaned up your code snippets a bit so I could run it on my machine and it appears to run as expected. If you're still seeing a problem on your end it may be in a piece of code that you didn't share that is somehow interfering with the code you shared.
That said, I saw that you mentioned the useState React hook so I'm assuming you're using React. If that is the case and all of this code is being run inside a React component, then it may be that something you're doing is triggering the component to re-render, which would re-declare new copies of all your local variables and functions. If that's the case, then the reason your old interval is still running is that after the re-render, your reset and start functions are only able to access and interact with the new copies of your local variables that store the current interval and time. I would suggest persisting the state of each of those variables by using the useState React hook
<html>
<head>
<script>
let [milliseconds, seconds, minutes] = [0, 0, 0];
let int = null;
const start = () => {
if (int !== null) {
clearInterval(int);
}
int = setInterval(displayTimer, 10);
}
const reset = () => {
clearInterval(int);
[milliseconds, seconds, minutes] = [0, 0, 0];
document.querySelector('.timerDisplay').innerHTML = '00 : 00';
}
function displayTimer() {
milliseconds += 10;
if (milliseconds == 1000) {
milliseconds = 0;
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
}
}
let m = minutes < 10 ? "0" + minutes : minutes;
let s = seconds < 10 ? "0" + seconds : seconds;
document.querySelector('.timerDisplay').innerHTML = `${m} : ${s}`;
}
const newGame = () => {
reset()
//some other code
start()
}
</script>
</head>
<body>
<button onClick="reset()" id="pauseTimer">Reset</button>
<button onClick="start()" id="startTimer">Start</button>
<button onClick="newGame()" id="newGame">New Game</button>
<p class="timerDisplay">00 : 00</p>
</body>
</html>
Related
I'm trying to create a 'Pomodoro' timer that takes user input and creates a timer based on how long somebody wants to use it for (for no reason other than to learn and use it for myself).
I'm finding that my for loops aren't behaving as I'd expect them to and when you look at the timer itself, it is counting down every second, however the timer itself actually reduces by 6 seconds for every one second counted.
I also can't seem to get the timer to move on to the next bit once it hits zero.
I did originally have breaks in the function so that it would move from the current time to the rest time but that didn't seem to do the trick.
In terms of the 6 seconds problem, I'm not even sure where to begin with that.
// set up a counter for how many times you want to set the pomodoro for - users will input how many cycles they want the program to go through.
const pomodoroQuestion = prompt("How many times would you like to use the pomodoro (1 Pomodoro = 3x 25 minute working burst, 2x 5 minute breaks and 1x 15 minute break)");
const pomodoroLength = parseInt(pomodoroQuestion);
for (let i = 0; i < pomodoroLength; i++) {
function startTimer() {
const currentTime = document.getElementById('pomodoroClock').innerHTML;
const timeArray = currentTime.split(/[:]+/);
let minutes = timeArray[0];
let seconds = secondsTimer((timeArray[1] - 1));
if (seconds === 59) {
minutes = minutes - 1;
}
if (minutes < 0) {
alert("Time's up");
}
document.getElementById('pomodoroClock').innerHTML = `${minutes}:${seconds}`;
setTimeout(startTimer, 1000); // Make the function countdown each second
}
// cycle through the seconds
function secondsTimer(sec) {
if (sec < 10 && sec >= 0) {
sec = `${0}${sec}`;
}
if (sec < 0) {
sec = 59;
}
return sec;
}
// the following loop will be what starts the actual pomodoro cycle.
for (let x = 0; x < 3; x++) {
// function starting a countdown timer for 25 minutes
document.getElementById('pomodoroClock').innerHTML = `${25}:${00}`;
startTimer();
if (x < 2) {
// this is where you're going to perform the function that'll allow for a 5 minute break
document.getElementById('pomodoroClock').innerHTML = `${05}:${00}`;
startTimer();
} else {
// this is where you're going to perform the function that'll allow for a 15 minute break
document.getElementById('pomodoroClock').innerHTML = `${15}:${00}`;
startTimer();
}
}
} // end pomodoroLength loop
<div id="pomodoroClock" class="timer"></div>
<script src="script/script.js"></script>
Where am I going wrong with this one? I feel like I'm just missing a few key pieces of understanding with projects like this, hence creating little practice projects to improve.
I think it's worthwhile to change your approach. What if you had a stand-alone countdown() function that displays minutes and seconds in a given target element, and notifies you when it's done?
That's easy to do with promises. You make a function that returns a new Promise, and you resolve() that promise when the time hits zero:
function countdown(minutes, seconds, targetElement) {
return new Promise(resolve => {
const tick = setInterval(function () {
// count down, display current time in targetElement
if (/* time's up */) {
// stop interval, call resolve()
}
}, 1000);
});
}
And since this function returns a promise, it becomes straightforward to chain multiple of those functions with async/await:
async function countdownSequence(...timers) {
for (let t of timers) {
await countdown(0, t, document.getElementById('target'));
}
alert('done!');
}
countdownSequence(5, 10, 5); // counts 5, 10, and 5 seconds, and then alerts 'done!'
Full implementation with a few extras. Note that for the sake of the example, instead of using your sequence 25, 5, 25, 5, 25, 15 for each round, I'm using 5, 2, 5, 2, 5, 3, and I'm using the seconds slot of the countdown function.
function countdown(minutes, seconds, targetElement) {
const pad = num => (num < 10 ? '0' : '') + num;
const display = () => targetElement.textContent = pad(minutes) + ':' + pad(seconds);
return new Promise(resolve => {
const tick = setInterval(function () {
seconds--;
if (seconds < 0) {
minutes--;
seconds = 59;
}
if (minutes < 0) {
clearInterval(tick);
resolve();
}
display();
}, 1000);
display();
});
}
async function pomodoro(numCycles, targetElement) {
targetElement.classList.add('active');
for (let i = 0; i < numCycles; i++) {
targetElement.classList.remove('work');
for (let minutes of [5, 2, 5, 2, 5, 3]) {
targetElement.classList.toggle('work');
await countdown(0, minutes, targetElement);
}
}
targetElement.classList.remove('active');
}
async function start() {
const cycles = parseInt(prompt("How many times would you like to use the pomodoro (1 Pomodoro = 3x 25 minute working burst, 2x 5 minute breaks and 1x 15 minute break)"), 10);
if (cycles > 0) {
await pomodoro(cycles, document.getElementById('pomodoroClock'));
alert("Finished!");
}
}
start();
#pomodoroClock {
display: none;
}
#pomodoroClock.active {
display: block;
color: blue;
}
#pomodoroClock.work {
color: green;
}
#pomodoroClock::after {
padding-left: 5px;
content: '(pause)';
}
#pomodoroClock.work::after {
padding-left: 5px;
content: '(work time)';
}
<div id="pomodoroClock"></div>
I'm trying to make a stopwatch. Here's the code:
var min = 0, sec = 0, censec = 0
$("#startBtn").on("click", function() { // when start button is clicked
$(this).hide(); // start is hidden
$("#stopBtn").show(); // stop is shown
setInterval(add, 10); // the censec will be increased every 10 millisecond.
$("#censec").text(censec);
})
function add() {
censec++;
if (censec == 100) {
censec = 0;
sec++;
if (sec == 60) {
sec = 0;
min++;
}
}
}
The problem is that setInterval() happens only at once. The censec only changes from 00 to 1. That's it.
P.S. I'm new to coding, so if there are other mistakes, please don't hesitate to tell me.
The setInterval calls to add will definitely repeat. But your code is only ever showing the value of censec once, when you start the timer.
If you want to update the display every hundredth of a second, put the code showing the value in add.
Separately, the code as it is in the question won't run at all, because it has a ReferenceError on the first line. Those ; should be ,.
Example (this also stores the timer's handle and clears the timer when you click the stop button):
var min = 0, sec = 0, censec = 0;
// Note ---^--------^
function add() {
censec++;
if (censec == 100) {
censec = 0;
sec++;
if (sec == 60) {
sec = 0;
min++;
}
}
$("#censec").text(censec);
}
var timer = 0;
$("#startBtn").on("click", function() { //when start button is clicked
$(this).hide(); //start is hidden
$("#stopBtn").show(); //stop is shown
timer = setInterval(add,10); //the censec will be increased every 10 millisecond.
});
$("#stopBtn").on("click", function() {
clearInterval(timer);
timer = 0;
$(this).hide();
$("#startBtn").show();
});
<input type="button" id="startBtn" value="Start">
<input type="button" id="stopBtn" value="Stop" style="display: none">
<div id="censec"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Note that although it may be mostly fine to use setInterval for displaying, using it to track the elapsed time is a bad idea; it frequently doesn't fire precisely.
Instead, record when you started
var start = Date.now();
...and then when the timer fires, figure out how long it's been since you started
var elapsed = Date.now() - start;
Then use the value (milliseconds) in elapsed to figure out your display.
Your variable declarations have ; instead of , .
Also checking numbers on equality should be done by using === but that is not the problem here.
Your also not updating the view in your timer. So updating of your html should also be in your function that is called by the timer.
If the goal is to use real seconds and milliseconds, I also suggest using the Date type because your timer will be late and not real-time. So still use the timer with the interval you like but in the add function you call the date object. You can replace the 3 vars for one datetime of type Date which will give you the granularity that you like.
var dateTimeStart = null, censecElement = null, timer = null;
$("#startBtn").on("click", function() {//when start button is clicked
$(this).hide(); // start is hidden
$("#stopBtn").show(); // stop is shown
if(timer === null) {
// timer was not started
dateTimeStart = new Date();
timer = setInterval(updateCensec, 10); //the censec will be increased every 10 millisecond.
console.log("Started timer");
}
});
$("#stopBtn").on("click", function() {//when stop button is clicked
$(this).hide(); // stop is hidden
$("#startBtn").show(); // start is shown
if(timer) {
// timer is started/running
clearInterval(timer);
console.log("Stopped timer");
}
timer = null;
});
function updateCensec() {
var sensec = 0, sec = 0, dateTimeNow = new Date(), diffMilliseconds = 0;
diffMilliseconds = dateTimeNow - dateTimeStart;
censec = parseInt((diffMilliseconds % 600 ) / 10); // convert milliseconds to centi seconds
sec = parseInt(diffMilliseconds / 600);
if(censecElement === null) {
censecElement = $("#censec");
}
censecElement.text(sec + ":" + censec);
}
I would like to suggest that you do not update your view every 10 milliseconds even if you want your stopwatch to show time in centiseconds.
I need a pause button for this timer. And unfortunately, it's not working.
When id "button-pause" is clicked, I need to set "var pressed = true".
If not, run the script below "if(!pressed){" and let "var pressed = false".
What's wrong?
HTML
<span id="button-pause">
<span class="music"></span>
<img src='img/icons/pause.png' alt='Stop music'>
</span>
JavaScript
var minutes = 60;
// Set number of seconds remaining after which to trigger a warning color
var warning = 59;
var pressed = false;
k = document.getElementById('button-pause');
k.onclick = function () {
pressed = true;
}
if(!pressed){
// Declare variables
var timerEl, seconds, timer;
// Get a reference to the HTML element
timerEl = document.getElementById('tim');
// Calculate total seconds
seconds = 1*minutes;
// Updates the timer element
function updateTimer() {
var m,s;
// Get whole minutes
m = Math.floor(seconds/60);
// Get left-over seconds
s = seconds % 60;
// Pad anything below 10 with a leading zero
s = (s < 10) ? "0"+s : s;
// Write time to HTML element
timerEl.innerHTML = m + ":" + s;
// Decrement seconds
seconds--;
// Add warning class when we hit threshold
if(seconds < warning) {
timerEl.classList.add('warning');
}
// Clear timer when we hit zero
if(seconds < 0) {
clearInterval(timer);
window.location.href='index.php?mode=workout&workoutid=test&status=done';
}
}
}
timer = setInterval(updateTimer, 1000);
Your first problem is using the same variable for two things
var pressed = false; <-- store a boolean
pressed = document.getElementById('button-pause'); <--override the boolean with the button
Second issue is the JavaScript code is not going to change when you click the button. It will set the variable, but the code that has already run will not update magically. You would need to add the code to some sort of function and trigger it when the button is clicked or when the page is loaded.
I have a game that I am making using only pure javascript. Instead of a GUI, It is more like the old command line games and uses a prompt for input.
One of the main components of it is the Clock, which is expressed in hours, and can be checked with the commmand "time" and tells them the value of the variable "time". Here is the code:
var timestrt = 1;
var timer = setInterval(function(){timestrt++;return timestrt;}, 86000); var time = timestrt;
After testing it, I realized that the clock was not changing. So I set it to 10 seconds instead of 86 to be sure that I was waiting long enough, and it still did not want to work
I know that it is probably caused by the prompt and constant alerts, but I am not sure even where to start for a workaround.
Any ideas?
Edit: is it possible to either
1. retrieve the timer from an external page
2. comparing it to an actual clock in real time or 3. Using a animated gif clock in the background and calculating the location of certain pixels as time?
Don't use the native prompts and dialogs, since they stop the script execution time. Instead use simulated ones, for example jQuery IU has prompts and dialog boxes that do not stop execution. Here is an example of that:
$(function() {
$( "#dialog" ).dialog();
var timestrt = 1;
var timer = setInterval(function(){
timestrt++;
var time = timestrt;
$("#time").text(time);
}, 1000);
});
Here is my workaround:
This code is called before the prompt is started:
function initTime() {
var now = new Date();
stS = now.getSeconds();
stM = now.getMinutes();
stH = now.getHours();
}
This is called after the prompt is done:
function computeTime() {
var now = new Date();
var reS = now.getSeconds();
var reM = now.getMinutes();
var reH = now.getHours();
var elapS = reS - stS;
var elapM = reM - stM;
var elapH = reH - stH;
if (elapM < 0) {
reM = reM + 60;
elapM = reM - stM;
}
if (elapS < 0) {
reS = reS + 60;
elapS = reS - stS;
}
Then I convert it to seconds to make it easier to check against:
var finalH = elapH * 3600;
var finalM = elapM * 60;
var finalS = finalM + elapS + finalH;
And check/change the time variable based on how many sets of 86 seconds has passed:
if (finalS > 86 && finalS < 172) {
time = 1;
}
if (finalS > 172 && finalS < 258) {
time = 2;
}
if (finalS > 258 && finalS < 344) {
time = 3;
}
if (finalS > 344 && finalS < 430) {
time = 4;
}
if (finalS > 430 && finalS < 516) {
time = 5;
}
if (finalS > 516) {
time = 6;
alert('5 A.M.');
alert('A clock is chiming...');
alert('6 A.M.');
playing = false;
alert('Thanks for playing! Goodbye!');
}
And that is my alternative to using a setinterval/timer behind multiple prompts and alerts. The last part probably wasn't needed, but since it answered my original question I included it.
Trying to make a simple count up timer in jQuery... this sort of works but is adding the numbers to the end of '0000' and I want it to go '0001' '0002' '0003' etc...
This is all happening in the jQuery onReady scope.
var i = '0000'
var timer = function doSomething ( )
{
i = i+= 1
$('.counter').text(i);
console.log(i);
}
setInterval (timer, 1000 );
Your "i" variable needs to be an integer. You can format it how you like when you want to print it somewhere.
$(document).ready(function() {
var i = 0;
var target = $('.counter');
var timer = function doSomething ( )
{
i++;
var output = pad(i,4);
target.text(output);
console.log(output);
}
setInterval (timer, 1000 );
});
function pad(number, length) {
var str = '' + number;
while (str.length < length) {
str = '0' + str;
}
return str;
}
Your current code is appending to a string, not addition to a number. It essentially looks like
i = '0000' + 1, i = '00001' + 1, i = '000011' + 1 ...
and so on. You'll need to keep it integer based to continue adding to the number. Here's an example with the formatting it looks like you wanted.
var pad = function(n) { return (''+n).length<4?pad('0'+n):n; };
jQuery.fn.timer = function() {
var t = this, i = 0;
setInterval(function() {
t.text(pad(i++));
}, 1000);
};
$('#timer').timer();
http://jsfiddle.net/jDaTK/
I would do something more like this:
// Make sure Date.now exists in your environment
Date.now = Date.now || function () { return Number(new Date()); };
var time = 0,
start = Date.now(),
intervalId;
intervalId = setInterval(function () {
var seconds, display;
// get the exact time since the timer was started
time = Date.now() - start;
// get the number or seconds, rounded down
seconds = Math.floor(time / 1000);
display = '' + seconds;
// pad the beginning of your display string with zeros
while (display.length < 4) {
display = "0" + display;
}
console.log(display);
}, 100);
setInterval is not exact. This code ensures that while the display could be up to nearly a second off (in theory), the actual time you are tracking is always the exact amount of time that has elapsed since you started the timer. But this code would update the display about once every tenth of a second, so it's not likely to ever be off more than a few milliseconds.
From here you can figure out smarter ways to update the display to ensure you have the level of accuracy you need. If this needs to be pretty accurate, then you could make sure you are displaying to the tenth of the second.
I really recommend the jQuery CountUp plugin. I tried a number of Javascript counters and this was one of the easiest to implement and came with lots of goodies:
<script type="text/javascript">
$(document).ready(function(){
$('#counter').countUp({
'lang':'en', 'format':'full', 'sinceDate': '22/07/2008-00::00';
});
});
</script>
<div id="counter"></div>