HTML: How to get multiple timers to combine - javascript

I am building a timer based on user inputs. Below is how to code should work:
User inputs:
Time on: 30s
Time off: 10s
Number of sets: 4
Number of exercises per set: 5
Rest in between sets: 30s
With the above user inputs the timer will do this:
Hopefully that makes sense on what It is supposed to do. I am currently trying to implement the rest in between sets. Does anyone know how I could make that happen with how my code currently is? Also totalTime already includes the amount of time the rest in between sets would add on if that helps.
var numsets = document.getElementById("userInput1");
var numex = document.getElementById("userInput2");
var numwork = document.getElementById("userInput3");
var numrest = document.getElementById("userInput4");
var numrestafterset = document.getElementById("userInput5");
var sets;
var OGtimeon;
var OGtimeoff;
var totalTime;
var timeRemaining;
var hasBeenStarted = false; // Boolean value to test what time to use
var isTimeON = true;
var timeon;
var timeoff;
var timeonRemaining;
var timeoffRemaining;
var setsRemaining;
var OGsets;
var Prepare;
var OGExPS;
var OGTOASets;
var ExercisePS;
var RestAfterS;
var Intervals
var j = 0;
var ExercisesRemaining;
var RestRemaining;
// function sleep(milliseconds) { //function I found online to create a sleep function
// const date = Date.now();
// let currentDate = null;
// do {
// currentDate = Date.now();
// } while (currentDate - date < milliseconds);
// }
// function updateRest() {
// if (RestAfterS > 0) {
// interval3 = setInterval(RestCount, 1000);
// }
// else {
// startTime();
// }
// function RestCount() {
// while (RestAfterS != 0) {
// RestAfterS--;
// }
// j = 0;
// }
function updatePrep() {
if (hasBeenStarted == false) {
Prepare = 5;
interval2 = setInterval(PrepCount, 1000);
}
else {
startTime();
}
}
function PrepCount() {
let seconds = parseFloat(Prepare) % 60;
if (Prepare == 0) {
clearInterval(interval2);
startTime();
}
else {
PWR.innerHTML = "Get Ready!";
textarea.innerHTML = Prepare;
console.log(Prepare);
Prepare--;
}
}
function startTime() {
// Set values in code
OGsets = numsets.value;
OGtimeon = numwork.value;
OGtimeoff = numrest.value;
OGTOASets = numrestafterset.value;
OGExPS = numex.value;
timeon = (hasBeenStarted)? timeonRemaining : OGtimeon;
timeoff = (hasBeenStarted)? timeoffRemaining : OGtimeoff;
sets = (hasBeenStarted)? setsRemaining : OGsets;
ExercisePS = (hasBeenStarted)? ExercisesRemaining : OGExPS;
RestAfterS = (hasBeenStarted)? RestRemaining : OGTOASets;
// How much time on timer
// Var = (expression)? true : false this is basically an if statement
totalTime = (hasBeenStarted)? timeRemaining : ((parseFloat(OGtimeon)*parseFloat(sets)*parseFloat(ExercisePS))+(parseFloat(OGTOASets)*(parseFloat(sets)-1))+(parseFloat(OGtimeoff)*(parseFloat(sets)*(parseFloat(ExercisePS)-1))));
Intervals = ((parseFloat(sets)*parseFloat(ExercisePS))+((parseFloat(sets)-1))+((parseFloat(sets)*(parseFloat(ExercisePS)-1))));
hasBeenStarted = true;
// Start timer
interval = setInterval(updateCountdown, 1000);
}
function updateCountdown() {
IntervalsLeft.innerHTML = Intervals;
setsLeft.innerHTML = sets;
var minutes= Math.floor (parseFloat(totalTime) / 60);
var seconds = parseFloat(totalTime) % 60;
if (seconds < 10) {
textareaRemaining.innerHTML = minutes + ":0" + seconds;
} else {
textareaRemaining.innerHTML = minutes + ":" + seconds;
}
// Update TimeON / Time OFF
if(isTimeON){
PWR.innerHTML = "Work!";
textarea.innerHTML = timeon;
timeon--;
if(timeon == 0){
isTimeON = false;
timeon = OGtimeon;
Intervals--;
IntervalsLeft.innerHTML = Intervals;
}
}
//BELOW IS THE AREA I AM STUCK ON
else{
textarea.innerHTML = timeoff;
timeoff--;
PWR.innerHTML = "Rest!";
if(timeoff == 0){
isTimeON = true;
timeoff = OGtimeoff;
j++;
Intervals--;
IntervalsLeft.innerHTML = Intervals;
if (j == OGExPS) {
sets--;
//updateRest();
j = 0;
}
}
}
if( totalTime == 0 ){
clearTimeout(interval);
hasBeenStarted = false;
console.log(sets);
sets--;
setsLeft.innerHTML = sets;
PWR.innerHTML = "OMG YOU'RE DONE";
}
totalTime--;
}
function updateRest() {
if (RestAfterS > 0) {
interval3 = setInterval(RestCount, 5000);
}
else {
startTime();
}
}
function RestCount() {
while (RestAfterS != 0) {
RestAfterS--;
PWR.innerHTML = "Set Rest!";
textarea.innerHTML = RestAfterS;
}
j = 0;
clearInterval(interval3);
}
function stop(){
timeRemaining = totalTime;
timeonRemaining = timeon;
timeoffRemaining = timeoff;
RestRemaining = RestAfterS;
ExercisesRemaining = OGExPS;
setsRemaining = sets;
clearTimeout(interval);
// document.getElementById("Counter").innerHTML = j;
}
p {
display: inline-flex;
align-items: center;
}
label {
float: left;
display: block;
}
#userInput1 {
display: flex;
margin-bottom: 10px;
}
#userInput2 {
display: flex;
margin-bottom: 10px;
}
#userInput3 {
display: flex;
margin-bottom: 10px;
}
#userInput4 {
display: flex;
margin-bottom: 10px;
}
#userInput5 {
display: flex;
margin-bottom: 10px;
}
#Prepare {
display: flex;
margin-bottom: 10px;
}
#sets {
display: flex;
margin-bottom: 10px;
}
#timeon {
display: flex;
margin-bottom: 10px;
}
#timeoff {
display: flex;
margin-bottom: 10px;
}
#TotalTime {
display: flex;
margin-bottom: 10px;
}
#Counter {
display: flex;
margin-bottom: 10px;
}
input {
height: 20px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-compatible" content="ie=edge" />
<title>Document</title>
<link rel="stylesheet" href="Countdown From Scratch.css" />
</head>
<script src="Countdown From Scratch.js" defer></script>
<body>
<label>Number of Sets </label>
<input id="userInput1" type="numsets" value = "0"/>
<label>Number of Exercises per Set </label>
<input id="userInput2" type="numex" value = "0"/>
<label>Time to Work </label>
<input id="userInput3" type="numwork" value = "0"/>
<label>Time to Rest </label>
<input id="userInput4" type="numrest" value = "0"/>
<label>Time Inbetween Sets </label>
<input id="userInput5" type="numrestafterset" value = "0"/>
<p id="Prepare"></p>
<div id="sets"> </div>
<div id="timeon"> </div>
<div id="timeoff"> </div>
<div id="TotalTime"> </div>
<textarea id="textarea" placeholder="00:00"></textarea>
<textarea id="PWR" placeholder="Hello!"></textarea>
<textarea id="textareaRemaining" placeholder="00:00"></textarea>
<textarea id="setsLeft" placeholder="00"></textarea>
<textarea id="IntervalsLeft" placeholder="00"></textarea>
<button onclick="updatePrep()">Start time</button>
<button onclick="stop()">Stop time</button>
</body>
</html>

I think I have a rough idea of what you are trying to accomplish here, but please let me know if I have misinterpreted anything.
Suppose you want to execute a sequence of effects with some duration in between them. To keep the example small, we'll use console.log as a stand-in for any effect you'd want to execute (for instance, setting the innerHTML of a DOM element).
As a first example, here we wait for 1 second, display "Hello", then wait for 1.5 seconds and display "There":
setTimeout(() => {
console.log('Hello');
setTimeout(() => {
console.log('There');
}, 1500);
}, 1000);
We can clarify this by describing the common parts as a function:
function logAfter(message, delay, callback) {
setTimeout(() => {
console.log(message);
if (callback) callback();
}, delay);
}
Then our example can be written as:
logAfter('Hello', 1000, () => {
logAfter('There', 1500);
});
As another example that is perhaps more relevant to your project, here is how we can create a "loop" that performs an effect multiple times:
function logNAfter(message, times, delay, callback) {
if (times === 0 && callback) {
callback();
}
else {
setTimeout(() => {
console.log(message);
logNAfter(message, times-1, delay, callback);
}, delay);
}
}
We could use this to display "Hi There!" 3 times, each separated by half a second, and then display "Goodbye!" once, 2 seconds later:
logNAfter('Hi There!', 3, 500, () => {
logAfter('Goodbye!', 2000);
});
In theory, you could create an arbitrarily-long sequence of appropriately spaced effects in this way. However, I should also mention that many would prefer to describe these effects as Promises instead:
function logAfter(message, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(message);
resolve();
}, delay);
});
}
function logNAfter(message, times, delay) {
if (times === 0) {
return Promise.resolve();
} else {
return logAfter(message, delay)
.then(() => logNAfter(message, times-1, delay));
}
}
The original examples then become:
logAfter('Hello', 1000)
.then(() => logAfter('There', 1500));
logNAfter('Hi There!', 3, 500)
.then(() => logAfter('Goodbye!', 2000));
This is arguably only a slight improvement over the callback-style approach above, but it is much clearer if you are able to use async/await:
async function logAfter(message, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(message);
resolve();
}, delay);
});
}
async function logNAfter(message, times, delay) {
for (let i = 0; i < times; ++i) {
await logAfter(message, delay);
}
}
async function demoSequence1() {
await logAfter('Hello', 1000);
await logAfter('There', 1500);
}
async function demoSequence2() {
await logNAfter('Hi There!', 3, 500);
await logAfter('Goodbye!', 2000);
}
Hope that helps!

I like Williams suggested of breaking it down into different timers that could be controlled easier by the user. However, using this approach, I personally ran into the issue of how to pause the timer.
I decided to take a slightly different approach. From the table given, there is a fairly clear pattern of how the timer needs to be set up.The timer generally alternates between "Workout time" and "Rest time" for the whole duration. Rest time can take 3 different values:
Rest time between exercises
Rest time between Sets
The workout is complete
Since all of these values are known when the the values are inputed, my approach is set up the course of the timer before starting the timer. This creates a little bit of setup time but I believe this is negotiable and will not impact performance heavily. To do this, I created a created an array before starting the timer. Each item in this array contains data including the total time remaining, the current set, the label (Working out or resting), and the label timer. After creating this array, we can just create a timer that is based off the number of items in the array. I hope that this little explanation helps you understand my solution a little bit better.
Index.html
<!DOCTYPE html>
<html>
<style>
td, th {
text-align: left;
padding: 8px;
}
</style>
<head>
<title>Timer</title>
</head>
<body>
<!-- Inputs -->
<h2>Inputs</h2>
<p>
<label for="sets-input">Number of Sets: </label>
<input type="number" id="sets-input" min="1" max="3600">
</p>
<p>
<label for="exercises-input">Number of Exersizes per Set: </label>
<input type="number" id="exercises-input" min="1" max="3600">
</p>
<p>
<label for="workout-input">Exersise Time: </label>
<input type="number" id="workout-input" min="1" max="3600">
</p>
<p>
<label for="exersiseRest-input">Rest between exersises: </label>
<input type="number" id="exersiseRest-input" min="1" max="3600">
</p>
<p>
<label for="setRest-input">Rest between Sets: </label>
<input type="number" id="setRest-input" min="1" max="3600">
</p>
<!-- Buttons -->
<p>
<button id="start-button">Start</button>
<button id="reset-button">Reset</button>
</p>
<!-- Timer Display -->
<h2>Outputs:</h2>
<table>
<tr>
<th>Total Time Remaining: </th>
<td id="timer-display">???</td>
</tr>
<tr>
<th>Set Number: </th>
<td id="set-display">???</td>
</tr>
<tr>
<th id="label-display">???</th>
<td id="labelTimer-display">???</td>
</tr>
</table>
<script src="tabada.js"></script>
</body>
</html>
Tabada.js
//-----------------------------------------------------------------------------------------------
// GLOBAL VARIABLES
//-----------------------------------------------------------------------------------------------
// HTML
var setsInput = document.getElementById("sets-input");
var exersisesInput = document.getElementById("exercises-input");
var workoutInput = document.getElementById("workout-input");
var exersiseRestInput = document.getElementById("exersiseRest-input");
var setRestInput = document.getElementById("setRest-input");
var timerDisplay = document.getElementById("timer-display");
var setDisplay = document.getElementById("set-display");
var labelDisplay = document.getElementById("label-display");
var labelTimerDisplay = document.getElementById("labelTimer-display");
var startButton = document.getElementById("start-button");
var resetButton = document.getElementById("reset-button");
// JavaScript
var sets = 2;
var exersises = 3;
var workout = 5;
var exersiseRest = 2;
var setRest = 3;
var totalTime = -1;
var myInterval = -1;
var tabadaArray = [];
var tabadaIndex = 0;
//-----------------------------------------------------------------------------------------------
// BUTTON FUNCTIONS
//-----------------------------------------------------------------------------------------------
// Start / Pause Button
startButton.addEventListener("click", function(event){
// Set up Tabada Timer
if (totalTime == -1){
collectInputs(); // Comment this line for testing without inputs
calculateTotalTime();
createTabadaArray();
}
// Start timer
if (myInterval == -1){
startButton.innerHTML = "Pause";
myInterval = setInterval(tabadaTimer, 1000);
}
// Pause timer
else{
startButton.innerHTML = "Start";
clearInterval(myInterval);
myInterval = -1
}
});
// Reset Button
resetButton.addEventListener("click", function(event){
// Stop Timer
clearInterval(myInterval);
// Refresh Timer Display
calculateTotalTime();
updateOutputs(totalTime, 1, 'Workout', workout);
totalTime=-1; // Alows user to change input values before clicking start button.
// Reset start / pause button
myInterval = -1;
startButton.innerHTML = "Start";
});
//-----------------------------------------------------------------------------------------------
// SETUP FOR TABADA TIMER
//-----------------------------------------------------------------------------------------------
function collectInputs(){
sets = parseFloat(setsInput.value);
exersises = parseFloat(exersisesInput.value);
workout = parseFloat(workoutInput.value);
exersiseRest = parseFloat(exersiseRestInput.value);
setRest = parseFloat(setRestInput.value);
}
function calculateTotalTime(){
let totalWorkoutTime = workout * exersises * sets;
let totalExersiseRest = exersiseRest * (exersises - 1) * sets;
let totalSetsRest = setRest * (sets - 1);
totalTime = totalWorkoutTime + totalExersiseRest + totalSetsRest;
}
function createTabadaArray() {
tabadaIndex = 0; // Global variable used for tabada timer
tabadaArray = [];
for( let set=1; set<=sets; set++ ){
for( let exersise=1; exersise<=exersises; exersise++){
// Workout
addTimeBlock(set, 'Workout', workout);
// Exersise Rest
if ( exersise < exersises){
addTimeBlock(set, 'Rest', exersiseRest);
}
// Set Rest
else if( set < sets){
addTimeBlock(set, 'Rest', setRest);
}
// Done
else{break;} // Very end exersize has no rest, so we must break the loop.
}
}
}
function addTimeBlock(set, label, labelTime) {
// Add a sub timer to the array (workout, exersice rest, or set rest)
for (let i=labelTime; i>0; i--) {
tabadaArray.push({
"totalTimeRemaining" : totalTime--,
"set" : set,
"label" : label,
"labelTimeRemaining" : i,
});
}
}
//-----------------------------------------------------------------------------------------------
// TABADA TIMER
//-----------------------------------------------------------------------------------------------
function tabadaTimer(){
// Still time left
if (tabadaIndex < tabadaArray.length){
let displayInfo = tabadaArray[tabadaIndex];
updateOutputs( displayInfo.totalTimeRemaining,
displayInfo.set,
displayInfo.label,
displayInfo.labelTimeRemaining );
tabadaIndex++;
}
// End of tabada timer
else{
clearInterval(myInterval); // stop timer
updateOutputs(0, 1, 'Rest', 0);
totalTime = -1
}
}
function updateOutputs(totalTimeRemaining, setNumber, label, labelTimeRemaining){
timerDisplay.innerHTML = convertSeconds(totalTimeRemaining);
setDisplay.innerHTML = setNumber;
labelDisplay.innerHTML = label;
labelTimerDisplay.innerHTML = convertSeconds(labelTimeRemaining);
}
function convertSeconds(s){
// Seconds -> mm:ss format
// Calculate
let minutes = Math.floor(s/60);
let seconds = s%60;
// Format
let formattedminutes = ("0" + minutes).slice(-2);
let formattedseconds = ("0" + seconds).slice(-2);
return formattedminutes + ':' + formattedseconds;
}

Related

Enqueue function to execute after currently running function is done executing (setTimeout)

I have a basic timer where a user puts in a number, then it counts down until it hits 0.
I want the user to put another number while the timer for the prev is still going on. When the timer for the prev number hits 0, a new timer for the recently entered number will begin. My code somehow has both timers running concurrently despite my uses of setInterval and setTimeout.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<script>
var isRunning = false;
var qNums = [];
var wrapFunction = function (fn, context, params) {
return function () {
fn.apply(context, params);
};
};
function q() {
var sec = document.getElementById("data").value;
if (!Number.isInteger(parseInt(sec))) {
document.getElementById("timer").innerHTML = "Not a number!";
return;
} else if (parseInt(sec) < 0) {
document.getElementById("timer").innerHTML = "Invalid timer setting!";
return;
}
qNums.push(wrapFunction(countDown, this, [sec]));
while (qNums) {
qNums.shift()();
}
}
function countDown(sec) {
var sec = document.getElementById("data").value;
var ms = 100;
isRunning = true;
document.getElementById("timer").innerHTML = "";
document.getElementById("btn").innerHTML = "Ticking!";
var interval = setInterval(function () {
if (ms == 100) {
document.getElementById("timer").innerHTML = sec + ".00";
} else {
document.getElementById("timer").innerHTML = sec + "." + ms;
}
ms -= 10;
if (ms < 0) {
sec--;
ms = 100;
}
if (sec < 0) {
document.getElementById("data").value = "";
document.getElementById("btn").innerHTML = "Start";
document.getElementById("timer").innerHTML = "Countdown complete";
isRunning = false;
clearInterval(interval);
}
}, 100);
}
</script>
<body>
<h1>Timer</h1>
<label>Timer Duration: </label><input id="data" />
<button id="btn" onclick="countDown()">Start</button>
<p id="timer"></p>
</body>
</html>
q() is my awful attempt at trying to implement this. countDown() is the standalone implementation of the countdown, separate from this functionality.
EDIT: Why does the snippet not run my code but the browser does???? Not sure how to fix this
Good try, but each interval has no way of triggering the next one to start with a callback, and without that, they'll all run concurrently. Pass the q.shift()() in as a callback to the timer function which can be invoked when the timer runs out alongside clearTimeout, or write a loop and only run the 0-th timer if it exists.
Another problem: setTimeout is often mistaken to be perfectly accurate, but this is an incorrect assumption. The ms parameter only guarantees the timer will be invoked no sooner than the duration specified. The consequence of this is that it will accumulate drift. A more accurate approach is to use a date object to check the system's time.
Here's a proof-of-concept using the polling version:
const enqueueTimer = () => {
const sec = +els.data.value;
if (!Number.isInteger(sec)) {
els.timer.innerHTML = "Not a number!";
}
else if (sec < 0) {
els.timer.innerHTML = "Invalid timer setting!";
}
else {
timers.push({duration: sec * 1000});
}
};
const updateTimers = () => {
if (!timers.length) {
return;
}
const {duration, start} = timers[0];
const now = new Date();
if (!start) {
timers[0].start = now;
}
const elapsed = now - start || 0;
const remaining = duration - elapsed || 0;
const sec = remaining / 1000;
const ms = remaining % 1000;
els.timer.innerHTML = `${~~sec}.${("" + ms)
.slice(0, 2).padEnd(2)}`;
els.btn.innerHTML = "Ticking!";
if (elapsed >= duration) {
timers.shift();
if (timers.length) {
timers[0].start = new Date(start.getTime() + duration);
}
else {
els.data.value = "";
els.btn.innerHTML = "Start";
els.timer.innerHTML = "Countdown complete";
}
}
};
const els = {
btn: document.getElementById("btn"),
data: document.getElementById("data"),
timer: document.getElementById("timer"),
};
els.btn.addEventListener("click", enqueueTimer);
const timers = [];
setInterval(updateTimers, 100);
<h1>Timer</h1>
<label>Timer Duration: <input id="data" /></label>
<button id="btn">Start</button>
<p id="timer"></p>
If it bothers you that the interval always runs, feel free to save the interval id, add a clearInterval() on the id when all the timers expire and kick off a new interval when a fresh timer is created.

How can I fix the stop-start process within this Javascript stopwatch-clock?

I have a JavaScript stopwatch here, I require the start-stop button to keep the same time when continuing.
Currently, if I stop and continue the clock diff is something ridiculous such as '-19330839:-3:-53'
Can anyone explain how this is fixed?
I have various method stopwatches made; however I would rather use real date time instead of a counter, this is because (I have tested after being made aware of this) that counters are very inaccurate over a period of time.
Any help is much appreciated.
html:
Please ignore the reset button for now. I will configure this later.
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst1" id='btnRst1' value="Reset" onclick="resetclock();"/>
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
JS:
const outputElement = document.getElementById("outputt");
var startTime = 0;
var running = false;
var splitcounter = 0;
function startstop() {
if (running == false) {
running = true;
startTime = new Date(sessionStorage.getItem("time"))
if (isNaN(startTime)) startTime = Date.now();
startstopbutton.value = 'Stop';
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
updateTimer();
} else {
running = false;
logTime();
startstopbutton.value = 'Start';
document.getElementById("outputt").style.backgroundColor = "#B3321B";
}
}
function updateTimer() {
if (running == true) {
let differenceInMillis = Date.now() - startTime;
sessionStorage.setItem("time", differenceInMillis)
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
requestAnimationFrame(updateTimer);
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
I just need the timer to continue on from where it was stopped at.
Issue with your code:
You start with initial value for sessionStorage as Date.now but then save difference on update.
You interact a lot with session storage. Any communication with external API is expensive. Instead use local variables and find an event to initialise values.
Time difference logic is a bit off.
Date.now - startTime does not considers the difference between stop action and start action.
You can use this logic: If startTime is defined, calculate difference and add it to start time. If not, initialise it to Date.now()
Suggestions:
Instead of adding styles, use classes. That will help you in reset functionality
Define small features and based on it, define small functions. That would make reusability easy
Try to make functions independent by passing arguments and only rely on them. That way you'll reduce side-effect
Note: as SO does not allow access to Session Storage, I have removed all the related code.
const outputElement = document.getElementById("outputt");
var running = false;
var splitcounter = 0;
var lastTime = 0;
var startTime = 0;
function logTime() {
console.log('Time: ', lastTime)
}
function resetclock() {
running = false;
startTime = 0;
printTime(Date.now())
applyStyles(true)
}
function applyStyles(isReset) {
startstopbutton.value = running ? 'Stop' : 'Start';
document.getElementById("outputt").classList.remove('red', 'green')
if (!isReset) {
document.getElementById("outputt").classList.add(running ? 'red' : 'green')
}
}
function startstop() {
running = !running;
applyStyles();
if (running) {
if (startTime) {
const diff = Date.now() - lastTime;
startTime = startTime + diff;
} else {
startTime = Date.now()
}
updateTimer(startTime);
} else {
lastTime = Date.now()
logTime();
}
}
function printTime(startTime) {
let differenceInMillis = Date.now() - startTime;
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
}
function updateTimer(startTime) {
if (running == true) {
printTime(startTime)
requestAnimationFrame(() => updateTimer(startTime));
}
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
.red {
background-color: #2DB37B
}
.green {
background-color: #B3321B
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst1" id='btnRst1' value="Reset" onclick="resetclock();" />
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
simple stopwatch example
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<input class="startstop" style="width: 120px;" type="button" value="Start" onclick="startstop();">
<input class="reset" style="width: 120px;" type="button" value="Reset" onclick="reset();"/>
<div class="timerClock" value="00:00:00">00:00:00</div>
<script type="text/javascript">
var second = 0
var minute = 0
var hour = 0
var interval
var status = false
var element = document.querySelector('.startstop')
var clock = document.querySelector('.timerClock')
var string = ''
function startstop()
{
if(status == 'false')
{
element.value = 'Stop'
clock.style.backgroundColor = "#2DB37B";
status = true
interval = setInterval(function()
{
string = ''
second += 1
if(second >= 60)
{
minute += 1
second = 0
}
if(minute >= 60)
{
hour += 1
minute = 0
}
if(hour < 10)
string += `0${hour}:`
else
string += `${hour}:`
if(minute < 10)
string += `0${minute}:`
else
string += `${minute}:`
if(second < 10)
string += `0${second}`
else
string += `${second}`
clock.innerHTML = string
},1000)
}
else
{
clock.style.backgroundColor = "#B3321B";
element.value = 'Start'
status = false
clearInterval(interval)
}
}
function reset()
{
second = 0
minute = 0
hour = 0
status = false
element.value = 'Start'
clearInterval(interval)
clock.innerHTML = `00:00:00`
clock.style.backgroundColor = "transparent";
}
</script>
</body>
</html>
One thing to know about requestAnimationFrame is that it returns an integer that is a reference to the next animation. You can use this to cancel the next waiting animation with cancelAnimationFrame.
As mentioned by #Rajesh, you shouldn't store the time each update, as it will stop the current process for a (very) short while. Better in that case to fire an event, preferably each second, that will wait until it can run. I haven't updated the code to take that into account, I only commented it away for now.
It's also better to use classes than updating element styles. I wrote sloppy code that overwrites all classes on the #outputt element (it's spelled "output"). That's bad programming, because it makes it impossible to add other classes, but it serves the purpose for now. #Rajesh code is better written for this purpose.
I added two variables - diffTime and animationId. The first one corrects startTime if the user pauses. The second one keeps track if there is an ongoing timer animation.
I refactored your style updates into a method of its own. You should check it out, because it defines standard values and then changes them with an if statement. It's less code than having to type document.getElementById("outputt").style... on different rows.
I also added a resetclock method.
const outputElement = document.getElementById("outputt");
var startTime = 0;
var diffTime = 0;
var animationId = 0;
function startstop() {
const PAUSED = 0;
let paused = animationId == PAUSED;
//diffTime = new Date(sessionStorage.getItem("time")) || 0;
startTime = Date.now() - diffTime;
if (paused) {
updateTimer();
} else {
cancelAnimationFrame(animationId);
animationId = PAUSED;
}
updateTimerClass(paused);
}
function updateTimerClass(paused) {
var outputClass = 'red';
var buttonText = 'Start';
if (paused) {
outputClass = 'green';
buttonText = 'Stop';
}
startstopbutton.value = buttonText;
outputElement.classList = outputClass;
}
function updateTimer() {
let differenceInMillis = Date.now() - startTime;
//sessionStorage.setItem("time", differenceInMillis)
let {
hours,
minutes,
seconds
} = calculateTime(differenceInMillis);
let timeStr = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
outputElement.innerText = timeStr;
diffTime = differenceInMillis;
animationId = requestAnimationFrame(updateTimer);
}
function calculateTime(milliS) {
const SECONDS = 1000; // should be 1000 - only 10 to speed up the timer
const MINUTES = 60;
const HOURS = 60;
const RESET = 60;
let hours = Math.floor(milliS / SECONDS / MINUTES / HOURS);
let minutes = Math.floor(milliS / SECONDS / MINUTES) % RESET;
let seconds = Math.floor(milliS / SECONDS) % RESET;
return {
hours,
minutes,
seconds
};
}
function pad(time) {
return time.toString().padStart(2, '0');
}
function resetclock() {
let paused = animationId == 0;
startTime = Date.now();
diffTime = 0;
if (paused) {
const REMOVE_ALL_CLASSES = '';
outputElement.className = REMOVE_ALL_CLASSES;
outputElement.innerText = '00:00:00';
}
}
#outputt.green {
background-color: #2DB37B;
}
#outputt.red {
background-color: #B3321B;
}
<input id="startstopbutton" class="buttonZ" style="width: 120px;" type="button" name="btn" value="Start" onclick="startstop();">
<input id="resetbutton" class="buttonZ" style="width: 120px;" type="button" name="btnRst1" id='btnRst1' value="Reset" onclick="resetclock();"/>
<div id="outputt" class="timerClock" value="00:00:00">00:00:00</div>
class Stopwatch {
constructor(display, results) {
this.running = false;
this.display = display;
this.results = results;
this.laps = [];
this.reset();
this.print(this.times);
}
reset() {
this.times = [ 0, 0, 0 ];
}
click(){
var x=document.getElementById('ctrl');
if(x.value=="start"){
this.start();
x.value="stop";
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
}
else{
x.value="start";
this.stop();
document.getElementById("outputt").style.backgroundColor = "#B3321B";
}
}
start() {
if (!this.time) this.time = performance.now();
if (!this.running) {
this.running = true;
requestAnimationFrame(this.step.bind(this));
}
}
stop() {
this.running = false;
this.time = null;
}
resets() {
document.getElementById("outputt").style.backgroundColor = "#2DB37B";
if (!this.time) this.time = performance.now();
if (!this.running) {
this.running = true;
requestAnimationFrame(this.step.bind(this));
}
this.reset();
}
step(timestamp) {
if (!this.running) return;
this.calculate(timestamp);
this.time = timestamp;
this.print();
requestAnimationFrame(this.step.bind(this));
}
calculate(timestamp) {
var diff = timestamp - this.time;
// Hundredths of a second are 100 ms
this.times[2] += diff / 1000;
// Seconds are 100 hundredths of a second
if (this.times[2] >= 100) {
this.times[1] += 1;
this.times[2] -= 100;
}
// Minutes are 60 seconds
if (this.times[1] >= 60) {
this.times[0] += 1;
this.times[1] -= 60;
}
}
print() {
this.display.innerText = this.format(this.times);
}
format(times) {
return `\
${pad0(times[0], 2)}:\
${pad0(times[1], 2)}:\
${pad0(Math.floor(times[2]), 2)}`;
}
}
function pad0(value, count) {
var result = value.toString();
for (; result.length < count; --count)
result = '0' + result;
return result;
}
function clearChildren(node) {
while (node.lastChild)
node.removeChild(node.lastChild);
}
let stopwatch = new Stopwatch(
document.querySelector('.stopwatch'),
document.querySelector('.results'));
<input type="button" id="ctrl" value="start" onClick="stopwatch.click();">
<input type="button" value="Reset" onClick="stopwatch.resets();">
<div id="outputt" class="stopwatch"></div>

How would you increase a variables value every second using a function?

I am trying to make a variable increase every second. What should I include inside the function autoClicker, so that the variable clicks increase by 1 every second? Also, if there are any more problems in the code, could you point them out to me? Sorry if this question seems basic, I am still quite new to JavaScript.
// The variable we are trying to increase
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clicks += upgrade1;
document.getElementById("clicks").innerHTML = clicks;
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
autoClicker()
} else {
alert = "Sorry, you don't have enough clicks to buy this";
}
}
// The function I will use to increase clicks
function autoClicker() {}
You could create an AutoClicker class that has a start, pause, ad update function. It will be in charge of managing the setInterval id.
Edit: I updated it to include upgrade buttons and the target can now be manually clicked.
const upgrades = [{
cost: 50,
rate: 2
}, {
cost: 100,
rate: 4
}];
const main = () => {
const target = document.querySelector('.auto-clicker');
const span = document.querySelector('.info > span');
const btn = document.querySelector('.btn-toggle');
const clicker = new AutoClicker(target, 1000, (clicks) => {
span.textContent = clicks;
}).start();
initializeUpgrades(clicker, upgrades);
btn.addEventListener('click', (e) => {
e.target.textContent = clicker.isRunning() ? 'Start' : 'Pause';
clicker.toggle();
});
};
const initializeUpgrades = (clicker, upgrades) => {
const upgradeContainer = document.querySelector('.upgrades');
upgrades.forEach(upgrade => {
const btn = document.createElement('button');
btn.textContent = upgrade.cost;
btn.value = upgrade.rate;
btn.addEventListener('click', (e) => {
let cost = parseInt(e.target.textContent, 10);
let value = parseInt(e.target.value, 10);
if (clicker.clicks >= cost) {
clicker.clicks -= cost;
clicker.step = value
} else {
console.log(`Cannot afford the ${value} click upgrade, it costs ${cost} clicks`);
}
});
upgradeContainer.appendChild(btn);
});
};
class AutoClicker {
constructor(target, rate, callback) {
if (typeof target === 'string') {
target = document.querySelector(target);
}
this.target = target;
this.rate = rate;
this.callback = callback;
this.init();
}
init() {
this.step = 1;
this.clicks = 0;
this._loopId = null;
this.target.addEventListener('click', (e) => {
this.update();
});
}
isRunning() {
return this._loopId != null;
}
toggle() {
this.isRunning() ? this.pause() : this.start();
}
update() {
this.clicks += this.step;
if (this.callback) {
this.callback(this.clicks);
}
}
start() {
this.update(); // Update immediately
this._loopId = setInterval(() => this.update(), this.rate);
return this;
}
pause() {
clearInterval(this._loopId);
this._loopId = null;
return this;
}
}
main();
.wrapper {
width: 10em;
text-align: center;
border: thin solid grey;
padding: 0.5em;
}
.auto-clicker {
width: 4em;
height: 4em;
background: #F00;
border: none;
border-radius: 2em;
}
.auto-clicker:focus {
outline: none;
}
.auto-clicker:hover {
background: #F44;
cursor: pointer;
}
.info {
margin: 1em 0;
}
.upgrades {
display: inline-block;
}
.upgrades button {
margin-right: 0.25em;
}
<div class="wrapper">
<button class="auto-clicker"></button>
<div class="info">Clicks: <span class="clicks"></span></div>
<button class="btn-toggle">Pause</button>
<div class="upgrades"></div>
</div>
// The variable we are trying to increase
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clicks += upgrade1;
document.getElementById("clicks").innerHTML = clicks;
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
autoClicker()
} else {
alert = "Sorry, you don't have enough clicks to buy this";
}
}
// The function I will use to increase clicks
setInterval(function(){ clicks++;console.log(clicks); }, 1000);
Use setInterval to run a function at a specified interval. This will run increaseClicks every 1000 milliseconds (every second):
function increaseClicks() {
clicks++;
}
var interval = setInterval(increaseClicks, 1000);
Use clearInterval to stop running it:
clearInterval(interval);
You can omit var interval = if you don't want to use clearInterval:
setInterval(increaseClicks, 1000);
There might be several things to improve this code
the use of textContent is preferable to innerHTML, it checks if there are no html tags in the text
then using inline functions like ()=>{} are more useful but in this program it does'nt make a difference, where you to use it in object oriented context you could use it several ways
you don't need document.getElementById, you could just use id.
And finaly (this is just à random tip which has nothing to do with much of anything) you may consider branchless programming because ifs are expensive.
Stackoverflow Branchless Programming Benefits
But anyways you should have fun :)
var clicks = 0;
var upgrade1 = 1;
function getClicks() {
clk.textContent = (clicks += upgrade1);
};
function buyAutoClicker() {
if (clicks >= 50) {
clicks -= 50
setInterval(()=>{getClicks();},1000);
} else {
alert("Sorry, you don't have enough clicks to buy this");
}
}
clk.onclick=()=>{getClicks();};
b.onclick=()=>{buyAutoClicker();};
html,body{height:100%;width:100%;margin:0;}
p{height:50px;width:50px;background:red;}
<p id="clk"></p>
<p id="b"></p>

JS countdown timer - Pause function

Here's a simple countdown timer that counts from 9 down to 0.
The countdown works fine. But what if I want to pause it mid-flow and then restart from where it was paused?
I have tried (see code below) to interrupt the countdown, save the number it was at, and then restart the function from the new number. But the countdown goes haywire, and I can't see why. Any ideas?
PS. I could cut and paste a timer from elsewhere, but I'm doing this for the learning experience. I'm sure there are better ways to code a countdown timer in JS, but it's bugging me that I can't make THIS way work and think I must be missing something obvious.
Many thanks
var currentTimeInt = 10;
var minn = [];
var stop = 0;
// stop
function stopCounter() {
currentTime = document.getElementById('mins').textContent; // grabs the number of minutes at moment of pause.
stop = 1;
}
// restart
function restart() {
stop = 0;
currentTimeInt = parseInt(currentTime, 10); // converts that number into an integer we can use
document.getElementById("mins").innerHTML=currentTimeInt;
newMinutes(); // restarts the newMinutes function with the start time currentTimeInt set to the time the counter stopped at
}
function newMinutes() {
document.getElementById('mins').innerHTML= currentTimeInt; // displays the counter
for (aa = currentTimeInt-1; aa >= 0; aa--) {
minn.push(aa); // builds an array of numbers in descending order
document.getElementById('mins').innerHTML= minn[aa];
for (let bb=1; bb<=currentTimeInt; bb++) {
if (bb<currentTimeInt) {
setTimeout( function timer(){
if (stop == 0) { // checks if "stop!" has been clicked and returns false to stop the function if that is the case
document.getElementById('mins').innerHTML= minn[bb];
console.log(minn[bb]);
}
else {return false;}
}, bb*1000 );
}
}
}
console.log(currentTimeInt + " the end");
}
<span>Minutes: </span><span id= "mins"></span>
<button onclick="newMinutes()">Go!</button>
<button onclick="stopCounter()">Stop!</button>
<button onclick="restart()">Reset!</button>
You may try this as an example:
var timerId;
var counter;
function start() {
console.log('start');
if (!counter) {
reset();
} else {
loop();
}
}
function pause() {
console.log('pause');
if (timerId) {
clearInterval(timerId);
timerId = null;
}
}
function reset() {
console.log('reset');
pause();
counter = 10;
loop();
}
function loop() {
timerId = setInterval(function() {
if (0 >= counter) {
pause();
return;
}
console.log('counter', counter);
counter--;
}, 500);
}
<button onclick='start();'>Start</button>
<button onclick='pause();'>Pause</button>
<button onclick='reset();'>Reset</button>
Here is my little Countdown with START, PAUSE, RESUME, STOP & RESET features:
var jqcd_start_id = 'input#jqcd_start';
var jqcd_time_id = 'input#jqcd_time';
var jqcd_count_id = 'span#jqcd_count';
var jqcd_end_message = 'Time is up!';
var jqcd_countdown = '';
var jqcd_status = 'stopped';
var jqcd_current = '';
function jqcd(action){
if (action == 'start') {
if (jqcd_status == 'stopped') {
jqcd_updtv(jqcd_start_id, 'Pause');
jqcd_status = 'running';
jqcd_current = jqcd_countdown;
jqcd_updtt(jqcd_count_id, jqcd_countdown);
}
else if (jqcd_status == 'running') {
jqcd_updtv(jqcd_start_id, 'Resume');
jqcd_status = 'paused';
}
else if (jqcd_status == 'paused') {
jqcd_updtv(jqcd_start_id, 'Pause');
jqcd_status = 'running';
}
}
else if (action == 'stop') {
jqcd_updtv(jqcd_start_id, 'Start');
jqcd_status = 'stopped';
jqcd_updtt(jqcd_count_id, jqcd_end_message);
}
else if (action == 'reset') {
jqcd_updtv(jqcd_start_id, 'Start');
jqcd_status = 'stopped';
jqcd_updtt(jqcd_count_id, jqcd_countdown);
}
var a = jqcd_current.split(":");
var m = a[0];
var s = (a[1] - 1);
if (s < 0) {
if (parseInt(m) == 0) {
jqcd_updtv(jqcd_start_id, 'Start');
jqcd_status = 'stopped';
jqcd_updtt(jqcd_count_id, jqcd_end_message);
}
else {
m = m - 1;
s = 59;
}
}
if(s >= 0){
setTimeout(function(){
if (jqcd_status == 'running') {
m = (parseInt(m) < 10)? "0" + parseInt(m): m;
s = (parseInt(s) < 10)? "0" + parseInt(s): s;
jqcd_updtt(jqcd_count_id, m + ":" + s);
jqcd_current = m + ":" + s;
jqcd('');
}
}, 1000);
}
}
function jqcd_updtv(selector, value) {
if (selector != '') {
$(selector).val(value);
}
}
function jqcd_updtt(selector, value) {
if (selector != '') {
$(selector).text(value);
}
}
$(document).ready(function() {
jqcd_countdown = $(jqcd_time_id).val();
jqcd_updtt(jqcd_count_id, jqcd_countdown);
$(jqcd_time_id).keyup(function() {
jqcd_countdown = $(jqcd_time_id).val();
jqcd_updtt(jqcd_count_id, jqcd_countdown);
jqcd_updtv(jqcd_start_id, 'Start');
jqcd_status = 'stopped';
});
});
span#jqcd_count {
font-size: 20pt;
font-weight: bold;
}
input#jqcd_start,
input#jqcd_stop,
input#jqcd_reset {
font-size: 12pt;
font-weight: bold;
}
input#jqcd_start,
input#jqcd_stop,
input#jqcd_reset {
width: 100px;
}
span#jqcd_count {
font-family: "Lucida Console", Monaco, "Courier New", Courier, monospace !IMPORTANT;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="jqcd_count">00:30</span><br><br>
<input type="button" id="jqcd_start" value="Start" onClick="jqcd('start')" />
<input type="button" id="jqcd_stop" value="Stop" onClick="jqcd('stop')" />
<input type="button" id="jqcd_reset" value="Reset" onClick="jqcd('reset')" /><br><br>
<input type="text" id="jqcd_time" value="00:10" />
<br><br>
It is pretty simple to customize. The first four variables in the JavaScript code can be adapted to work with your specific HTML.
If you want an action to take place every second, add your lines of code inside of the "jqcd_updtt" function.
The CSS is completely optional, of course.
The Countdown start time is set dynamically by altering the value in the "jqcd_time" field. If, however, you want to set a static value for the Countdown starting point, you can alter the variables inside the "$(document).ready(function() {" function at the end of the JavaScript code.
PS.
This Countdown has no maximum limit for minutes or seconds
What about keeping it KISS!
let i = 9,j
function chrono(){
if (i>=0){
now.innerText = i--
}
}
<h1><div id="now">⏰ Ready!</div>
<button onclick="setInterval(function(){ chrono() }, 1000);this.style.display='none'">Start</button>
<button onclick="i=10">Reset</button>
<button onclick="j=i;i=-1">Pause</button>
<button onclick="i=j">Continue</button>
This is the most basic example ready to be expanded, mostly there is no clearInterval.
The KISS principle states that most systems work best if they are kept
simple rather than made complicated; therefore simplicity should be a
key goal in design and unnecessary complexity should be avoided.
https://en.wikipedia.org/wiki/KISS_principle
Therefore, js incrementation and setInterval seems easy but they hide complex things.
An other approach using date.now() that provide an accurate unix timestamp based on the system clock, and the web audio api for beeping.
i = Date.now();j=i+10000;z.innerText="Target #"+j
function d(){
if(now.innerText < j){
now.innerText = Date.now()
k(3,603,80)
}
if(now.innerText > j){
now.innerHTML = "<b>TIME TRAVEL COMPLETE!</b>"
k(8,728,100)
}
}
setInterval(function(){ d() }, 100)
a=new AudioContext()
function k(w,x,y){
v=a.createOscillator()
u=a.createGain()
v.connect(u)
v.frequency.value=x
v.type="square"
u.connect(a.destination)
u.gain.value=w*0.01
v.start(a.currentTime)
v.stop(a.currentTime+y*0.001)
}
EPOCH: <out id="now"></out><h6 id="z">
The first issue here is that currentTime isn't globally defined, so it can't be accessed from within restart. Just put var currentTime; at the start of your file.
But you have another serious breaking issue in that you're using setTimeout in a really awkward way. You're creating multiple timeouts all at once and giving them a delay based on their relation tocurrentTimeInt. This has two problems. For one the use of two for loops isn't very efficient and also seems redundant as your inner for loop is just going to count up to the currentTimeInt anyway.
Secondly, you never clear (and probably won't be able to clear) the timeouts. That means when you restart your timer after pausing if any timeouts hadn't yet been fired, then your program will run those and make the minutes jump back and forth between the old timeouts and the new ones you create after unpausing.
I know in your comment you said you wanted to get this to work because you basically did the whole thing yourself, but it may not be worth continuing down this road. After looking at it some I think fixing your program would require it to be restructured, or else require it to be hacked in a way that makes it pretty inefficient. And if you're someone who's just learning Javascript, it's probably better to just start over and do it the right way anyway.
Below is an example of a better way using setInterval rather than setTimeout, but feel free to just try and figure it out on your own.
(There are ways you can improve the functionality of the code below, but it should be enough to get you the general idea)
var startTimeInt = 10;
var currentTimeInt = startTimeInt;
var interval = undefined;
// start the timer
function startCounter() {
if(!interval){
document.getElementById('mins').innerHTML = currentTimeInt;
interval = setInterval(newNumber, 1000) // set an interval
}
}
// stop
function stopCounter() {
// clear the interval
clearInterval(interval)
interval = undefined;
}
// reset the timer
function resetCounter(){
currentTimeInt = startTimeInt;
document.getElementById('mins').innerHTML = currentTimeInt;
//stopCounter(); startCounter();
}
// change the time and handle end of time event
function newNumber(){
currentTimeInt--; // decrement the current time
document.getElementById('mins').innerHTML = currentTimeInt;
if(currentTimeInt == 0){
console.log("Done");
stopCounter();
}
}
<span>Minutes: </span><span id= "mins"></span>
<button onclick="startCounter()">Go!</button>
<button onclick="stopCounter()">Stop!</button>
<button onclick="resetCounter()">Reset!</button>
Here is a working Snippet..
var paused = false;
var started = false;
var stopped = true;
var currentCount = 0;
var running = false;
interval = 1000;
maxCount = 10;
function start() {
if (stopped){
started = true;
paused= false;
stopped = false;
currentCount = maxCount;
loop(); running = true;
return;
}
paused= false;
}
function pause() {
paused= true;
}
function stop(){
paused = false;
started = false;
stopped = true;
running = false;
currentCount = 0;
}
function update(item){
document.getElementById("status").innerHTML = item;
//console.log(item);
--currentCount;
if(currentCount < 0){stop()}
}
function reset() {
currentCount = maxCount;
document.getElementById("status").innerHTML = currentCount;
}
function loop(){
if (!stopped){
if (!paused){update(currentCount);}
setTimeout(function(){loop()}, interval)
}
}
<button onclick='start();'>Start</button>
<button onclick='pause();'>Pause</button>
<button onclick='reset();'>Reset</button>
<button onclick='stop();'>Stop</button>
<div id="status"></div>
for anyone who want to re-use the code, simply change the value of timer and the render function to fit your project
var timer= 10;
var intervalID
// pause/stop
function stopTimer() {
clearInterval(intervalID);
intervalID= null;
}
// restart
function restart() {
stopTimer();
timer= 10;
render();
// Go(); //Optional
}
// start/resume
function Go() {
if(intervalID){
//if interval already created previously, exit function
return
}
intervalID = setInterval(
() => {
if(timer< 1){
//escape from interval so that counter dont go below 0
return stopTimer();
}
timer--;
render();
}, 1000); //1000 milisecond == 1 second
}
function render(){
// it is ok to run render redundantly as it does not mutate the data
// feel free to change this to fit your needs
console.log(timer);
document.getElementById("mins").innerHTML=timer;
}
render() //render once on load
<span>Minutes: </span><span id= "mins"></span>
<button onclick="Go()">Go!</button>
<button onclick="stopTimer()">Stop!</button>
<button onclick="restart()">Reset!</button>

How to create a stopwatch using JavaScript?

if(stopwatch >= track[song].duration)
track[song].duration finds the duration of a soundcloud track.
I am looking to create a stopwatch function that starts counting milliseconds when you click on the swap ID stopwatch so that when the function has been "clicked" for a certain amount of time the if function will do something. In my case replace an image. And also that the function will reset it itself when clicked again.
so like stopwatch = current time - clicked time How can I set up the clicked time
current time = new Date().getTime(); ? And is this in milliseconds?
$('#swap').click(function()...
You'll see the demo code is just a start/stop/reset millisecond counter. If you want to do fanciful formatting on the time, that's completely up to you. This should be more than enough to get you started.
This was a fun little project to work on. Here's how I'd approach it
var Stopwatch = function(elem, options) {
var timer = createTimer(),
startButton = createButton("start", start),
stopButton = createButton("stop", stop),
resetButton = createButton("reset", reset),
offset,
clock,
interval;
// default options
options = options || {};
options.delay = options.delay || 1;
// append elements
elem.appendChild(timer);
elem.appendChild(startButton);
elem.appendChild(stopButton);
elem.appendChild(resetButton);
// initialize
reset();
// private functions
function createTimer() {
return document.createElement("span");
}
function createButton(action, handler) {
var a = document.createElement("a");
a.href = "#" + action;
a.innerHTML = action;
a.addEventListener("click", function(event) {
handler();
event.preventDefault();
});
return a;
}
function start() {
if (!interval) {
offset = Date.now();
interval = setInterval(update, options.delay);
}
}
function stop() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
function reset() {
clock = 0;
render(0);
}
function update() {
clock += delta();
render();
}
function render() {
timer.innerHTML = clock / 1000;
}
function delta() {
var now = Date.now(),
d = now - offset;
offset = now;
return d;
}
// public API
this.start = start;
this.stop = stop;
this.reset = reset;
};
// basic examples
var elems = document.getElementsByClassName("basic");
for (var i = 0, len = elems.length; i < len; i++) {
new Stopwatch(elems[i]);
}
// programmatic examples
var a = document.getElementById("a-timer");
aTimer = new Stopwatch(a);
aTimer.start();
var b = document.getElementById("b-timer");
bTimer = new Stopwatch(b, {
delay: 100
});
bTimer.start();
var c = document.getElementById("c-timer");
cTimer = new Stopwatch(c, {
delay: 456
});
cTimer.start();
var d = document.getElementById("d-timer");
dTimer = new Stopwatch(d, {
delay: 1000
});
dTimer.start();
.stopwatch {
display: inline-block;
background-color: white;
border: 1px solid #eee;
padding: 5px;
margin: 5px;
}
.stopwatch span {
font-weight: bold;
display: block;
}
.stopwatch a {
padding-right: 5px;
text-decoration: none;
}
<h2>Basic example; update every 1 ms</h2>
<p>click <code>start</code> to start a stopwatch</p>
<pre>
var elems = document.getElementsByClassName("basic");
for (var i=0, len=elems.length; i<len; i++) {
new Stopwatch(elems[i]);
}
</pre>
<div class="basic stopwatch"></div>
<div class="basic stopwatch"></div>
<hr>
<h2>Programmatic example</h2>
<p><strong>Note:</strong> despite the varying <code>delay</code> settings, each stopwatch displays the correct time (in seconds)</p>
<pre>
var a = document.getElementById("a-timer");
aTimer = new Stopwatch(a);
aTimer.start();
</pre>
<div class="stopwatch" id="a-timer"></div>1 ms<br>
<pre>
var b = document.getElementById("b-timer");
bTimer = new Stopwatch(b, {delay: 100});
bTimer.start();
</pre>
<div class="stopwatch" id="b-timer"></div>100 ms<br>
<pre>
var c = document.getElementById("c-timer");
cTimer = new Stopwatch(c, {delay: 456});
cTimer.start();
</pre>
<div class="stopwatch" id="c-timer"></div>456 ms<br>
<pre>
var d = document.getElementById("d-timer");
dTimer = new Stopwatch(d, {delay: 1000});
dTimer.start();
</pre>
<div class="stopwatch" id="d-timer"></div>1000 ms<br>
Get some basic HTML wrappers for it
<!-- create 3 stopwatches -->
<div class="stopwatch"></div>
<div class="stopwatch"></div>
<div class="stopwatch"></div>
Usage is dead simple from there
var elems = document.getElementsByClassName("stopwatch");
for (var i=0, len=elems.length; i<len; i++) {
new Stopwatch(elems[i]);
}
As a bonus, you get a programmable API for the timers as well. Here's a usage example
var elem = document.getElementById("my-stopwatch");
var timer = new Stopwatch(elem, {delay: 10});
// start the timer
timer.start();
// stop the timer
timer.stop();
// reset the timer
timer.reset();
jQuery plugin
As for the jQuery portion, once you have nice code composition as above, writing a jQuery plugin is easy mode
(function($) {
var Stopwatch = function(elem, options) {
// code from above...
};
$.fn.stopwatch = function(options) {
return this.each(function(idx, elem) {
new Stopwatch(elem, options);
});
};
})(jQuery);
jQuery plugin usage:
// all elements with class .stopwatch; default delay (1 ms)
$(".stopwatch").stopwatch();
// a specific element with id #my-stopwatch; custom delay (10 ms)
$("#my-stopwatch").stopwatch({delay: 10});
jsbin.com demo
Two native solutions
performance.now --> Call to ... took 6.414999981643632 milliseconds.
console.time --> Call to ... took 5.815 milliseconds
The difference between both is precision.
For usage and explanation read on.
Performance.now (For microsecond precision use)
var t0 = performance.now();
doSomething();
var t1 = performance.now();
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");
function doSomething(){
for(i=0;i<1000000;i++){var x = i*i;}
}
performance.now
Unlike other timing data available to JavaScript (for example
Date.now), the timestamps returned by Performance.now() are not
limited to one-millisecond resolution. Instead, they represent times
as floating-point numbers with up to microsecond precision.
Also unlike Date.now(), the values returned by Performance.now()
always increase at a constant rate, independent of the system clock
(which might be adjusted manually or skewed by software like NTP).
Otherwise, performance.timing.navigationStart + performance.now() will
be approximately equal to Date.now().
console.time
Example: (timeEnd wrapped in setTimeout for simulation)
console.time('Search page');
doSomething();
console.timeEnd('Search page');
function doSomething(){
for(i=0;i<1000000;i++){var x = i*i;}
}
You can change the Timer-Name for different operations.
A simple and easy clock for you and don't forget me ;)
var x;
var startstop = 0;
function startStop() { /* Toggle StartStop */
startstop = startstop + 1;
if (startstop === 1) {
start();
document.getElementById("start").innerHTML = "Stop";
} else if (startstop === 2) {
document.getElementById("start").innerHTML = "Start";
startstop = 0;
stop();
}
}
function start() {
x = setInterval(timer, 10);
} /* Start */
function stop() {
clearInterval(x);
} /* Stop */
var milisec = 0;
var sec = 0; /* holds incrementing value */
var min = 0;
var hour = 0;
/* Contains and outputs returned value of function checkTime */
var miliSecOut = 0;
var secOut = 0;
var minOut = 0;
var hourOut = 0;
/* Output variable End */
function timer() {
/* Main Timer */
miliSecOut = checkTime(milisec);
secOut = checkTime(sec);
minOut = checkTime(min);
hourOut = checkTime(hour);
milisec = ++milisec;
if (milisec === 100) {
milisec = 0;
sec = ++sec;
}
if (sec == 60) {
min = ++min;
sec = 0;
}
if (min == 60) {
min = 0;
hour = ++hour;
}
document.getElementById("milisec").innerHTML = miliSecOut;
document.getElementById("sec").innerHTML = secOut;
document.getElementById("min").innerHTML = minOut;
document.getElementById("hour").innerHTML = hourOut;
}
/* Adds 0 when value is <10 */
function checkTime(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
function reset() {
/*Reset*/
milisec = 0;
sec = 0;
min = 0
hour = 0;
document.getElementById("milisec").innerHTML = "00";
document.getElementById("sec").innerHTML = "00";
document.getElementById("min").innerHTML = "00";
document.getElementById("hour").innerHTML = "00";
}
<h1>
<span id="hour">00</span> :
<span id="min">00</span> :
<span id="sec">00</span> :
<span id="milisec">00</span>
</h1>
<button onclick="startStop()" id="start">Start</button>
<button onclick="reset()">Reset</button>
This is my simple take on this question, I hope it helps someone out oneday, somewhere...
let output = document.getElementById('stopwatch');
let ms = 0;
let sec = 0;
let min = 0;
function timer() {
ms++;
if(ms >= 100){
sec++
ms = 0
}
if(sec === 60){
min++
sec = 0
}
if(min === 60){
ms, sec, min = 0;
}
//Doing some string interpolation
let milli = ms < 10 ? `0`+ ms : ms;
let seconds = sec < 10 ? `0`+ sec : sec;
let minute = min < 10 ? `0` + min : min;
let timer= `${minute}:${seconds}:${milli}`;
output.innerHTML =timer;
};
//Start timer
function start(){
time = setInterval(timer,10);
}
//stop timer
function stop(){
clearInterval(time)
}
//reset timer
function reset(){
ms = 0;
sec = 0;
min = 0;
output.innerHTML = `00:00:00`
}
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const resetBtn = document.getElementById('resetBtn');
startBtn.addEventListener('click',start,false);
stopBtn.addEventListener('click',stop,false);
resetBtn.addEventListener('click',reset,false);
<p class="stopwatch" id="stopwatch">
<!-- stopwatch goes here -->
</p>
<button class="btn-start" id="startBtn">Start</button>
<button class="btn-stop" id="stopBtn">Stop</button>
<button class="btn-reset" id="resetBtn">Reset</button>
Solution by Mosh Hamedani
Creating a StopWatch function constructor.
Define 4 local variables
startTime
endTime
isRunning
duration set to 0
Next create 3 methods
start
stop
reset
start method
check if isRunning is true if so throw an error that start cannot be called twice.
set isRunning to true
assign the current Date object to startTime.
stop method
check if isRunning is false if so throw an error that stop cannot be called twice.
set isRunning to false
assign the current Date object to endTime.
calculate the seconds by endTime and startTime Date object
increment duration with seconds
reset method:
reset all the local variables.
Read-only property
if you want to access the duration local variable you need to define a property using Object.defineProperty.
It's useful when you want to create a read-only property.
Object.defineProperty takes 3 parameters
the object which to define a property (in this case the current object (this))
the name of the property
the value of the key property.
We want to create a Read-only property so we pass an object as a value.
The object contain a get method that return the duration local variable.
in this way we cannot change the property only get it.
The trick is to use Date() object to calculate the time.
Reference the code below
function StopWatch() {
let startTime,
endTime,
isRunning,
duration = 0;
this.start = function () {
if (isRunning) throw new Error("StopWatch has already been started.");
isRunning = true;
startTime = new Date();
};
this.stop = function () {
if (!isRunning) throw new Error("StopWatch has already been stop.");
isRunning = false;
endTime = new Date();
const seconds = (endTime.getTime() - startTime.getTime()) / 1000;
duration += seconds;
};
this.reset = function () {
duration = 0;
startTime = null;
endTime = null;
isRunning = false;
};
Object.defineProperty(this, "duration", {
get: function () {
return duration;
},
});
}
const sw = new StopWatch();
function StopWatch() {
let startTime, endTime, running, duration = 0
this.start = () => {
if (running) console.log('its already running')
else {
running = true
startTime = Date.now()
}
}
this.stop = () => {
if (!running) console.log('its not running!')
else {
running = false
endTime = Date.now()
const seconds = (endTime - startTime) / 1000
duration += seconds
}
}
this.restart = () => {
startTime = endTime = null
running = false
duration = 0
}
Object.defineProperty(this, 'duration', {
get: () => duration.toFixed(2)
})
}
const sw = new StopWatch()
sw.start()
sw.stop()
sw.duration
well after a few modification of the code provided by mace,i ended up building a stopwatch.
https://codepen.io/truestbyheart/pen/EGELmv
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Stopwatch</title>
<style>
#center {
margin: 30% 30%;
font-family: tahoma;
}
.stopwatch {
border:1px solid #000;
background-color: #eee;
text-align: center;
width:656px;
height: 230px;
overflow: hidden;
}
.stopwatch span{
display: block;
font-size: 100px;
}
.stopwatch p{
display: inline-block;
font-size: 40px;
}
.stopwatch a{
font-size:45px;
}
a:link,
a:visited{
color :#000;
text-decoration: none;
padding: 12px 14px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div id="center">
<div class="timer stopwatch"></div>
</div>
<script>
const Stopwatch = function(elem, options) {
let timer = createTimer(),
startButton = createButton("start", start),
stopButton = createButton("stop", stop),
resetButton = createButton("reset", reset),
offset,
clock,
interval,
hrs = 0,
min = 0;
// default options
options = options || {};
options.delay = options.delay || 1;
// append elements
elem.appendChild(timer);
elem.appendChild(startButton);
elem.appendChild(stopButton);
elem.appendChild(resetButton);
// initialize
reset();
// private functions
function createTimer() {
return document.createElement("span");
}
function createButton(action, handler) {
if (action !== "reset") {
let a = document.createElement("a");
a.href = "#" + action;
a.innerHTML = action;
a.addEventListener("click", function(event) {
handler();
event.preventDefault();
});
return a;
} else if (action === "reset") {
let a = document.createElement("a");
a.href = "#" + action;
a.innerHTML = action;
a.addEventListener("click", function(event) {
clean();
event.preventDefault();
});
return a;
}
}
function start() {
if (!interval) {
offset = Date.now();
interval = setInterval(update, options.delay);
}
}
function stop() {
if (interval) {
clearInterval(interval);
interval = null;
}
}
function reset() {
clock = 0;
render(0);
}
function clean() {
min = 0;
hrs = 0;
clock = 0;
render(0);
}
function update() {
clock += delta();
render();
}
function render() {
if (Math.floor(clock / 1000) === 60) {
min++;
reset();
if (min === 60) {
min = 0;
hrs++;
}
}
timer.innerHTML =
hrs + "<p>hrs</p>" + min + "<p>min</p>" + Math.floor(clock / 1000)+ "<p>sec</p>";
}
function delta() {
var now = Date.now(),
d = now - offset;
offset = now;
return d;
}
};
// Initiating the Stopwatch
var elems = document.getElementsByClassName("timer");
for (var i = 0, len = elems.length; i < len; i++) {
new Stopwatch(elems[i]);
}
</script>
</body>
</html>

Categories