I'm working on my first jQuery plugin which is a simple countdown timer with an option to set the target date. The goal is to get a plain text clock counting down to the provided target date & time. For the life of me I can't figure out how to use setTimeout or setInverval within the plugin so it actually counts down. Spent all day digging through stackoverflow and other sources but couldn't find a solution, so apologies if I'm just not getting it.
Here's what I've got:
(function( $ ){
$.fn.clock = function( options ) {
// ESTABLISH DEFAULTS
var settings = $.extend( {
'target' : '07/21/2013 09:00:00', // mm/dd/yyyy hh:mm:ss
}, options);
return this.each(function() {
// calculate milliseconds to target
tarDate = new Date(settings.target);
dateNow = new Date();
amount = tarDate.getTime() - dateNow.getTime();
delete dateNow;
// set label
label = settings.title;
// generate output
if(amount <= 0) {
out = '000:00:00:00';
} else {
td = 0; th = 0; tm = 0; ts = 0; out = ''; // reset everything
amount = Math.floor(amount/1000); // kill the milliseconds
td = Math.floor(amount/86400); // calculate days to target
amount = amount%86400; // convert amount to days
th = Math.floor(amount/3600); // calculate hours to target
amount = amount%3600; // convert amount to hours
tm = Math.floor(amount/60); // calculate minutes to target
amount = amount%60; // convert amount to minutes
ts = Math.floor(amount)+1; // calculate seconds to target
out += (td<=99?'0':'') + (td<=9?'0':'') + td + ':';
out += (th<=9?'0':'') + th + ':';
out += (tm<=9?'0':'') + tm + ':';
out += (ts<=9?'0':'') + ts;
}
// assemble and pump out to dom
$(this).html(out);
// set refresh rate
??????
});
};
})( jQuery );
Check out this link for an interesting pattern for plugin authoring. Basically what you need to do is provide a "method" for updating your clock:
(function( $ ){
function updateClock(element) {
var settings = element.data("settings");
// Your update logic goes here
}
$.fn.clock = function( options ) {
if ( options === 'update' )
return updateClock(this);
// Other methods, if any
else if ( typeof options !== 'object' )
throw 'Unknown method';
// ESTABLISH DEFAULTS
var settings = $.extend( {
'target' : '07/21/2013 09:00:00', // mm/dd/yyyy hh:mm:ss
}, options);
// Save your settings for later access
this.data("settings",settings);
Then, every time you want to update an element, you call it this way:
$(yourselector).clock("update");
(from outside; if updateClock is accessible from your scope, you can call it directly for efficiency. Just remember to wrap the element in $() if necessary)
Lastly, you have to configure setTimeout or setInterval. I would prefer setTimeout, because that will allow you to stop or restart your clock if necessary. Add this to the end of your updateClock, maybe preceeded by a check:
setTimeout(function() { updateClock(element); }, settings.refreshRate);
i believe you have to have setTimeout inside the function that needs to be called and have setTimeout call the function its in. then have a condition setup so once it reaches zero clearTimeout will go off http://jsfiddle.net/qUYQN/
Related
I am trying to create a Countup counter Starting from 1 to 10000 and i do not want it to reset when user refreshes the page or cancels the page. The Counter should start from 1 for every user that visits the page and keep running in background till it gets to 10000 even if the page is closed.
I have written the page below which;
Starts from the specified number for every new visitor
Saves the progress and does not reset when page is refreshed, however
It does not keep counting when page is closed and starts from the last progress when user closes the tab and comes back later. My code is
function countUp() {
var countEl = document.querySelector('.counter');
var countBar = document.querySelector('.progress-bar');
var x = parseInt(localStorage.getItem('lastCount')) - 1 || 1;
var y = countEl.dataset.to;
var z = countBar.dataset.to;
function addNum() {
countEl.innerHTML = x;
x += 1;
if (x > y && x > z) {
clearInterval(timer);
}
localStorage.setItem('lastCount', x);
}
var timer = window.setInterval(addNum, 1000);
localStorage.setItem("addNum", counter);
toggleBtn.addEventListener('click', function(){
countUp();
toggleBtn.classList.add('hidden');
});
}
countUp();</script>
<body onload=countUp();>
<div class="counter" data-from="0" data-to="10000000"></div>
<div class="progress-bar" data-from="0" data-to="10000000"></div>
</body>
It's difficult to show an example on StackOverflow because it doesn't let you fiddle with localStorage but, it sounds like you want something like:
When a user visits the page check localStorage for a timestamp.
If timestamp exists, go to step 4
Timestamp doesn't exist so get the current timestamp and stash it in localStorage.
Get the current timestamp. Subtract the timestamp from before. If over 10,000, stop, you're done.
Display difference calculated in step 4.
Start a 1 second timer, when time is up, go to step 4.
Something along those lines should work even if they refresh the page and since you are calculating from the original timestamp it will "count" in the background even if the page is closed.
window.addEventListener("DOMContentLoaded", () => {
const start = localStorage.getItem("timestamp") || Date.now();
localStorage.setItem("timestamp", start);
function tick() {
const now = Date.now();
const seconds = Math.floor((now - start) / 1000);
const display = document.getElementById("display");
if (seconds > 10000) return display.innerHTML = "We're done";
display.innerHTML = seconds;
setTimeout(tick, 1000);
}
tick();
});
<div id="display"></div>
So, client-side code can't normally execute when a client-side javascript page is closed.
What you could do, however, is calculate where the timer should be then next time it is loaded.
For example, in your addNum() function, you could in addition to the last count, also store the current date (and time).
function addNum() {
countEl.innerHTML = x;
x += 1;
if (x > y && x > z) {
clearInterval(timer);
}
localStorage.setItem('lastCount', x);
localStorage.setItem('lastDate', new Date());
}
Then, when your code starts, you can retrieve lastDate, and then subtract the current Date() from it.
Then use that to add the difference to your counter.
function countUp() {
let storedCount = parseInt(localStorage.getItem('lastCount'));
let storedDate = Date.parse(localStorage.getItem('lastDate'));
let now = new Date()
let diffSeconds = (now.getTime() - storedDate.getTime()) / 1000;
let storedCount += diffSeconds;
var countEl = document.querySelector('.counter');
var countBar = document.querySelector('.progress-bar');
var x = storedCount - 1 || 1;
var y = countEl.dataset.to;
var z = countBar.dataset.to;
}
I'm sure there are some more changes required to make it work with your code, but the idea is to store the current time so that when the page is closed and reopened, you can 'adjust' the count to catch up to what it should be.
What you want here is not possible just from the client-side code, there is no way for 2 different machines to share that information at all.
Here's the thing though, you can do this with a backend where the database gets updated every time a new IP hits the server. Note with this approach, a user here is one system and not different browsers or sessions.
To update this real-time for someone who is already on the website, run a timer and call an API that specifically gives you the count. Therefore the page gets updated frequently. You can also do this with react-query as it comes with inbuilt functions to do all this.
Is there any way I can make full calendar to automatically proceed to next month if there is no event coming in a particular month.
Say there is no Event in June or the event is already finished, the calendar will proceed to July without click the next month button.
I never used full calendar (or it was years ago) so I may be a bit wrong, but I flew through their docs and this is what I found (btw: their api is rather a weak part of this plugin, speed and efficiency as well but it's your choice).
There is an event in docs that triggers every time the view change, so although the view will be rendered, skipping will be probably not visible. First of all you will have to implement your own methods to move across calendar, because you have to know if you are going forward or backward (example). So according to this you can try it as such: https://codepen.io/prowseed/pen/bKQEov?editors=0010
var lastOperationFlag;
$('#calendar').fullCalendar({
events: [...],
header: false, // don't display the default header
viewRender: function (view, element) {
var start = view.start._i; // timestamp of the beginning of current view
var end = view.end._i; // timestamp of the end of current view
var events = view.options.events; // all events
if( checkBoundariesAndCurrentRange(start, end, events) ){
(lastOperationFlag === 'next') ? $('#calendar').fullCalendar('next') : $('#calendar').fullCalendar('prev');
}
}
});
$('#prev').on('click', function() {
lastOperationFlag = 'prev';
$('#calendar').fullCalendar('prev'); // request previous view
});
$('#next').on('click', function() {
lastOperationFlag = 'next';
$('#calendar').fullCalendar('next'); // request next view
});
function checkBoundariesAndCurrentRange (start, end, events) {
if (events.length === 0) return false;
var smallestDate = null;
var biggestDate = null;
var eventsInRange = false;
events.forEach(e => {
var ev_start = (new Date(e.start)).getTime(); // !!!!! MORE SAFE to transform it with a momentjs
// here you can eventually also handle event.end if exists with additional logic
if (smallestDate == null || ev_start < smallestDate) smallestDate = ev_start;
if (biggestDate == null || ev_start > biggestDate) biggestDate = ev_start;
if (start <= ev_start && end >= ev_start) eventsInRange = true; // set the flag if there is at least one event in given start-end range
});
console.log((!eventsInRange && smallestDate < start && biggestDate > end) )
return (!eventsInRange && smallestDate < start && biggestDate > end)
}
I'm using NodeJs.
I received constantly request from server.
I'm added some variable like createdTime to it and saved to the database.
when I sorted data by createdTime in some case It is not reliable, It is Repeated
How can I make differentiate between them ?
I do not want to count request.
I do not like to change timestamp's format.
var createdTime = new Date().getTime();
Here's a method of combining a counter with the current time to allow you to have as many as 1000 separate transactions within the same ms that are all uniquely numbered, but still a time-based value.
And, here's a working snippet to illustrate:
// this guarantees a unique time-based id
// as long as you don't have more than 1000
// requests in the same ms
var getTransactionID = (function() {
var lastTime, counter = 0;
return function() {
var now = Date.now();
if (now !== lastTime) {
lastTime = now;
counter = 0;
} else {
++counter;
}
return (now * 1000) + counter;
}
})();
for (var i = 0; i < 100; i++) {
document.write(getTransactionID() + "<br>");
}
If you want something that is likely to work across clusters, you can use process.hrtime() to use the high resolution timer instead of the counter and then make the id be a string that could be parsed into a relative time if needed. Since this requires node.js, I can't make a working snippet here in the browser, but here's the idea:
// this makes a unique time-based id
function getTransactionID () {
var now = Date.now();
var hrtime = process.hrtime();
return now + "." + ((hrtime[0] * 1e9) + hrtime[1]);
}
Due to my low rep I can't add a comment but it looks like you are needing to go beyond milliseconds.Maybe this stackoverflow question can help you
How to get a microtime in Node.js?
I'm trying to make a countdown timer in JS that will change the value of a field every one minute (in the begining there is 20, and then change it to 19,18,17 etc), but it's not working correctly. It's changing value not every 60sec but I have a feel that it works random (sometimes it change value first time after 15 sec, another time it's 53). Any idea what I'm doing wrong? Here is the code:
function getTimeNow(){
var Time = new Date;
return Time.getHours()*60*60+Time.getMinutes()*60 + Time.getSeconds();
}
var start = getTimeNow();
var start_point = start%60;
var target = start+60*20;
function TimeOut(){
if((getTimeNow()-start)%60 == start_point && target>getTimeNow()){
var temp = jQuery('.Timer').html();
temp-=1;
jQuery('.Timer').html(temp);
}
setTimeout(TimeOut,1000);
}
You cannot count on the exact moment a timer function will be called. You need to change your logic to something more resilient to time shifts...
setInterval(function(){count.innerText = count.innerText - 1;},
60*1000);
this is also a lot shorter...
I'm looking for a way to manipulate animation without using libraries
and as usual I make a setTimeout in another setTimout in order to smooth the UI
but I want to make a more accurate function to do it, so if I want to make a 50ms-per-piece
animation, and I type:
............
sum=0,
copy=(new Date()).getMilliseconds()
function change(){
var curTime=(new Date()).getMilliseconds(),
diff=(1000+(curTime-copy))%1000 //caculate the time between each setTimeout
console.log("diff time spam: ",diff)
sum+=diff
copy=curTime
var cur=parseInt(p.style.width)
if (sum<47){//ignore small error
//if time sum is less than 47,since we want a 50ms-per animation
// we wait to count the sum to more than the number
console.log("still wating: ",sum)
}
else{
//here the sum is bigger what we want,so make the UI change
console.log("------------runing: ",sum)
sum=0 //reset the sum to caculate the next diff
if(cur < 100)
{
p.style.width=++cur+"px"
}
else{
clearInterval(temp)
}
}
}
var temp=setInterval(change,10)
I don't know the core thought of my code is right,anyone get some ideas about how to make a more accurate timer in most browser?
Set the JsFiddle url:
http://jsfiddle.net/lanston/Vzdau/1/
Looks too complicated to me, use setInterval and one start date, like:
var start = +new Date();
var frame = -1;
var timer = setInterval(checkIfNewFrame, 20);
function checkIfNewFrame () {
var diff = +new Date() - start;
var f = Math.floor(diff / 50);
if (f > frame) {
// use one of these, depending on whether skip or animate lost frames
++frame; // in case you do not skip
frame = f; // in case you do skip
moveAnimation();
}
}
function moveAnimation () {
... do whatever you want, there is new frame, clear timer past last one
}