I'm creating a simple web browser game. In it there is a clock, it doesn't display the real time, but is supposed to start at 9:00 am and avance 1h every time a button is clicked. The problem is that it only works on the first button click and always stays at 10am when I click a button again. I don't understand why.
Here's the code:
<script role="script" id="twine-user-script" type="text/twine-javascript">
$(document).ready( function() {
var callback = function() {
displayTime.incrementTime();
};
$('#onebutton .link-internal').on('click', callback);
$('#container1 .link-internal').on('click', callback);
$('#container2 .link-internal').on('click', callback);
var displayTime = {
currentTime: new Date(new Date().getUTCFullYear(),
new Date().getUTCMonth(),
new Date().getUTCDay(),
'9',
'0',
'0',
'0'),
hoursVal: null,
hours: function() {
return displayTime.currentTime.getHours();
},
minutesVal: null,
minutes: function() {
return displayTime.currentTime.getMinutes();
},
meridiem: "am",
init: function() {
displayTime.formatDate();
displayTime.display();
},
formatDate: function() {
if (displayTime.hours() > 12) {
displayTime.hoursVal = displayTime.hours() - 12;
displayTime.meridiem = "pm";
}
if (displayTime.hours() === 0) {
displayTime.hoursVal = 12;
}
if(displayTime.hours() < 10) {
displayTime.hoursVal = "0" + displayTime.hours();
}
if(displayTime.minutes() < 10) {
displayTime.minutesVal = "0" + displayTime.minutes();
}
},
resetValues: function() {
displayTime.hoursVal = null;
displayTime.minutesVal = null;
//displayTime.secondsVal = null;
},
display: function() {
var clockDiv = document.getElementById('clock');
clockDiv.innerText = (displayTime.hoursVal === null ? displayTime.hours() : displayTime.hoursVal) + ":" +
(displayTime.minutesVal === null ? displayTime.minutes() : displayTime.minutesVal) + " " +
displayTime.meridiem;
},
incrementTime: function() {
displayTime.currentTime = new Date(displayTime.currentTime.getTime() + (1000 * 60 * 60));
displayTime.resetValues();
displayTime.formatDate();
displayTime.display();
}
};
displayTime.init();
setInterval(displayTime.init, 1000);
});
</script>
Can anyone point out where the error in my code is?
This JSFiddle works better, but I need your HTML too to give you the exact problem you have:
JS:
$(document).ready(function () {
var callback = function () {
displayTime.incrementTime();
};
$('#onebutton').on('click', callback);
$('#container1').on('click', callback);
$('#container2').on('click', callback);
var displayTime = {
currentTime: new Date(new Date().getUTCFullYear(),
new Date().getUTCMonth(),
new Date().getUTCDay(),
'9',
'0',
'0',
'0'),
hoursVal: null,
hours: function () {
return displayTime.currentTime.getHours();
},
minutesVal: null,
minutes: function () {
return displayTime.currentTime.getMinutes();
},
meridiem: "am",
init: function () {
displayTime.formatDate();
displayTime.display();
},
formatDate: function () {
if (displayTime.hours() > 12) {
displayTime.hoursVal = displayTime.hours() - 12;
displayTime.meridiem = "pm";
}
if (displayTime.hours() === 0) {
displayTime.hoursVal = 12;
}
if (displayTime.hours() < 10) {
displayTime.hoursVal = "0" + displayTime.hours();
}
if (displayTime.minutes() < 10) {
displayTime.minutesVal = "0" + displayTime.minutes();
}
},
resetValues: function () {
displayTime.hoursVal = null;
displayTime.minutesVal = null;
//displayTime.secondsVal = null;
},
display: function () {
var clockDiv = document.getElementById('clock');
clockDiv.innerText = (displayTime.hoursVal === null ? displayTime.hours() : displayTime.hoursVal) + ":" + (displayTime.minutesVal === null ? displayTime.minutes() : displayTime.minutesVal) + " " + displayTime.meridiem;
},
incrementTime: function () {
displayTime.currentTime = new Date(displayTime.currentTime.getTime() + (1000 * 60 * 60));
displayTime.resetValues();
displayTime.formatDate();
displayTime.display();
}
};
displayTime.init();
setInterval(displayTime.init, 1000);
});
Related
Am creating a coming soon website
<div class="countdown styled"></div>
From this div, I need a countdown timer. So I used the default countdown timer from jquery.countdown.js.
UI coming correctly and shows seconds at 0 only.
What do I have to do to run a counter?
Am getting Js code at jquery.countdown.js file
(function() {
(function($) {
$.countdown = function(el, options) {
var getDateData,
_this = this;
this.el = el;
this.$el = $(el);
this.$el.data("countdown", this);
this.init = function() {
_this.options = $.extend({}, $.countdown.defaultOptions, options);
if (_this.options.refresh) {
_this.interval = setInterval(function() {
return _this.render();
}, _this.options.refresh);
}
_this.render();
return _this;
};
getDateData = function(endDate) {
var dateData, diff;
endDate = Date.parse($.isPlainObject(_this.options.date) ? _this.options.date : new Date(_this.options.date));
diff = (endDate - Date.parse(new Date)) / 1000;
if (diff <= 0) {
diff = 0;
if (_this.interval) {
_this.stop();
}
_this.options.onEnd.apply(_this);
}
dateData = {
years: 0,
days: 10,
hours: 10,
min: 20,
sec: 14,
millisec: 10
};
$.countdown.defaultOptions = {
date: "Dec 25, 2021 15:03:25",
refresh: 1000,
onEnd: $.noop,
render: function(date) {
return $(this.el).html("" + date.years + " years, " + date.days + " days, " + (this.leadingZeros(date.hours)) + " hours, " + (this.leadingZeros(date.min)) + " min and " + (this.leadingZeros(date.sec)) + " sec");
}
};
$.fn.countdown = function(options) {
return $.each(this, function(i, el) {
var $el;
$el = $(el);
if (!$el.data('countdown')) {
return $el.data('countdown', new $.countdown(el, options));
}
});
};
return void 0;
})(jQuery);
}).call(this);
But counter not running . Only UI coming . Thanks in advance
I am using this code for countdown timer to a quiz test. Its working Fine. In this I need to add two buttons one is Pause button and another one is Resume button. When I click a Pause button it's stop time and when I click a Resume button it's start from where I pause the time.
I have tried this code. It's not working.
HTML Code
<div class="div__time">
<div style="display: none;" id="overall_time"></div>
<div id="overall_times"></div>
<div class="total_time"></div>
</div>
<input id="pauseButton" type="button" value="Pause">
<input id="resumeButton" type="button" value="Resume">
JS Code
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" type="text/javascript"></script>
<script>
var speaking_ms = "00:00:10";
var speaking_ms_arr = speaking_ms.split(":");
var speaking_time_min_sec = (+speaking_ms_arr[0]) * 60 * 60 + (+speaking_ms_arr[1]) * 60 + (+speaking_ms_arr[2]);
var speaking_time_min_sec = parseInt(speaking_time_min_sec) + 1;
var speaking_value;
if (localStorage.getItem("speaking_counter")) {
if (localStorage.getItem("speaking_counter") <= 0) {
speaking_value = speaking_time_min_sec;
} else {
speaking_value = localStorage.getItem("speaking_counter");
}
} else {
speaking_value = speaking_time_min_sec;
}
document.getElementById('overall_time').innerHTML = speaking_value;
var speaking_counter = function() {
if (speaking_value <= 0) {
localStorage.setItem("speaking_counter", speaking_time_min_sec);
} else {
speaking_value = parseInt(speaking_value) - 1;
localStorage.setItem("speaking_counter", speaking_value);
}
document.getElementById('overall_time').innerHTML = speaking_value;
if (speaking_value == 0) {
localStorage.setItem("speaking_counter", speaking_value);
setTimeout(function() {
clearInterval(interval);
}, 1000);
}
var hours = Math.floor(speaking_value / 3600);
var minutes = Math.floor(speaking_value % 3600 / 60);
var seconds = Math.floor(speaking_value % 3600 % 60);
var red_time = hours + ' : ' + minutes + ' : ' + seconds;
document.getElementById('overall_times').innerHTML = red_time;
};
var interval = setInterval(function() {
speaking_counter();
}, 1000);
var Clock = {
pause: function() {
clearInterval(this.interval);
delete this.interval;
},
resume: function() {
if (!this.interval) this.start();
}
};
$('#pauseButton').click(function() { Clock.pause(); });
$('#resumeButton').click(function() { Clock.resume(); });
</script>
Fiddle Link
Thanks in Advance.
I changed the end of the code in the fiddle and it worked: https://jsfiddle.net/bohv0j9w/5/
var Clock = {
pause: function() {
clearInterval(interval);
interval = null;
},
resume: function() {
if (!interval) interval = setInterval(speaking_counter, 1000);
}
};
document.querySelector('#pauseButton').addEventListener("click", Clock.pause);
document.querySelector('#resumeButton').addEventListener("click", Clock.resume);
In your version this.internal is undefined you may want to learn about "this" and scopes in js. And $(...) is a syntax that requires the lib jquery (not included in the fiddle), the vanilla js equivalent is "document.querySelector", you should read the MDN article about this function.
Finally, your syntax is a bit old school. If you use localStorage you aren't targeting very old browsers maybe you should avoid jquery and use keywords such as "const".
Following are your mistakes. (Fiddle link)
var Clock = {
pause: function() {
clearInterval(this.interval);//(this.interval is undefined, replace it with interval)
^^^^
delete this.interval;//(this.interval is undefined, replace it with interval)
^^^^
},
resume: function() {
if (!this.interval) this.start();//(this.interval is undefined, replace it typeof interval === undefined to check whether interval exists or not)
^^^^
^^^^^^^^^^^ //(this.start() is also not defined, replace it with start() and move `var interval = setInterval(fun.....` into new start function)
}
};
look at following code.
var speaking_ms = "00:00:10";
var speaking_ms_arr = speaking_ms.split(":");
var speaking_time_min_sec = (+speaking_ms_arr[0]) * 60 * 60 + (+speaking_ms_arr[1]) * 60 + (+speaking_ms_arr[2]);
var speaking_time_min_sec = parseInt(speaking_time_min_sec) + 1;
var speaking_value;
if (localStorage.getItem("speaking_counter")) {
if (localStorage.getItem("speaking_counter") <= 0) {
speaking_value = speaking_time_min_sec;
} else {
speaking_value = localStorage.getItem("speaking_counter");
}
} else {
speaking_value = speaking_time_min_sec;
}
document.getElementById('overall_time').innerHTML = speaking_value;
var speaking_counter = function() {
if (speaking_value <= 0) {
localStorage.setItem("speaking_counter", speaking_time_min_sec);
} else {
speaking_value = parseInt(speaking_value) - 1;
localStorage.setItem("speaking_counter", speaking_value);
}
document.getElementById('overall_time').innerHTML = speaking_value;
if (speaking_value == 0) {
localStorage.setItem("speaking_counter", speaking_value);
setTimeout(function() {
clearInterval(interval);
}, 1000);
}
var hours = Math.floor(speaking_value / 3600);
var minutes = Math.floor(speaking_value % 3600 / 60);
var seconds = Math.floor(speaking_value % 3600 % 60);
var red_time = hours + ' : ' + minutes + ' : ' + seconds;
document.getElementById('overall_times').innerHTML = red_time;
};
var start = function() {
interval = setInterval(function() {
speaking_counter();
}, 1000);
}
var Clock = {
pause: function() {
clearInterval(interval);
delete interval;
},
resume: function() {
if (typeof interval === 'undefined') start();
}
};
$('#pauseButton').click(function() { Clock.pause(); });
$('#resumeButton').click(function() { Clock.resume(); });
start();
I have fixed your solution and implemented what you were trying to implement. There were minor issues with your solution which i fixed.
1. You were using this to access interval variable that was not part of the clock object in which you were accessing the variable.
2. You were using this.start() function in the resume function of clock object which was not part of the clock object.
3. JQuery used to define events was not included in the fiddle. Although it is included in the code pasted in the question.
html:
<html>
<head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
</head>
<body>
<div class="div__time">
<div style="display: none;" id="overall_time"></div>
<div id="overall_times"></div>
<div class="total_time"></div>
</div>
<input id="pauseButton" type="button" value="Pause">
<input id="resumeButton" type="button" value="Resume">
</body>
js:
var speaking_ms = "00:00:10";
var speaking_ms_arr = speaking_ms.split(":");
var speaking_time_min_sec = (+speaking_ms_arr[0]) * 60 * 60 + (+speaking_ms_arr[1]) * 60 + (+speaking_ms_arr[2]);
var speaking_time_min_sec = parseInt(speaking_time_min_sec) + 1;
var speaking_value;
if (localStorage.getItem("speaking_counter")) {
if (localStorage.getItem("speaking_counter") <= 0) {
speaking_value = speaking_time_min_sec;
} else {
speaking_value = localStorage.getItem("speaking_counter");
}
} else {
speaking_value = speaking_time_min_sec;
}
document.getElementById('overall_time').innerHTML = speaking_value;
var speaking_counter = function() {
if (speaking_value <= 0) {
localStorage.setItem("speaking_counter", speaking_time_min_sec);
} else {
speaking_value = parseInt(speaking_value) - 1;
localStorage.setItem("speaking_counter", speaking_value);
}
document.getElementById('overall_time').innerHTML = speaking_value;
if (speaking_value == 0) {
localStorage.setItem("speaking_counter", speaking_value);
setTimeout(function() {
clearInterval(interval);
}, 1000);
}
var hours = Math.floor(speaking_value / 3600);
var minutes = Math.floor(speaking_value % 3600 / 60);
var seconds = Math.floor(speaking_value % 3600 % 60);
var red_time = hours + ' : ' + minutes + ' : ' + seconds;
document.getElementById('overall_times').innerHTML = red_time;
};
var interval = setInterval(function() {
speaking_counter();
}, 1000);
var Clock = {
pause: function() {
clearInterval(interval);
delete interval;
},
resume: function() {
interval = setInterval(function() {
speaking_counter();
}, 1000);
}
};
$('#pauseButton').click(function() { Clock.pause(); });
$('#resumeButton').click(function() { Clock.resume(); });
Here is the fiddle link: JS Fiddle
You have gotten quite a few answers so far, I wanted to give you another example so that you have an alternative architecture to consider. Definitely read up on scopes and closures and the use of this. Also, you don't need jQuery for this function. Only import a large library like jQuery when you need it. In this case document.getElementById does you well, as does addEventListener.
There are even more exciting ways to handle a countdown (including the use of recursion), but below is an example of envisioning your Clock as a single object. This gives you greater control over the clock and it makes it reusable.
You can define functions on the clock that manage the interval, decrement the countdown, pause, resume, start and reset the clock. I added buttons for each so you can see how it would work. Also note that I commented out localStorage to show that it works without storage, but mainly because the snippets in StackOverflow aren't given permission to use localStorage so it would crash, but you can run this code here as is.
var Clock = {
speaking_value: 10,
interval: -1,
countdown: function() {
speaking_value = this.speaking_value;
if (speaking_value <= 0) {
//localStorage.setItem("speaking_counter", 0);
} else {
speaking_value = parseInt(speaking_value) - 1;
//localStorage.setItem("speaking_counter", speaking_value);
}
document.getElementById('overall_time').innerHTML = speaking_value;
if (speaking_value == 0) {
//localStorage.setItem("speaking_counter", speaking_value);
var self = this;
setTimeout(function() {
clearInterval(self.interval);
}, 1000);
}
this.speaking_value = speaking_value;
this.updateClock();
},
paused: false,
pause: function() {
clearInterval(this.interval);
this.paused = true;
},
resume: function() {
if (this.paused) {
this.paused = false;
this.tick();
}
},
updateClock: function() {
speaking_value = this.speaking_value;
var hours = Math.floor(speaking_value / 3600);
var minutes = Math.floor(speaking_value % 3600 / 60);
var seconds = Math.floor(speaking_value % 3600 % 60);
var red_time = hours + ' : ' + minutes + ' : ' + seconds;
document.getElementById('overall_times').innerHTML = red_time;
},
tick: function() {
var self = this;
this.interval = setInterval(function() {
self.countdown()
}, 1000)
},
start: function() {
this.updateClock();
this.tick();
},
reset: function(){
clearInterval(this.interval);
this.speaking_value = 10;
this.updateClock();
}
};
Clock.updateClock();
document.getElementById('startButton').addEventListener('click', function() { Clock.start(); });
document.getElementById('pauseButton').addEventListener('click', function() { Clock.pause(); });
document.getElementById('resumeButton').addEventListener('click', function() { Clock.resume(); });
document.getElementById('resetButton').addEventListener('click', function() { Clock.reset(); });
<div class="div__time">
<div style="display: none;" id="overall_time"></div>
<div id="overall_times"></div>
<div class="total_time"></div>
</div>
<input id="startButton" type="button" value="Start">
<input id="pauseButton" type="button" value="Pause">
<input id="resumeButton" type="button" value="Resume">
<input id="resetButton" type="button" value="Reset">
I'm using the Jcounter plugin http://devingredients.com/jcounter/ on a project, it's perfect for what I need, except for a problem I'm facing. I need it when I finish the countdown, it updates the same page only once, without giving infinite loop. How can I do this?
The callback function seems to give this possibilide, would I need some control? I have little experience in jquery.
<!DOCTYPE html>
<html>
<head>
<title>jCounter - jQuery plugin - devingredients.com</title>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">
<link rel="stylesheet" type="text/css" href="css/jquery.jCounter-iosl.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.jCounter-0.1.4.js"></script>
<script type="text/javascript">
$(document).ready(function() {
//first counter
$(".countdown1").jCounter({
animation: "slide",
date: "2017/10/13 19:53:20",
format: "dd:hh:mm:ss",
twoDigits: 'on',
callback: function() {
window.location.href = "index.php?block=Yes"
}
});
});
</script>
<div class="iosl-theme-wrapper countdown1">
<div class="iosl-theme">
<ul>
<li><p><span><em><b class="days">00</b><i class="daysSlider"><u>00</u><u>00</u></i></em></span></p></li>
<li><p><span><em><b class="hours">00</b><i class="hoursSlider"><u>00</u><u>00</u></i></em></span></p></li>
<li><p><span><em><b class="minutes">00</b><i class="minutesSlider"><u>00</u><u>00</u></i></em></span></p></li>
<li><p><span><em><b class="seconds">00</b><i class="secondsSlider"><u>00</u><u>00</u></i></em></span></p></li>
</ul>
<div class="jC-clear"></div>
<p class="jCtext">
<span><em class="textSeconds">SEGUNDOS</em></span>
<span><em class="textMinutes">MINUTOS</em></span>
<span><em class="textHours">HORAS</em></span>
<span><em class="textDays">DIAS</em></span>
</p>
<div class="jC-clear"></div>
</div>
</div>
**jquery.jCounter-0.1.4.js**
/**********************************
* jCounter Script v0.1.4 (beta)
* Author: Catalin Berta
* Official page and documentation: http://devingredients.com/jcounter
* Licensed under the MIT license
**********************************/
;(function($,document,window,undefined) {
//once upon a time...
$.fn.jCounter = function(options,callback) {
var jCounterDirection = 'down'; // points out whether it should count down or up | handled via customRange setting
var customRangeDownCount; //if true, it will tell countdown_proc() it's a down count and not an up count
var days,hours,minutes,seconds;
var endCounter = false; //stops jCounter if true
var eventDate; //time target (holds a number of seconds)
var pausedTime; //stores the time (in seconds) when pausing
var thisEl = this; //custom 'this' selector
var thisLength = this.length; //number of multiple elements per selector
var pluralLabels = new Array('DAYS','HOURS','MINUTES','SECONDS'); //plural labels - used for localization
var singularLabels = new Array('DAY','HOUR','MINUTE','SECOND'); //singular labels - used for localization
this.options = options; //stores jCounter's options parameter to verify against specified methods
this.version = '0.1.4';
//default settings
var settings = {
animation: null,
callback: null,
customDuration: null,
customRange: null,
date: null,
debugLog: false,
serverDateSource: 'dateandtime.php', //path to dateandtime.php file (i.e. http://my-domain.com/dateandtime.php)
format: 'dd:hh:mm:ss',
timezone: 'Europe/London',
twoDigits: 'on'
};
//merge the settings with the options values
if (typeof options === 'object') {
$.extend(settings,options);
thisEl.data("userOptions", settings); //push the settings to applied elements (they're used by methods)
}
if(thisEl.data('userOptions').debugLog == true && window['console'] !== undefined ) {
var consoleLog = true; //shows debug messages via console.log() if true
}
//METHODS
var jC_methods = {
//initialize
init : function() {
thisEl.each(function(i,el) {
initCounter(el);
});
},
//pause method: $.jCounter('pause')
pause : function() {
if(consoleLog) { console.log("(jC) Activity: Counter paused."); }
endCounter = true;
return thisEl.each(function(i,el) {
clearInterval($(el).data("jC_interval"));
});
},
//stop method: $.jCounter('stop')
stop : function() {
if(consoleLog) { console.log("(jC) Activity: Counter stopped."); }
endCounter = true;
return thisEl.each(function(i,el) {
clearInterval($(el).data("jC_interval"));
$(el).removeData("jC_pausedTime");
resetHTMLCounter(el);
});
},
//reset method: $.jCounter('reset')
reset : function() {
if(consoleLog) { console.log("(jC) Activity: Counter reset."); }
return thisEl.each(function(i,el) {
clearInterval($(el).data("jC_interval"));
resetHTMLCounter(el);
initCounter(el);
});
},
//start method: $.jCounter('start')
start : function() {
if(consoleLog) { console.log("(jC) Activity: Counter started."); }
return thisEl.each(function(i,el) {
pausedTime = $(el).data("jC_pausedTime");
endCounter = false;
clearInterval($(el).data("jC_interval"));
initCounter(el);
});
}
}
//checks whether customDuration is used
if(thisEl.data("userOptions").customDuration) {
if(!isNaN(thisEl.data("userOptions").customDuration)) {
var customDuration = true;
} else {
var customDuration = false;
if(consoleLog) { console.log("(jC) Error: The customDuration value is not a number! NOTE: 'customDuration' accepts a number of seconds."); }
}
}
//checks whether customRange is used
if(thisEl.data("userOptions").customRange) {
var customRangeValues = thisEl.data("userOptions").customRange.split(":");
var rangeVal0 = parseInt(customRangeValues[0]);
var rangeVal1 = parseInt(customRangeValues[1]);
if(!isNaN(rangeVal0) && !isNaN(rangeVal1)) {
var customRange = true;
if(rangeVal0 > rangeVal1) {
var customRangeDownCount = true;
} else {
var customRangeDownCount = false;
jCounterDirection = 'up';
}
} else {
var customRange = false;
if(consoleLog) { console.log("(jC) Error: The customRange value is not a valid range! Example: customRange: '0:30' or '30:0'"); }
}
}
//checks whether animation is set to slide
if(thisEl.data("userOptions").animation == 'slide') {
thisEl.data("jCanimation","slide");
}
//FUNCTIONS
//jCounter initializer
function initCounter(el) {
if(customDuration) {
if (pausedTime) {
if (!isNaN(pausedTime)) {
eventDate = Math.round(pausedTime);
}
} else {
eventDate = Math.round($(el).data("userOptions").customDuration);
}
currentTime = 0;
countdown_proc(currentTime,el);
$(el).data("jC_interval", setInterval(function(){
if(endCounter == false) {
currentTime = parseInt(currentTime) + 1;
countdown_proc(currentTime,el)
}
},1000));
} else if(customRange) {
eventDate = Math.round(customRangeValues[1]);
if (pausedTime) {
if (!isNaN(pausedTime)) {
var currentTime = eventDate - pausedTime;
}
} else {
var currentTime = Math.round(customRangeValues[0]);
}
countdown_proc(currentTime,el);
$(el).data("jC_interval", setInterval(function(){
if(endCounter == false) {
var ifRangeDownCount = (customRangeDownCount) ? currentTime = parseInt(currentTime) - 1 : currentTime = parseInt(currentTime) + 1;
countdown_proc(currentTime,el);
}
},1000));
} else {
eventDate = Date.parse($(el).data("userOptions").date) / 1000;
dateSource = thisEl.data("userOptions").serverDateSource + '?timezone=' + thisEl.data("userOptions").timezone + '&callback=?';
$.ajax({
url: dateSource,
dataType : 'json',
data : {},
success : function(data, textStatus){
var currentDate = Date.parse(data.currentDate) / 1000;
startCounter(currentDate,el);
},
error : function(){
if(consoleLog) { console.log("(jC) Error: Couldn't find dateandtime.php from serverDateSource: " + thisEl.data('userOptions').serverDateSource + "\n(jC) - Make sure the path is correct! \n(jC) - Now using the client-side time (not recommended).") }
var currentDate = Math.floor($.now() / 1000);
startCounter(currentDate,el);
}
});
}
}
function startCounter(currentDate,el) {
countdown_proc(currentDate,el);
if (eventDate > currentDate) {
$(el).data("jC_interval", setInterval(function(){
if(endCounter == false) {
currentDate = parseInt(currentDate) + 1;
countdown_proc(currentDate,el)
}
},1000));
} else {
resetHTMLCounter(el)
}
}
//jCslider - adds the slide effect layer
//Note: this requires a jCounter slide-ready theme! (i.e. iOS dark or iOS light)
function jCslider(el,unitClass,timeUnit,eventDate,duration) {
$(el).find(unitClass + " u").each(function(i,el) {
var twoDigits = (thisEl.data("userOptions").twoDigits == 'on') ? '0' : '';
var newIndex = (jCounterDirection == 'up') ? newIndex = -i : newIndex = i;
currNo = parseInt(timeUnit,10) + (newIndex);
if (String(parseInt(timeUnit,10)).length >= 2) {
$(el).text(parseInt(timeUnit,10) + (newIndex))
} else if(String(parseInt(timeUnit,10)).length == 1 && currNo == 10) {
$(el).text(parseInt(timeUnit,10) + (newIndex))
} else {
$(el).text(twoDigits + (parseInt(timeUnit,10) + (newIndex)));
}
})
$(el).find(unitClass).animate({
top: '0.15em'
},200, function() {
$(el).find(unitClass + " u:eq(1)").remove();
$(el).find(unitClass).prepend('<u></u>');
$(el).find(unitClass).css({'top':'-1.24em'})
});
}
//resets jCounter's HTML values to 0 or 00, based on the twoDigits setting
function resetHTMLCounter(el) {
if(thisEl.data("userOptions").twoDigits == 'on') {
$(el).find(".days,.hours,.minutes,.seconds").text('00');
} else if(thisEl.data("userOptions").twoDigits == 'off') {
$(el).find(".days,.hours,.minutes,.seconds").text('0');
}
if(thisEl.data("jCanimation") == 'slide') {
$(el).find(".daysSlider u,.hoursSlider u,.minutesSlider u,.secondsSlider u").text('00');
}
}
//main jCounter processor
function countdown_proc(duration,el) {
//check if the counter needs to count down or up
if(customRangeDownCount) {
if(eventDate >= duration) {
clearInterval($(el).data("jC_interval"));
if(thisEl.data("userOptions").callback) {
thisEl.data("userOptions").callback.call(this);
}
}
} else {
if(eventDate <= duration) {
clearInterval($(el).data("jC_interval"));
if(thisEl.data("userOptions").callback) {
thisEl.data("userOptions").callback.call(this);
}
}
}
//if customRange is used, update the seconds variable
var seconds = (customRange) ? duration : eventDate - duration;
var thisInstanceFormat = thisEl.data("userOptions").format;
//calculate seconds into days,hours,minutes,seconds
//if dd (days) is specified in the format setting (i.e. format: 'dd:hh:mm:ss')
if(thisInstanceFormat.indexOf('dd') != -1) {
var days = Math.floor(seconds / (60 * 60 * 24)); //calculate the number of days
seconds -= days * 60 * 60 * 24; //update the seconds variable with no. of days removed
}
//if hh (hours) is specified
if(thisInstanceFormat.indexOf('hh') != -1) {
var hours = Math.floor(seconds / (60 * 60));
seconds -= hours * 60 * 60; //update the seconds variable with no. of hours removed
}
//if mm (minutes) is specified
if(thisInstanceFormat.indexOf('mm') != -1) {
var minutes = Math.floor(seconds / 60);
seconds -= minutes * 60; //update the seconds variable with no. of minutes removed
}
//if ss (seconds) is specified
if(thisInstanceFormat.indexOf('ss') == -1) {
seconds -= seconds; //if ss is unspecified in format, update the seconds variable to 0;
}
//conditional Ss
//updates the plural and singular labels accordingly
if (days == 1) { $(el).find(".textDays").text(singularLabels[0]); } else { $(el).find(".textDays").text(pluralLabels[0]); }
if (hours == 1) { $(el).find(".textHours").text(singularLabels[1]); } else { $(el).find(".textHours").text(pluralLabels[1]); }
if (minutes == 1) { $(el).find(".textMinutes").text(singularLabels[2]); } else { $(el).find(".textMinutes").text(pluralLabels[2]); }
if (seconds == 1) { $(el).find(".textSeconds").text(singularLabels[3]); } else { $(el).find(".textSeconds").text(pluralLabels[3]); }
//twoDigits ON setting
//if the twoDigits setting is set to ON, jCounter will always diplay a minimum number of 2 digits
if(thisEl.data("userOptions").twoDigits == 'on') {
days = (String(days).length >= 2) ? days : "0" + days;
hours = (String(hours).length >= 2) ? hours : "0" + hours;
minutes = (String(minutes).length >= 2) ? minutes : "0" + minutes;
seconds = (String(seconds).length >= 2) ? seconds : "0" + seconds;
}
//updates the jCounter's html values
if(!isNaN(eventDate)) {
$(el).find(".days").text(days);
$(el).find(".hours").text(hours);
$(el).find(".minutes").text(minutes);
$(el).find(".seconds").text(seconds);
if(thisEl.data("jCanimation") == 'slide') {
$(el).find(".daysSlider u:eq(1)").text(days);
$(el).find(".hoursSlider u:eq(1)").text(hours);
$(el).find(".minutesSlider u:eq(1)").text(minutes);
$(el).find(".secondsSlider u:eq(1)").text(seconds);
jCslider(el,'.secondsSlider',seconds,eventDate,duration);
if(parseInt(seconds,10) == 59) {
jCslider(el,'.minutesSlider',minutes,eventDate,duration)
if(parseInt(minutes,10) == 59) {
jCslider(el,'.hoursSlider',hours,eventDate,duration)
if(parseInt(hours,10) == 23) {
jCslider(el,'.daysSlider',days,eventDate,duration)
}
}
}
}
} else {
if(consoleLog) { console.log("(jC) Error: Invalid date! Here's an example: 01 January 1970 12:00:00"); }
clearInterval($(el).data("jC_interval"));
}
//stores the remaining time when pausing jCounter
$(el).data("jC_pausedTime", eventDate-duration);
}
//method calling logic
if ( jC_methods[this.options] ) {
return jC_methods[ this.options ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof this.options === 'object' || ! this.options ) {
return jC_methods.init.apply( this, arguments );
} else {
console.log('(jC) Error: Method >>> ' + this.options + ' <<< does not exist.' );
}
}
//the end;
}) (jQuery,document,window);
I am trying to make timer in javascript using a prototype. Each time a new timer is created, a object of prototype is created. There are methods to increase time and print each second. The whole code snippet is as follows:
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.prototype.start = function() {
var self = this;
if (!self.interval) {
self.interval = setInterval(update, 1000);
}
function update() {
incrementTime();
render();
}
function render() {
self.elem.innerText = getPrintableTime();
}
function incrementTime() {
self.currentTime["min"] += Math.floor((++self.currentTime["sec"]) / 60);
self.currentTime["hr"] += Math.floor(self.currentTime["min"] / 60);
self.currentTime["sec"] = self.currentTime["sec"] % 60;
self.currentTime["min"] = self.currentTime["min"] % 60;
}
function getPrintableTime() {
var text = getTwoDigitNumber(self.currentTime["hr"]) + ":" + getTwoDigitNumber(self.currentTime["min"]) + ":" + getTwoDigitNumber(self.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
};
module.exports = Timer;
I have all methods in start function. The problem is that for each new object of Timer, new space for each method will be used which is very inefficient. But when I try to put methods outside of start function, they lose access to self variable. You can see that there is setInterval function used which will be calling these methods per second. I cannot use this also as this will be instance of Window in subsequent calls.
How can I solve this situation by only keeping one instance of all the interior methods?
You don't need to have all methods in the start function. Yes, for each new Timer instance, new space for each function will be used, but that is necessary when you want to work with setInterval as you need a function which closes over the instance. However, you need only one such closure, the other methods can be standard prototype methods.
function getTwoDigitNumber(number) {
return (number > 9 ? "" : "0") + number;
}
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.prototype.start = function() {
var self = this;
if (!this.interval) {
this.interval = setInterval(function update() {
self.incrementTime();
self.render();
}, 1000);
}
};
Timer.prototype.render() {
this.elem.innerText = this.getPrintableTime();
};
Timer.prototype.incrementTime = function() {
this.currentTime.sec += 1;
this.currentTime.min += Math.floor(this.currentTime.sec / 60);
this.currentTime.hr += Math.floor(this.currentTime.min / 60);
this.currentTime.sec = this.currentTime.sec % 60;
this.currentTime.min = this.currentTime.min % 60;
};
Timer.prototype.getPrintableTime = function() {
var text = getTwoDigitNumber(this.currentTime.hr) + ":"
+ getTwoDigitNumber(this.currentTime.min) + ":"
+ getTwoDigitNumber(self.currentTime.sec);
return text;
};
module.exports = Timer;
Btw, regarding your incrementTime pattern, you should have a look at How to create an accurate timer in javascript?.
You can use apply to use functions defined outside of prototype with correct this context.
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
function update() {
incrementTime.apply(this);
render.apply(this);
}
function render() {
this.elem.innerText = getPrintableTime.apply(this);
}
function incrementTime() {
this.currentTime["min"] += Math.floor((++this.currentTime["sec"]) / 60);
this.currentTime["hr"] += Math.floor(this.currentTime["min"] / 60);
this.currentTime["sec"] = this.currentTime["sec"] % 60;
this.currentTime["min"] = this.currentTime["min"] % 60;
}
function getPrintableTime() {
var text = getTwoDigitNumber(this.currentTime["hr"]) + ":" + getTwoDigitNumber(this.currentTime["min"]) + ":" + getTwoDigitNumber(this.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
Timer.prototype.start = function() {
var self = this;
if (!self.interval) {
self.interval = setInterval(function() {
update.apply(self);
}, 1000);
}
};
document.addEventListener('DOMContentLoaded', function() {
var timer = new Timer(document.getElementById('timer'));
timer.start();
}, false);
<div id="timer"></div>
If I understand correctly, you're wanting to only create one interval.
One possible solution would be to create a static method and variable to manage the setInterval. I would note that while this may be more performance friendly, the timers will always start and run on the same count...not from the moment each timer is created. (See example)
Of course, you could capture the current timestamp and calculate the elapsed time from there. But, that's another thread ;)
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.subscribe = function(timer) {
Timer.subscribers = Timer.subscribers || [];
if (Timer.subscribers.indexOf(timer) === -1) {
Timer.subscribers.push(timer);
timer.update.call(timer);
}
Timer.checkInterval();
};
Timer.unsubscribe = function(timer) {
Timer.subscribers = Timer.subscribers || [];
if (Timer.subscribers.indexOf(timer) !== -1) {
Timer.subscribers.splice(Timer.subscribers.indexOf(timer), 1);
}
Timer.checkInterval();
};
Timer.checkInterval = function() {
if (!Timer.interval && Timer.subscribers.length > 0) {
Timer.interval = setInterval(function() {
Timer.subscribers.forEach(function(item) {
item.update.call(item);
});
}, 1000);
} else if (Timer.interval && Timer.subscribers.length === 0) {
clearInterval(Timer.interval);
Timer.interval = null;
}
};
Timer.prototype = {
start: function() {
Timer.subscribe(this);
},
stop: function() {
Timer.unsubscribe(this);
},
update: function() {
this.incrementTime();
this.render();
},
incrementTime: function() {
this.currentTime["min"] += Math.floor((++this.currentTime["sec"]) / 60);
this.currentTime["hr"] += Math.floor(this.currentTime["min"] / 60);
this.currentTime["sec"] = this.currentTime["sec"] % 60;
this.currentTime["min"] = this.currentTime["min"] % 60;
},
render: function() {
var self = this;
function getPrintableTime() {
var text = getTwoDigitNumber(self.currentTime["hr"]) + ":" + getTwoDigitNumber(self.currentTime["min"]) + ":" + getTwoDigitNumber(self.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
this.elem.innerText = getPrintableTime();
}
};
/**
*
*/
var timers = document.getElementById('timers');
function addTimer() {
var el = document.createElement('div');
var tmr = document.createElement('span');
var btn = document.createElement('button');
var t = new Timer(tmr);
btn.innerText = 'Stop';
btn.onclick = function() {
t.stop();
};
el.appendChild(tmr);
el.appendChild(btn);
timers.appendChild(el);
t.start();
};
<div id="timers"></div>
<button onclick="addTimer()">Add Timer</button>
I am a JavaScript newbie. I am using a template which has the code below. It shows a countdown timer. I can't understand why it does not work. I have provided it the value of 90 days. Please guide. Thanks
// Generated by CoffeeScript 1.4.0
/*
countdown is a simple jquery plugin for countdowns
Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
and GPL-3.0 (http://opensource.org/licenses/GPL-3.0) licenses.
#source: http://github.com/rendro/countdown/
#autor: Robert Fleischmann
#version: 1.0.1
*/
(function () {
(function ($) {
$.countdown = function (el, options) {
var getDateData,
_this = this;
this.el = el;
this.$el = $(el);
this.$el.data("countdown", this);
this.init = function () {
_this.options = $.extend({}, $.countdown.defaultOptions, options);
if (_this.options.refresh) {
_this.interval = setInterval(function () {
return _this.render();
}, _this.options.refresh);
}
_this.render();
return _this;
};
getDateData = function (endDate) {
var dateData, diff;
endDate = Date.parse($.isPlainObject(_this.options.date) ? _this.options.date : new Date(_this.options.date));
diff = (endDate - Date.parse(new Date)) / 1000;
if (diff <= 0) {
diff = 0;
if (_this.interval) {
_this.stop();
}
_this.options.onEnd.apply(_this);
}
dateData = {
years: 90,
days: 90,
hours: 0,
min: 0,
sec: 90,
millisec: 0
};
if (diff >= (365.25 * 86400)) {
dateData.years = Math.floor(diff / (365.25 * 86400));
diff -= dateData.years * 365.25 * 86400;
}
if (diff >= 86400) {
dateData.days = Math.floor(diff / 86400);
diff -= dateData.days * 86400;
}
if (diff >= 3600) {
dateData.hours = Math.floor(diff / 3600);
diff -= dateData.hours * 3600;
}
if (diff >= 60) {
dateData.min = Math.floor(diff / 60);
diff -= dateData.min * 60;
}
dateData.sec = diff;
return dateData;
};
this.leadingZeros = function (num, length) {
if (length == null) {
length = 2;
}
num = String(num);
while (num.length < length) {
num = "0" + num;
}
return num;
};
this.update = function (newDate) {
_this.options.date = newDate;
return _this;
};
this.render = function () {
_this.options.render.apply(_this, [getDateData(_this.options.date)]);
return _this;
};
this.stop = function () {
if (_this.interval) {
clearInterval(_this.interval);
}
_this.interval = null;
return _this;
};
this.start = function (refresh) {
if (refresh == null) {
refresh = _this.options.refresh || $.countdown.defaultOptions.refresh;
}
if (_this.interval) {
clearInterval(_this.interval);
}
_this.render();
_this.options.refresh = refresh;
_this.interval = setInterval(function () {
return _this.render();
}, _this.options.refresh);
return _this;
};
return this.init();
};
$.countdown.defaultOptions = {
date: "June 7, 2087 15:03:25",
refresh: 1000,
onEnd: $.noop,
render: function (date) {
return $(this.el).html("" + date.years + " years, " + date.days + " days, " + (this.leadingZeros(date.hours)) + " hours, " + (this.leadingZeros(date.min)) + " min and " + (this.leadingZeros(date.sec)) + " sec");
}
};
$.fn.countdown = function (options) {
return $.each(this, function (i, el) {
var $el;
$el = $(el);
if (!$el.data('countdown')) {
return $el.data('countdown', new $.countdown(el, options));
}
});
};
return void 0;
})(jQuery);
}).call(this);
Just change
endDate = Date.parse($.isPlainObject(_this.options.date)
? _this.options.date : new Date(_this.options.date));
to
endDate = Date.parse($.isPlainObject(_this.options.date)
? _this.options.date : new Date(year,month,date));
example:
endDate = Date.parse($.isPlainObject(_this.options.date)
? _this.options.date : new Date(2017,02,15));