Is SetInterval() to milliseconds too much for browser? - javascript

I have made a JavaScript Stopwatch that works by incementing the value of a variable every second (using setInterval(stopwatchValue, 1000)). I have some issues though: if the user hits pause at, say, 3.8 seconds, the actual pause value will be 3 seconds (when they click resume, the stop watch will continue from 3.0 seconds instead of 3.8).
I DO have a solution to fix this. My idea is too update the stopwatch every split second by doing something like setInterval(stopwatchValue, 1).
My question is: Is it too much for the browser to handle this code every split second:
function stopwatchValue("result") {
document.getElementById("result").innerHTML = timer;
timer++;
}
ALSO: Some people have said you should avoid accessing the DOM more than once if you can. In my case, would it work if I replaced my above code with:
var displayResultHere = document.getElementById("result");
function stopwatchValue("result") {
displayResultHere = timer;
timer++;
}
My Stopwatch: hntr.atwebpages.com/stopwatch/stopwatch.html

What you need is a variable delay.
SetInterval's lack of precision is still fine to use to update what the user sees.
What's not cool is that the rounding effect means I can sit down and rapidly click start and stop over and over for hours and never see this thing tick off a second.
What you need to do is timestamp the clicks, do the math to find the real duration (of time spent while started), calculate what should be display'd, and set a one time (per start click) variable delay before SetInterval's one second ticking starts again.
Do that and no amount of clicking will slow your clock down.

Here is an example of a StopWatch. I just made it. If you know how to read code, which you sort of have to to be on this site, it should be easy to understand.
//<![CDATA[
/* external.js */
var doc, bod, htm, M, I, S, Q, StopWatch, old = onload; // for use on other loads
onload = function(){ // load wrapper - not indented to save space
if(old)old(); // change old var name above and here if using technique on other pages
doc = document; bod = doc.body; htm = doc.documentElement;
M = function(tag){
return doc.createElement(tag);
}
I = function(id){
return doc.getElementById(id);
}
S = function(selector, within){
var w = within || doc;
return w.querySelector(selector);
}
Q = function(selector, within){
var w = within || doc;
return w.querySelectorAll(selector);
}
StopWatch = function(outputDiv, decimals, interval){
var dm = typeof decimals === 'number' ? decimals : 3;
var iv = typeof interval === 'number' ? interval : 10;
this.zero = '00:00:0'+(0).toFixed(dm); outputDiv.innerHTML = this.zero;
var st, si, et;
this.start = function(){
if(st === undefined){
st = Date.now();
}
else if(et !== undefined){
st = Date.now()-et+st; et = undefined;
}
si = setInterval(function(){
var t = Date.now()-st, h = Math.floor(t/3600000), hf = h*3600000, m = Math.floor((t-hf)/60000), mf = m*60000, s = Math.floor((t-hf-mf)/1000), sf = s*1000;
s = (s+(t-hf-mf-sf)/1000).toFixed(dm);
if(h.toString().match(/^\d$/))h = '0'+h;
if(m.toString().match(/^\d$/))m = '0'+m;
if(s.toString().match(/^\d(\.?\d*)?$/))s = '0'+s;
var out = h+':'+m+':'+s;
outputDiv.innerHTML = out;
}, iv);
return this;
}
this.stop = function(){
if(si){
if(et === undefined)et = Date.now();
clearInterval(si); si = undefined;
}
return this;
}
this.clear = function(){
this.stop(); st = et = undefined; outputDiv.innerHTML = this.zero;
return this;
}
}
I('f').onsubmit = function(){
return false;
}
var act = I('act'), clear = I('clear'), out = I('out');
var stopWatch = new StopWatch(out);
act.onclick = function(){
if(this.value === 'start'){
stopWatch.start(); this.value = 'stop';
}
else{
stopWatch.stop(); this.value = 'start';
}
}
clear.onclick = function(){
stopWatch.clear(); out.innerHTML = stopWatch.zero; act.value = 'start';
}
} // end load
//]]>
/* external.css */
html,body{
padding:0; margin:0;
}
body{
background:#000; overflow-y:scroll;
}
.main{
width:940px; background:#ccc; padding:20px; margin:0 auto;
}
#out{
display:inline-block; width:85px; background:#fff; padding:5px 10px; cursor:default;
}
#act,#clear{
width:55px; cursor:pointer;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
<meta name='viewport' content='width=device-width' />
<title>StopWatch</title>
<link type='text/css' rel='stylesheet' href='external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div class='main'>
<form id='f' name='f'>
<div id='out'></div>
<input id='act' type='button' value='start' />
<input id='clear' type='button' value='reset' />
</form>
</div>
</body>
</html>

Instead of using setTimeout, use requestAnimationFrame().
When the timer starts, create a new Date, and create a new date every animation frame: subtract the two to get the current value of the timer.
Cache your DOM element into a variable, as you have already done. Something like this:
var result_display = document.getElementById("result");
var timer_is_running = false;
var remembered_time = null;
var timer_start_time;
function start_timer() {
timer_is_running = true;
timer_start_time = new Date();
update_timer_value(accumulated_time);
}
function pause_timer() {
timer_is_running = false;
}
function update_timer_value(acc_time) {
var elapsed_time = new Date() - timer_start_time;
if (remembered_time) {
elapsed_time += remembered_time;
remembered_time = null;
}
var second_value = Math.floor(elapsed_time);
result_display.innerHTML = second_value;
if (timer_is_running) {
requestAnimationFrame(update_timer_value);
}
else {
remembered_time = elapsed_time;
}
}

Related

I have trouble hiding elements in my game if they don't match

I am working on a memory game and I asked a previous question earlier which was answered. I've had this problem and I haven't been able to find a solution with effort. So it's a memory game and when the cards are clicked, they are pushed into an array which can hold two elements at max (you can only click two cards) and then the two elements' frontSrc is checked if they are the same. I set that using an expando property. If so, have them visible and then clear the array so I can do more comparisons. However, it doesn't seem to be working as intended. I'll attach a video below. I've tried using timeout to set the length again, but that didn't work. It works for the first cards, but the other ones after that, it doesn't work.
Here is my code.
<!DOCTYPE html>
<!--
Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
Click nbfs://nbhost/SystemFileSystem/Templates/Other/html.html to edit this template
-->
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.card{
width: 35%;
}
#cards{
display: grid;
grid-template-columns:25% 25% 25% 25%;
row-gap: 25px;
}
</style>
</head>
<body onload="init()">
<section id="cards">
</section>
<script>
var arrCards = [];
var arrShowedCards = [];
//appendElements();
function init(){
createCards();
shuffleCards();
appendElements();
}
function createCards(){
var arrCardNames = ["Mouse","Penguin","Pop","Mouse","Penguin","Pop"];
for(var i = 0; i<6; i++){
var card = document.createElement("IMG");
card.src = "Images/red_back.png";
card.className = "card";
card.frontSrc = "Images/" + arrCardNames[i] + ".png";
card.id = "card" + i;
card.addEventListener("click", showCard);
document.getElementById("cards").appendChild(card);
arrCards.push(card);
}
}
function shuffleCards(){
for (let i = arrCards.length-1; i > 0; i--)
{
const j = Math.floor(Math.random() * (i + 1));
const temp = arrCards[i];
arrCards[i] = arrCards[j];
arrCards[j] = temp;
}
return arrCards;
}
function appendElements(){
for(var i = 0; i<arrCards.length; i++){
document.getElementById("cards").appendChild(arrCards[i]);
}
}
function showCard(){
var sourceString = "Images/red_back.png";
this.src = this.frontSrc;
arrShowedCards.push(this);
if(arrShowedCards.length === 2){
if(arrShowedCards[0].frontSrc === arrShowedCards[1].frontSrc){
console.log("Match!");
arrShowedCards = [];
}
else{
console.log("No match!");
setTimeout(function(){
arrShowedCards[0].src = sourceString;
arrShowedCards[1].src = sourceString;
}, 1000);
}
}
}
</script>
</body>
</html>
I am not sure how come it doesn't work for it for the other cards.
Here is the video.
https://drive.google.com/file/d/1aRPfLALHvTKjawGaiRgD1d0hWQT3BPDQ/view
If anyone finds a better way to approach this, let me know.
Thanks!
I think when not match, you need to reset arrShowedCards otherwise its length will be greater than 2 forever.
function showCard() {
var sourceString = "Images/red_back.png";
this.src = this.frontSrc;
arrShowedCards.push(this);
if (arrShowedCards.length === 2) {
var a = arrShowedCards[0], b = arrShowedCards[1];
arrShowedCards.length = 0;
if (a.frontSrc === b.frontSrc) {
console.log("Match!");
} else {
console.log("No match!");
setTimeout(function () {
a.src = sourceString;
b.src = sourceString;
}, 1000);
}
}
}

In a jsf web application, a Javascript based session timer works on Chrome but not on IE

In a jsf web application based on Seam and Richfaces, I ran into a problem concerning different browsers. The code (and every variation I tried) works flawless in Chrome, but not in Internet Explorer (I am testing version 11).
The code is supposed to start and display a session-timeout countdown in the header. In the beginning of the template file, the timeout is retrieved from the application preferences and stored in a hidden field. The countdown timer is reset whenever a new page is loaded, or when an AJAX request is triggered (resetInactivityTimer()).
I am having 2 problems in IE:
It seems that the window.onloadfunction is not triggered on IE. The counter starts working fine when triggered manually in the console.
When the counter is started manually, an error occurs when an AJAX request is triggered.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Title</title>
<link rel="shortcut icon" type="image/x-icon" href="#{facesContext.externalContext.requestContextPath}/img/favicon.ico" />
<a:loadStyle src="/stylesheet/theme.css" />
<ui:insert name="head" />
</head>
<body>
<h:inputHidden id="originalTimeoutId" value="#{preferencesManager.getPreferenceValue(Preference.HTTP_SESSION_TIMEOUT)}"/>
<a:loadScript src="/scripts/script.js"/>
<a:region id="status_zone">
<a:status for="status_zone" forceId="false" id="ajaxStatus" onstart="resetInactivityTimer()">
<f:facet name="start">
<h:panelGroup>
<div style="position: absolute; left: 50%; top: 50%; text-align:center; width: 100%; margin-left: -50%;z-index: 10001;" >
<h:graphicImage value="/img/wait.gif"/>
</div>
<rich:spacer width="95%" height="95%" style="position: absolute; z-index: 10000; cusor: wait;" />
</h:panelGroup>
</f:facet>
</a:status>
<div class="main">
<ui:include src="/layout/header.xhtml" />
<ui:include src="/layout/menu.xhtml" />
<div style="margin-top: 10px;">
<ui:insert name="body" />
</div>
<ui:include src="/layout/footer.xhtml" />
</div>
</a:region>
<script type="text/javascript">
window.onload = initCountdown();
</script>
</body>
</html>
The countdown timer is displayed in the top right corner in the Header file "header.xhtml", which is loaded in the template, and therefore contained on every page:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<div class="header">
<s:graphicImage value="#{preferencesManager.getPreferenceByteContent(Preference.LOGO)}" styleClass="logo"/>
<h:panelGrid width="92%" columns="3" columnClasses="headerCol2,headerCol3,headerCol4">
<h:outputText styleClass="titel"
value="#{cM.getStringProp('de.gai_netconsult.kodaba.text.title')}"/>
<span class="timer">Automatischer Logout in: </span>
<h:outputText id="counter" styleClass="timer"></h:outputText>
</h:panelGrid>
</div>
The time is placed at the id="counter" position.
This is the Javascript code: "script.js"
var hiddenField;
var timeoutInSeconds;
var originalTimeout;
var originalCounter;
var initialized = false;
function initCountdown(){
// quit if this function has already been called
if (arguments.callee.done) return;
// flag this function so we don't do the same thing twice
arguments.callee.done = true;
// do stuff
startCountdown();
}
function getHiddenField() {
if (hiddenField != null) {
timeoutInSeconds = parseInt(hiddenField.value) * 60;
return timeoutInSeconds;
}
try {
hiddenField = document.getElementById('originalTimeoutId');
} catch (e) {
timeoutInSeconds = 0;
}
return timeoutInSeconds;
}
function getOriginalCounter(){
return document.getElementById('counter');
}
function resetInactivityTimer() {
if (initialized) {
console.log("resetInactivityTimer - initialized: " + initialized);
stopCountdown();
countdown(timeoutInSeconds, 'counter');
}
}
function startCountdown () {
timeoutInSeconds = getHiddenField();
if(timeoutInSeconds == 0) return;
originalCounter = getOriginalCounter();
if(timeoutInSeconds == null || originalCounter == null) {
setTimeout(function(){
startCountdown()}, 1000);
}
if(timeoutInSeconds != null && originalCounter != null){
initialized = true;
originalTimeout = timeoutInSeconds;
countdown(originalTimeout, 'counter');
}
}
function stopCountdown() {
var element = document.getElementById('counter');
clearTimeout(element.timerId);
}
function leadingzero(number) {
return (number < 10) ? '0' + number : number;
}
function countdown(seconds, target) {
var element = document.getElementById(target);
element.seconds = seconds;
calculateAndShow('counter');
}
function calculateAndShow(target) {
var element = document.getElementById('counter');
if (element.seconds >= 0) {
element.timerId = window.setTimeout(calculateAndShow,1000,target);
var h = Math.floor(element.seconds / 3600);
var m = Math.floor((element.seconds % 3600) / 60);
var s = element.seconds % 60;
element.innerHTML=
leadingzero(h) + ':' +
leadingzero(m) + ':' +
leadingzero(s);
element.seconds--;
} else {
completed(target);
return false;
}
}
function completed(target) {
var element = document.getElementById(target);
element.innerHTML = "<strong>Finished!<\/strong>";
}
Some things I tried is replacing
<script type="text/javascript">
window.onload = initCountdown();
</script>
with
<script type="text/javascript">
if (window.attachEvent) {window.attachEvent('onload', initCountdown());}
else if (window.addEventListener) {window.addEventListener('load', initCountdown(), false);}
else {document.addEventListener('load', initCountdown(), false);}
</script>
This leads to a "Typeconflict".
or with:
<rich:jQuery name="jcountdown" query="initCountdown()" timing="onload"/>
None of this helps.
I was able to get my timer to work in the end and I will post my solution here:
Problem 1:
<script type="text/javascript">
jQuery(document).ready(function(){
startCountdown();
});
</script>
instead of window.onload = startCountdown(); solves the problem.
Important: when using any console.log() statements, the function will only be executed when the developer console is opened! (F12).
Problem 2: (AJAX)
Richfaces version 3.3 is simply not compatible with any Internet Explorer Version above IE8.
It is important to apply a patch. This site describes the process in detail.
I also had to make many changes to the Javascript code. I am sure this could be much more elegantly written, but I confess I don't really know much Javascript at all... I'm posting my code anyway, in case somebody may find it useful:
var hiddenField;
var timeoutInSeconds;
var originalTimeout;
var originalCounter;
function getHiddenField() {
if (hiddenField != null) {
timeoutInSeconds = parseInt(hiddenField.value) * 60 -1;
timeoutInSeconds;
return timeoutInSeconds;
}
try {
hiddenField = document.getElementById('originalTimeoutId');
} catch (e) {
timeoutInSeconds = 0;
}
return timeoutInSeconds;
}
function getOriginalCounter(){
return document.getElementById('counter');
}
function startCountdown () {
timeoutInSeconds = getHiddenField();
if(timeoutInSeconds == 0) return;
originalCounter = getOriginalCounter();
if(timeoutInSeconds == null || originalCounter == null) {
setTimeout(function(){
startCountdown()}, 1000);
}
if(timeoutInSeconds != null && originalCounter != null){
originalTimeout = timeoutInSeconds;
countdown(originalTimeout, 'counter');
}
}
function countdown(seconds, target) {
var element = document.getElementById(target);
element.seconds = seconds;
calculateAndShow('counter');
}
function resetCountdown(){
var element = document.getElementById('counter');
element.seconds = timeoutInSeconds;
updateDisplay(element);
}
function calculateAndShow() {
var element = document.getElementById('counter');
if (element.seconds > 0) {
element.timerId = window.setTimeout(calculateAndShow,1000,'counter');
updateDisplay(element);
element.seconds--;
} else {
completed();
return false;
}
}
function updateDisplay(element){
var h = Math.floor(element.seconds / 3600);
var m = Math.floor((element.seconds % 3600) / 60);
var s = element.seconds % 60;
element.innerHTML =
leadingzero(h) + ':' +
leadingzero(m) + ':' +
leadingzero(s);
}
function leadingzero(number) {
return (number < 10) ? '0' + number : number;
}
function completed() {
var element = document.getElementById('counter');
element.innerHTML = "<strong>Beendet!<\/strong>";
logoutCallBackToServer();
}
Somewhere in one of your xhtml files (template, Header, menu, whatever) you also need to add this line:
<a4j:jsFunction name="logoutCallBackToServer" immediate="true" action="#{identity.logout}" />
This will ensure that the user is actually logged out precisely when the countdown reaches zero, just in case this does not 100% match the actual session time out.

How to Redirect if no mouse movement and show current timer countown

I have this javascript to act as a countdown timer in a div, and simultaneously detecting mouse idleness.
var timer = null;
setInterval(function() {
var div = document.querySelector("#counter");
var count = div.textContent * 1 - 1;
div.textContent = count;
if (count == 0) {
window.location.href="https://example.com";
}
}, 1000);
function goAway() {
clearTimeout(timer);
timer = setTimeout(function() {
window.location.href="https://example.com";
}, 5000);
}
window.addEventListener('mousemove', goAway, true);
goAway();
If the user makes no mouse movement for more than 5 seconds, I want him to be redirected to another page. The example.com in this case. This part is working. However, I also intent for a right placed div to show the countdown to be redirected, and to disappear in case of .mousemove event. I cannot seem to get both of them working.
is it possible?
http://jsfiddle.net/9sAce/
Hope this helps. A few modifications done to goAway function.
var timer = null;
setInterval(function() {
var div = document.querySelector("#counter");
var count = div.textContent * 1 - 1;
div.textContent = count;
if (count == 0) {
window.location.href="https://example.com";
}
}, 1000);
function goAway() {
var div = document.querySelector("#counter");
div.innerText = "10";
clearTimeout(timer);
timer = setTimeout(function() {
if (div.innerText === "0")
window.location.href="https://example.com";
}, 5000);
}
window.addEventListener('mousemove', goAway, true);
goAway();
<div id="counter" style="border:1px solid black;width:100px">10</div>
Configurable:
Swap out .timer for #counter if that's your preference.
t tracks time
l is total time
h is url
Inside the interval n is the total time minus the current time t
v compares the calculation greater than 0 if false set n to 0 ( needed to prevent negative integers )
Update the DOM with the visual count
if n is equal to 0 redirect to the set URL.
(()=>{
t = 0;
l = 5;
h = 'https://example.com';
document.write('<h1 class="timer">'+l+'</h1>');
timer = document.querySelector('.timer');
setInterval(()=>{
t += 1;
n = (l - t);
v = n > 0 ? n : 0;
timer.innerText = v;
if(n === 0) {
window.location.href = h;
}
}, 1000);
document.addEventListener('mousemove', (e) => {
t = 0;
});
})();
Click here to see an example: https://codepen.io/DanielTate/pen/VMVMLa
You might try something like:
//<![CDATA[
// external.js
function countdown(outputElement, seconds){
var s = 5, bs = 5;
if(seconds){
s = bs = seconds;
}
function tF(){
outputElement.innerHTML = s = bs;
return setInterval(function(){
s--;
if(!s){
clearInterval(timer); location = 'https://example.com';
}
outputElement.innerHTML = s;
}, 1000);
}
var timer = tF();
onmousemove = function(){
clearInterval(timer); timer = tF();
}
}
var old = onload;
onload = function(){
if(old)old();
countdown(document.getElementById('counter'));
}
//]]>
/* external.css */
html,body{
padding:0; margin:0;
}
.main{
width:940px; padding:20px; margin:0 auto;
}
#counter{
font-size:80px;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
<meta name='viewport' content='width=device-width' />
<title>simple countdown</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div class='main'>
<div id='counter'></div>
</div>
</body>
</html>

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>

JavaScript Timer Pause

The code is below. How would you set a button to pause the timer and resume when pressing resume? The // marks below are where I'm placing my pause and resume tags. Thank you for all of your help!!
<head>
<script type="text/javascript">
var d1 = new Date();
d1.setHours(1,0,0);
function f(){
var h= d1.getHours();
var m= d1.getMinutes();
var s=d1.getSeconds();
m= (m<10)?"0"+m: m;
s= (s<10)? "0"+s : s;
var el= document.getElementById("inputid");
el.value= h+":"+m+":"+s;
d1.setSeconds(d1.getSeconds()-1);
if( h==0 && m==0 && s==0 ) clearTimeout(t)
var t= setTimeout("f()",1000);
}
</script>
</head>
<body>
<form><input type="text" id="inputid"></form>
<script type="text/javascript">f()</script>
//pause and resume buttons would go here.
</body>
Another approach is: when the buttons is pressed, set a variable like paused. In your f function, if paused is true, simply return immediately.
setInterval(function(){
if (paused) return;
// update the dom
}, 1000);
input
<input type="button" value="Pause" onClick="window.paused=true" />
Here is a basic fiddle
You can stop a timeout with clearTimeout() passing it the return from setTimeout or, in your case t.
Live example: http://jsfiddle.net/tJWmH/
Another pointer: dont pass a string to setTimeout, instead pass a function reference, so:
var t = setTimeout(f,1000)
in place of
var t = setTimeout("f()",1000);
if you're wondering why, search for "eval is evil".
Try this:
var d1 = new Date();
d1.setHours(1,0,0);
var t;
function f() {
var h= d1.getHours();
var m= d1.getMinutes();
var s=d1.getSeconds();
m= (m < 10) ? ('0'+m) : m;
s= (s < 10) ? ('0'+s) : s;
var el= document.getElementById("inputid");
el.value = h+":"+m+":"+s;
if( h==0 && m==0 && s==0 ) {
clearTimeout(t)
return;
}
d1.setSeconds(d1.getSeconds()-1);
t= setTimeout(f,1000);
}
function pause() {
clearTimeout(t);
}
function resume() {
t= setTimeout(f,1000);
}
resume();
You just call pause() to pause it, and resume() to resume it. That's it!
Notice that I am calling resume() once, just to start the counter.
EDIT: You must check if it reached zero before decrementing, and return so that pressing resume won't continue to work.
This is not my code. I found it and use in my projects. Here's the original post: https://stackoverflow.com/a/3969760/1250044
Timer = function(callback, delay) {
var timerId, start, remaining = delay;
this.pause = function() {
window.clearTimeout(timerId);
remaining -= new Date() - start;
};
this.resume = function() {
start = new Date();
timerId = window.setTimeout(callback, remaining);
};
this.resume();
};
use:
var t = new Timer(function(){
/* ... */
}, 500);
t.pause();
t.resume();

Categories