separate countdown for divs - javascript

I want to show countdown in each of my divs. Right now I get the seconds from my database and store in in data-countdown attribute and then use the following js code for countdown. Only the first div changes the value every second and the other ones do not change.
Here is the fiddle:
http://fiddle.jshell.net/j61qs7oc/
//imagine this line of code in every loop of a for loop so $remaining will be different
<div style="font-size: 25px; color:#e3b40b ; font-weight: 600;" data-countdown="'.$remaining.'"><i class="fa fa-spinner fa-pulse"></i></div>
here is the js code
$('[data-countdown]').each(function() {
finalDate = $(this).data('countdown');
var $this = $(this);
timeout = null;
time = null;
startCountdown($this,finalDate, 1000, end);
function startCountdown(display,timen, pause, callback) {
time = timen;
display.html(timen);
if (timen == 0)
callback();
else {
clearTimeout(timeout);
timeout = setTimeout(function () {
startCountdown(display,timen - 1, pause, callback)
}, pause);
}
}
function end() {
alert();
}
});

When you declare a variable without using the var keyword, you're creating a global. So each instance of your countdown is overwriting the previous value of finalDate, timeout, and time. Try adding var before each of those lines and it should do what you need. i.e.:
var finalDate = $(this).data('countdown');
var $this = $(this);
var timeout = null;
var time = null;
startCountdown($this,finalDate, 1000, end);

You could also just do this in regular Javascript instead of jQuery...
var countdownDivs = document.querySelectorAll('div[data-countdown]');
function end() {
alert();
}
function countdown(display, timen, pause, callback) {
display.innerHTML = timen;
if (timen == 0) callback();
else {
display.timeout;
clearTimeout(display.timeout);
display.timeout = setTimeout(function () {
countdown(display, timen - 1, pause, callback)
}, pause);
}
}
for(var i = countdownDivs.length>>>0; i--;){
countdown(countdownDivs[i], countdownDivs[i].dataset.countdown, 1000, end);
}
div[data-countdown]{
font-size:25px;
color:#e3b40b;
font-weight:600;
}
<div data-countdown="12312312"></div>
<div data-countdown="555555"></div>
<div data-countdown="95695"></div>

Related

update a delayed function in javascript

I have a simple javascript function that loads on document ready:
var start = 1;
var speed = 1000;
$(document).ready(function () {
go();
setInterval(function () {
go();
}, speed);
This is the function in details:
function go() {
$("#score").html(start.toLocaleString());
start += 1;
}
This is basically a counter which starts from number 1 to infinite, at 1000 milliseconds speed. Here is the thing , now: I have another function:
function modify() {
speed = 500;
}
which regulates the setIntval speed on the main function. The problem is it applies on page refresh only. How do I update it in real time without refreshing page?
You can't update the current one, you have to stop it and set a new timer, which does the same but with a different delay.
var speed = 1000;
var start = 1;
function go() {
$("#score").html(start.toLocaleString());
start += 1;
}
function startGoTimer(){
return = setInterval(function () {
go();
}, speed);
}
function modifyTimer( previousTimer, newDelay=500) {
clearInterval(previousTimer);
speed = newDelay;
startGoTimer();
}
var timer = startGoTimer();
// Some code
modifyTimer(timer, 500);
For fun I just tested what would hapopen if you just change the time:
var timing = 1000;
var interval = setInterval(function(){console.log("test")}, timing);
// Now we get a log every 1000ms, change the var after some time (via console):
timing = 10;
// still an interval of 1000ms.
A really simple solution is to make use of the setInterval's parameters,
var intervalID = scope.setInterval(func, delay[, param1, param2, ...]);
and also pass the speed as param1.
Then, at each interval, check if it changed, and if, clear the existing timer and fire up a new.
Stack snippet
var start = 1;
var speed = 1000;
var timer;
$(document).ready(function() {
go();
timer = setInterval(function(p) {
go(p);
}, speed, speed);
// for this demo
$("button").click(function(){ speed = speed/2});
})
function go(p) {
if(p && p != speed) {
clearInterval(timer);
timer = setInterval(function(p) {
go(p);
}, speed, speed);
}
$("#score").html(start.toLocaleString());
start += 1;
}
div {
display: inline-block;
width: 40px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="score">0</div>
<button>Modify</button>

clearinterval() outside of jquery plugin

I create plugin something like this
timer plugin
(function($) {
$.fn.timer = function(options) {
var defaults = {
seconds: 60
};
var options = $.extend(defaults, options);
return this.each(function() {
var seconds = options.seconds;
var $this = $(this);
var timerIntval;
var Timer = {
setTimer : function() {
clearInterval(timerIntval);
if(seconds <= 0) {
alert("timeout");
}else {
timerIntval = setInterval(function(){
return Timer.getTimer();
}, 1000);
}
},
getTimer : function () {
if (seconds <= 0) {
$this.html("0");
} else {
seconds--;
$this.html(seconds);
}
}
}
Timer.setTimer();
});
};
})(jQuery);
and I call the plugin like this.
$(".myTimer").timer({
seconds : 100
});
i called the plugin at timerpage.php. When i changed the page to xxx.php by clicking another menu, the timer interval is still running and i need to the clear the timer interval.
i created a webpage using jquery ajax load. so my page was not refreshing when i change to another menu.
my question is, how to clear the timer interval or destroy the plugin when i click another menu?
Please try with following modifications:
timer plugin:
(function($) {
$.fn.timer = function(options) {
var defaults = {
seconds: 60
};
var options = $.extend(defaults, options);
return this.each(function() {
var seconds = options.seconds;
var $this = $(this);
var timerIntval;
var Timer = {
setTimer : function() {
clearInterval(timerIntval);
if(seconds <= 0) {
alert("timeout");
}else {
timerIntval = setInterval(function(){
return Timer.setTimer();
}, 1000);
$this.data("timerIntvalReference", timerIntval); //saving the timer reference for future use
}
},
getTimer : function () {
if (seconds <= 0) {
$this.html("0");
} else {
seconds--;
$this.html(seconds);
}
}
}
Timer.setTimer();
});
};
})(jQuery);
Now in some other JS code which is going to change the div content
var intervalRef = $(".myTimer").data("timerIntvalReference"); //grab the interval reference
clearInterval(intervalRef); //clear the old interval reference
//code to change the div content on menu change
For clearing timer associated with multiple DOM element, you may check below code:
//iterate ovel all timer element:
$("h3[class^=timer]").each(function(){
var intervalRef = $(this).data("timerIntvalReference"); //grab the interval reference
clearInterval(intervalRef);
});
Hope this will give an idea to deal with this situation.
Instead of var timerIntval; set the variable timerInterval on the window object, then you will have the access this variable until the next refresh.
window.timerIntval = setInterval(function() {
Then when the user clicks on any item menu you can clear it:
$('menu a').click(function() {
clearInterval(window.timerIntval);
});
Live example (with multiple intervals)
$('menu a').click(function(e) {
e.preventDefault();
console.log(window.intervals);
for (var i = 0; i < window.intervals.length; i++) {
clearInterval(window.intervals[i]);
}
});
(function($) {
$.fn.timer = function(options) {
var defaults = {
seconds: 60
};
var options = $.extend(defaults, options);
return this.each(function() {
if (!window.intervals) {
window.intervals = [];
}
var intervalId = -1;
var seconds = options.seconds;
var $this = $(this);
var Timer = {
setTimer : function() {
clearInterval(intervalId);
if(seconds <= 0) {
alert("timeout");
} else {
intervalId = setInterval(function(){
//Timer.getTimer();
return Timer.getTimer();
}, 1000);
window.intervals.push(intervalId);
}
},
getTimer : function () {
if (seconds <= 0) {
$this.html("0");
} else {
seconds--;
$this.html(seconds);
}
}
}
Timer.setTimer();
});
};
})(jQuery);
$(".myTimer").timer({
seconds : 100
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<menu>
Menu 1
</menu>
<div class="myTimer"></div>
<div class="myTimer"></div>
Just notice that it's little bit risky because you can only run it once otherwise the interval id of the second will override the first.

Clear interval from within a closure

I'm trying to clear an interval when the user hovers over an element and then start it up again when they hover off an element. I think this is a closure but I'm not sure, hopefully my code will make sense what I'm trying to do.
var rotatorInterval = function(elem){
var interval = setInterval(function(){
var active = elem.find('.dot.active');
if(active.is('.dot:last-of-type',elem)){
elem.find('.dot').first().click();
}else{
active.next().click();
}
},6000);
interval;
return interval;
};
if($('.rotator').length){
$('.rotator').each(function(){
var self = $(this);
rotatorInterval(self);
self.find('.slide, .dot').on('mouseenter',function(){
console.log('hovered');
clearInterval(interval);
});
});
}
I tried returning the interval from that closure but when I hovered it said interval (the name of the variable I returned) is not defined, so it's like it didn't return it or something.
You just have to actually return the interval reference somewhere
var rotatorInterval = function (elem) {
var interval = setInterval(function () {
var active = elem.find('.dot.active');
if (active.is('.dot:last-of-type', elem)) {
elem.find('.dot').first().click();
} else {
active.next().click();
}
}, 6000);
return interval;
};
if ($('.rotator').length) {
$('.rotator').each(function () {
var self = $(this);
var return_interval = rotatorInterval(self);
self.find('.slide, .dot').on('mouseenter', function () {
clearInterval(return_interval);
});
});
}

Reset Counter OnClick

I have some code I am using to countdown from 15 after 15 secs passes it echoes "times over". The problem is if someone clicks twice there will be two counters in the same Div. I need the counter to reset if someone clicks on the button again.
function startCountDown(i, p, f) {
// store parameters
var pause = p;
var fn = f;
// make reference to div
var countDownObj = document.getElementById("countDown");
if (countDownObj == null) {
// error
alert("div not found, check your id");
// bail
return;
}
countDownObj.count = function (i) {
// write out count
countDownObj.innerHTML = i;
if (i == 0) {
// execute function
fn();
// stop
return;
}
setTimeout(function () {
// repeat
countDownObj.count(i - 1);
},
pause);
}
// set it going
countDownObj.count(i);
}
function myFunction() {
alert("Time Over");
}
HTML:
<div id="TimerTitle">Timer</div>
<span id="countDown"></span>
<button onclick="startCountDown(15, 1000, myFunction);">
Start Time
</button>
Set the timeout as a global variable like so:
timer = setTimeout(function(){countDownObj.count(i - 1);},pause);
At the beginning of the function clear the timeout
clearTimeout(timer)
Code:
var timer;
function startCountDown(i, p, f) {
// store parameters
if(timer){clearTimeout(timer)}
var pause = p;
var fn = f;
// make reference to div
var countDownObj = document.getElementById("countDown");
if (countDownObj == null) {
// error
alert("div not found, check your id");
// bail
return;
}
countDownObj.count = function (i) {
// write out count
countDownObj.innerHTML = i;
if (i == 0) {
// execute function
fn();
// stop
return;
}
timer = setTimeout(function(){countDownObj.count(i - 1);},pause);
}
// set it going
countDownObj.count(i);
}
function myFunction() {
alert("Time Over");
}
setTimeout returns an id to the timer that can be used with window.clearTimeout. The simplest solution would be to create a global timerId
var timerId;
...
function startCountDown(i, p, f) {
...
if (timerId) { window.clearTimeout(timerId); }
timerId = window.setTimeout(...);
https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout

javascript scoping? - jquery plugin countdown timer

Hi I am having a problem where scoping seems to be lost. What I am doing wrong?
Suppose the logic is that 1 second is decreased from every counter and not from the last counter only.
What I am doing wrong?
<html>
<head>
<link rel="stylesheet" href="http://static.jquery.com/files/rocker/css/reset.css" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
(function($){
$.fn.extend({
countdown: function(options) {
var defaults = {
daysSelector : 'span.days',
hoursSelector : 'span.hours',
minutesSelector : 'span.minutes',
secondsSelector : 'span.seconds'
}
var options = $.extend(defaults, options);
var _this = $(this);
tick = function()
{
var days = _this.find(options.daysSelector);
var hours = _this.find(options.hoursSelector);
var minutes = _this.find(options.minutesSelector);
var seconds = _this.find(options.secondsSelector);
console.log(_this);
var currentSeconds=seconds.text();
currentSeconds--;
if(currentSeconds<0)
{
seconds.text("59");
var currentMinutes=minutes.text();
currentMinutes--;
if(currentMinutes<0)
{
(minutes).text("59");
var currentHours=(hours).text();
currentHours--;
if(currentHours<0)
{
(hours).text("23");
var currentDays=(hours).text();
currentDays--;
}
else
{
if(currentHours.toString().length==1)
{
(hours).text('0'+currentHours);
}
else
{
(hours).text(currentHours);
}
}
}
else
{
if(currentMinutes.toString().length==1)
{
(minutes).text('0'+currentMinutes);
}
else
{
(minutes).text(currentMinutes);
}
}
}
else
{
if(currentSeconds.toString().length==1)
{
seconds.text('0'+currentSeconds);
}
else
{
seconds.text(currentSeconds);
}
}
}
return this.each(function()
{
console.log(_this);
setInterval("this.tick()",1000);
});
}
});
})(jQuery);
$(document).ready(function()
{
$("#timer1").countdown();
$("#timer2").countdown();
$("#timer3").countdown();
});
</script>
</head>
<body>
<div id="timer1">
<span class="days">1</span>
<span class="hours">18</span>
<span class="minutes">6</span>
<span class="seconds">45</span>
</div>
<div id="timer2">
<span class="days">2</span>
<span class="hours">28</span>
<span class="minutes">1</span>
<span class="seconds">59</span>
</div>
<div id="timer3">
<span class="days">10</span>
<span class="hours">0</span>
<span class="minutes">59</span>
<span class="seconds">59</span>
</div>
</body>
I edited your code
(function($){
$.fn.extend({
countdown: function(options) {
var defaults = {
daysSelector : 'span.days',
hoursSelector : 'span.hours',
minutesSelector : 'span.minutes',
secondsSelector : 'span.seconds'
}
var options = $.extend(defaults, options);
var _this = $(this);
var tick = function()
{
var days = _this.find(options.daysSelector);
var hours = _this.find(options.hoursSelector);
var minutes = _this.find(options.minutesSelector);
var seconds = _this.find(options.secondsSelector);
console.log(_this);
var currentSeconds=seconds.text();
currentSeconds--;
if(currentSeconds<0)
{
seconds.text("59");
var currentMinutes=minutes.text();
currentMinutes--;
if(currentMinutes<0)
{
(minutes).text("59");
var currentHours=(hours).text();
currentHours--;
if(currentHours<0)
{
(hours).text("23");
var currentDays=(hours).text();
currentDays--;
}
else
{
if(currentHours.toString().length==1)
{
(hours).text('0'+currentHours);
}
else
{
(hours).text(currentHours);
}
}
}
else
{
if(currentMinutes.toString().length==1)
{
(minutes).text('0'+currentMinutes);
}
else
{
(minutes).text(currentMinutes);
}
}
}
else
{
if(currentSeconds.toString().length==1)
{
seconds.text('0'+currentSeconds);
}
else
{
seconds.text(currentSeconds);
}
}
}
return _this.each(function()
{
console.log(_this);
setInterval(tick,1000);
});
}
});
})(jQuery);
$(document).ready(function()
{
$("#timer1").countdown();
$("#timer2").countdown();
$("#timer3").countdown();
});
tick was global and that was the problem (also never pass code to be evalued to setInterval!
Fiddle here: http://jsfiddle.net/kvqWR/1/
If I have to guess what the issue is... only 1 timer actually counts down and the other two doesn't?
That's because the extended countdown timer function you have never passes the name (id) of the countdown timer through to the function. So it always only executes the last one since the last one is what was called...last.
You'd need to send through the name (id) of the div for it to count down all 3 simultaneously.
You declared tick in global scope. Make it local:
var tick = function()...
and don't pass a string to setInterval:
setInterval(tick,1000);
A better structure of your plugin would be to declare the tick function only once and pass the current element (or the time fields) as argument:
(function($){
var tick = function(days, hours, minutes, seconds) {
//...
};
$.fn.countdown = function(options) {
var defaults = {
daysSelector : 'span.days',
hoursSelector : 'span.hours',
minutesSelector : 'span.minutes',
secondsSelector : 'span.seconds'
}
var options = $.extend(defaults, options);
return this.each(function(){
var days = $(this).find(options.daysSelector);
var hours = $(this).find(options.hoursSelector);
var minutes = $(this).find(options.minutesSelector);
var seconds = $(this).find(options.secondsSelector);
setInterval(function() {
tick(days, hours, minutes, seconds);
},1000);
});
};
}(jQuery));
Then your plugin also works if you select multiple elements at once:
$("#timer1, #timer2, #timer3").countdown();
which would not work otherwise (see here http://jsfiddle.net/fkling/kvqWR/5/).
Working DEMO
It might also be better to have only setInterval going on which iterates over selected elements and performs the operation. The fewer timers you have, the better. Have a look at this example: http://jsfiddle.net/fkling/kvqWR/4/

Categories