Why doesn't my timer function count down? - javascript

I am working on a personal project to create a pomodoro clock. I am starting off by trying to create a 25 minute countdown timer with a start and stop button. I have included a timer function that should count down my variable every 1000 milliseconds but it does not function. Here is my HTML:
Pomodoro Clock
<div id="timer" class="circle">Timer</div>
<button onclick="setTimeout(timer, 1000);">Start</button>
<button>Stop</button>
Javascript:
var i = 25;
document.getElementById("timer").innerHTML = i;
function timer(){
setInterval(function(){i--}, 1000);
}
I am guessing it may have something to do with my timer function?

document.getElementById("timer").innerHTML = i;
Here the current value of i is being assigned to innerHTML. It's not being passed by reference.
The only data types in JavaScript that are passed by reference are Objects (Plain objects, functions, arrays, etc).
You need to assign the new value of i to innerHTML on every iteration of your interval:
setInterval(function(){
i--;
document.getElementById("timer").innerHTML = i;
}, 1000);

You need to update the innerHTML inside the setInterval() callback. Also you can clear the interval using clearInterval(). I just removed the setTimeout() , since setInterval() starts after the delay.
var i = 25,
ele = document.getElementById("timer");
ele.innerHTML = i, inter;
function timer() {
inter = setInterval(function() {
ele.innerHTML = --i;
if (i == 0) clearInterval(inter);
}, 1000);
}
function stop() {
clearInterval(inter);
}
<div id="timer" class="circle">Timer</div>
<button onclick="timer()">Start</button>
<button onclick="stop()">Stop</button>

Try this
var i = 25;
function timer(){
setInterval(function(){
i--;
document.getElementById("timer").innerHTML = i;
}, 1000);
}

You only set the innerHTML one time, at the start of the script. You need to do it each time your function runs.
document.getElementById("timer").innerHTML = i;
function timer(){
setInterval(function(){
i--;
document.getElementById("timer").innerHTML = i;
}, 1000);
}

You need to update the div inside the interval function.
var i = 25;
var timerDiv = document.getElementById("timer");
function timer() {
setInterval(function() {
timerDiv.innerHTML = i--;
}, 1000);
}
.circle {
background-color: red;
color: white;
border-radius: 20px;
display: inline-block;
width: 40px;
height: 40px;
text-align: center;
font-weight: bold;
font-family: sans-serif;
line-height: 40px;
}
<div id="timer" class="circle">Timer</div>
<button onclick="setTimeout(timer, 1000);">Start</button>
<button>Stop</button>

Related

Countdown timer counting too far

I am trying to make a game that tests how many times you can click on a div in a minute. I can not get my countdown to function properly. My countdown should stop at 0, but instead it keeps going after reaching 0. My code is below. Any ideas?
let div = document.getElementById("div");
let score = document.querySelector("h3");
let time = document.querySelector("span");
var jsTime = 60;
var jsScore = 0;
alert("Click inside the box as many times as you can in one minute. \n Ready?");
var clicker = div.addEventListener("click", function() {
jsScore++;
score.innerHTML = jsScore;
}, 1);
var i = 60;
setInterval(function() {
if (i > 1) {
jsTime--;
time.innerHTML = jsTime;
} else {
removeEventListener(clicker);
}
}, 1000);
#div {
text-align: center;
width: 100px;
height: 100px;
background: black;
color: white;
}
#container {
display: inline-block;
}
<h1>Click inside the box as many times as you can in one minute.</h1>
<br>
<div id="container">
<h2>Time:</h2><span>60</span>
<h2>Score:</h2>
<h3>0</h3>
</div>
<div id="div"></div>
You're decreasing the jsTime as long as i is > 1, which it will always be, since you never decrease the value of i. I think you meant to use jsTime in the conditional instead. Renaming it to something that gives it some context may make it easier to follow. Ex remainingTimeSeconds. See the revised code below.
let div = document.getElementById("div");
let score = document.querySelector("h3");
let time = document.querySelector("span");
var jsTime = 60;
var jsScore = 0;
alert("Click inside the box as many times as you can in one minute. \n Ready?");
var clicker = div.addEventListener("click", function() {
jsScore++;
score.innerHTML = jsScore;
},1);
setInterval(function() {
// As long as there is time left, keep decreasing it, and update the time element's innerHTML with the new current time.
if (jsTime > 1){
jsTime--;
time.innerHTML = jsTime;
} else {
removeEventListener(clicker);
}
},1000);

How to run pause and stop setInterval

My task:
Run setInterval loop when I hover the current block, for example #main
When I hover on some children element of #main, setInterval has to be paused
After when I leave children element of #main, and return my mouse focus back to #main, setTimeOut should run again. here is screen http://joxi.ru/L215V3qh65weW2
My code:
let num = 0;
var timer = function() { // auto click
{ num >= $(`.the_wrap_graf`).children().length-1 ? num = 0 : num++ }
$(`.year-wrap:eq(${num}) .q`).click()
}
var timerID = null // name of interval
$('.the_wrap_feed').hover(function (ev) { // hover run loop #main
timerID = setInterval(timer, 3000);
}, function (ev) { // mouseleave kill loop
clearInterval(timerID)
})
$(`.q`).mouseenter(function(e) { // kill loop when hover square
clearInterval(timerID)
})
If I add callback to $(.q), it breaks down. How can I do it?
You can't pause an interval timer. You can only cancel it and start a new one.
Re the requirement, I think I'd probably use mouseenter and mouseleave (which you're already doing, using hover) and track whether the cursor is in #main or a child:
var timer = 0;
var timerValue = 0;
var inMain = 0;
var inChild = 0;
function showTimer() {
$("#timer").text(
timer ? "Running: " + timerValue : "Not running"
);
}
function updateTimer() {
if (inMain && !inChild) {
if (!timer) {
timer = setInterval(tick, 100);
}
} else {
if (timer) {
clearInterval(timer);
timer = 0;
}
}
}
function tick() {
++timerValue;
showTimer();
}
showTimer();
$("#main")
.hover(
function() {
++inMain;
updateTimer();
},
function() {
--inMain;
updateTimer();
}
);
$("#main .child")
.hover(
function() {
++inChild;
updateTimer();
},
function() {
--inChild;
updateTimer();
}
);
#main {
border: 1px solid #aaa;
padding: 8px;
}
.child {
border: 1px solid #ddd;
margin: 8px;
}
<div id="timer"></div>
<div>
Not in main
<div id="main">
In main, not in any children
<div class="child">one child</div>
<div class="child">another child</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Repeating a setInterval

I'm currently using a setInterval function in JavaScript for an HTML page. It has a button that allows me to start a countdown from 10 to 0 with a 1 second interval, and every time I press the button the countdown is supposed to reset. However, after the first button press the next countdown messes up the interval badly.
var count;
function countdown() {
count = 10;
var repeat = setInterval(reduce, 1000);
}
function reduce() {
if (count > 0) {
document.getElementById('number').innerHTML = count;
count--;
} else {
clearInterval(repeat);
}
}
<html>
<head>
<title>Page 4</title>
</head>
<body>
<button style=" display: inline-block; " onclick="countdown()">Start Count Down</button>
<div style=" display: inline-block;" id="number"></div>
</body>
</html>
You could use a global variable repeat and reset if set when countdown is invoked.
var count,
repeat;
function countdown(){
count = 10;
if (repeat) {
clearInterval(repeat);
}
repeat = setInterval(reduce, 1000);
}
function reduce() {
if (count > 0) {
document.getElementById('number').innerHTML = count;
count--;
} else {
clearInterval(repeat);
}
}
<button style=" display: inline-block; " onclick ="countdown()" >Start Count Down</button>
<div style="display: inline-block;" id="number"></div>
You problem is the repeat variable, it's defined and accessible only inside countdown.
You could just make it global as well, and then you'd have to clear previous intervals when clicking the button again
var count, repeat;
function countdown() {
clearInterval(repeat);
count = 10;
repeat = setInterval(reduce, 1000);
}
function reduce() {
if (count > 0) {
document.getElementById('number').innerHTML = count;
count--;
} else {
clearInterval(repeat);
}
}
FIDDLE
put your repeat outside so the function reduce can access it
var count;
var repeat = null;
function countdown(){
count = 10;
repeat = setInterval(reduce, 1000);
}
function reduce() {
if(count > 0)
{document.getElementById('number').innerHTML = count;
count--;}
else
{clearInterval(repeat);}
}
You can also use this to stop interval;
clearInterval(this)
var count;
function countdown() {
count = 10;
var repeat = setInterval(reduce, 1000);
}
function reduce() {
if (count > 0) {
document.getElementById('number').innerHTML = count--;
} else {
clearInterval(this);
}
}
<button style=" display: inline-block; " onclick="countdown()">Start Count Down</button>
<div style=" display: inline-block;" id="number"></div>
This will solve your issue but will raise another issue. If you click multiple times, multiple events will be registered. For this, you should either disable button or define repeat in parent scope.
var count, repeat;
function countdown() {
count = 10;
repeat = repeat || setInterval(reduce, 1000);
}
function reduce() {
if (count > 0) {
document.getElementById('number').innerHTML = count--;
} else {
clearInterval(this);
}
}
<button style=" display: inline-block; " onclick="countdown()">Start Count Down</button>
<div style=" display: inline-block;" id="number"></div>
You will notice a simple trick:
repeat = repeat || setInterval(reduce, 1000);
This will ensure multiple intervals are not registered.

fadeIn and fadeOut in javascript

I'm trying to write my own animations using JavaScript.
I wrote a function for fadeIn() as below, it changes the display property followed by a change in value of opacity. But it doesn't seem to be working.
What am I doing wrong?
function fadeIn(obj, defDisp) {
obj.style.opacity = 0;
obj.style.display = defDisp;
var opVal = 0;
while (opVal < 1) {
obj.style.opacity = opVal;
opVal += 0.1;
}
}
defDisp = Default value for display property
Without a timing interval, this will likely execute too fast for you to see it. The while loop, without a timeout feature, will execute in far less than a second, and you won't see it happen. It's like asking a computer to count to 10, it will do it in less than a millisecond.
Try using a setTimeout
http://www.w3schools.com/jsref/met_win_settimeout.asp
while(opVal < 1) {
setTimeout(function(){
obj.style.opacity = opVal;
opVal += 0.1;
}, 3000);
}
Alter the timer (3000 in this case) to something that makes your fade work for you. Every 1000 is a one second and your loop runs 10 times, so in this case it would be 30 seconds, likely too slow.
I would probably stick with a CSS transition however, as they tend to render better on all browsers.
var el = document.getElementById('fadein');
fadeIn(el);
function fadeIn(ele, defDisp) {
ele.style.opacity = 0;
ele.style.display = defDisp;
var opVal = 0;
var t = setInterval(function(){
if(opVal >= 1){
clearInterval(t);
}
ele.style.opacity = opVal;
opVal += 0.1;
}, 100);
}
#fadein{ background: #ccc; border:1px solid #ddd; padding: 10px }
<div id="fadein">Hello</div>
Use a function that calls itself after a delay.
function fadeIn(obj, defDisp) {
obj.style.opacity = 0;
obj.style.display = defDisp;
var last = +new Date(); // Keep track of the time to calculate the opacity
var fadeStep = function () {
obj.style.opacity = +obj.style.opacity + (new Date() - last) / 800;
last = +new Date();
if (+obj.style.opacity < 1) {
setTimeout(fadeStep, 16);
}
};
fadeStep();
}
var el = document.getElementById('box');
fadeIn(el, 'block');
#box{ padding: 1em; background: #009afd; color: #ffffff; display: none; }
<div id="box">Hello</div>
If you want the fade to be faster, replace 800 by anything lower and vice-versa.
Because html render and for loop use the same thread, so when you doing the for-loop,you can't see any changes until the function complete. You have to use a setTimeout or setInterval (or requestAnimationFrame which is introduced from html5) so you browser can have the control to change the properties on the page:
You can see a example from the snippet, although the second that use a setTimeout is faster than the first one, which use for loop, the first one will not change its color as browser not able to change color during for-loop.
And if you choose to use requestAnimationFrame like I do in the snippets, you can have a smooth animation while the time can also be controlled precisely.
function fadeIn() {
this.style.opacity = 0;
this.style.display = 'block';
var opVal = 0;
console.time("count");
while(opVal < 1) {
this.style.opacity = opVal;
opVal += 0.000001;
}
console.timeEnd("count");
}
// Accept target as the target to apply anim, time is total anim time in ms.
function fadeInAlt(target, time) {
var opacity = 0;
var last = window.performance.now();
console.time("count2");
target.style.opacity = opacity;
target.style.display = 'block';
var fadeInFunc = function(timeStamp) {
if (opacity < 1) {
// Define the change by passed time.
var timePassed = timeStamp - last;
opacity += timePassed / time;
target.style.opacity = opacity;
last = timeStamp;
requestAnimationFrame(fadeInFunc);
} else {
console.timeEnd("count2");
return;
}
};
requestAnimationFrame(fadeInFunc);
}
var div = document.getElementById('test');
div.onclick = fadeIn;
var div2 = document.getElementById('test2');
div2.onclick = function() {
fadeInAlt(this, 3000);
};
#test {
background-color: red;
width: 30px;
height:30px;
}
#test2 {
background-color: blue;
width: 30px;
height:30px;
}
<div id="test"></div>
<div id="test2"></div>

Stop a javascript timer and animation and resume them

I have to stop a timer and an animation of a bar which width is decreasing dependently on a time variable and then resume both if pressed a button, so that the bar animation will continue from where it stopped and the same for the timer. How can I do that?
I can execute a function on button press, it's just the stopping and resuming functions that I don't know.
$("#timebar") is the animated bar.
function startTimer() {
timer = setTimeout(function(){
barAnimation();
}, time);
}
function stopTimer() {
$('#timebar').stop();
$('#timebar').css("width","100%");
clearTimeout(timer);
}
function barAnimation() {
$("#timebar").animate({ width: "0%" }, time, "linear");
}
This doesn't have the animation, but it does offer a start/stop and progress indicator.
var time = 0;
var timer = 0;
var running = false;
function startTimer() {
running = true;
timer = setInterval(function() {
barAnimation();
}, 1000);
}
function stopTimer() {
running = false;
clearInterval(timer);
}
function barAnimation() {
time++;
$("#count").text(time);
$("#timebar").prop("value", time);
}
$("#go").on("click", function(evt) {
if (running) {
stopTimer();
} else {
startTimer();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button id="go">Go</button>
<progress id="timebar" value="0" max="100"></progress>
<div id="count"></div>
I think something like this is what you're looking for:
var time = 0;
var remaining = 15000;
var interval;
$('#start').click(startTimer);
$('#stop').click(stopTimer);
function startTimer() {
if (!interval) {
interval = setInterval(function(){
if (remaining % 1000 === 0)
$('#time').html(time++);
remaining -= 100;
}, 100); // 100 w/ modulo instead of 1000 for better precision
barAnimation();
}
}
function stopTimer() {
$('#timebar').stop();
clearInterval(interval);
interval = false;
}
function barAnimation() {
$("#timebar").animate({ width: "0%" }, remaining, "linear");
}
#timebar {
background-color: black;
width: 100%;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="start">Start</button>
<button id="stop">Stop</button>
<div id="time">0</div>
<div id="timebar"></div>

Categories