A repeating countdown timer that continues even after browser refresh - javascript

Firstly I am very new to javascript. I have a Shopify store and I am planning to have a countdown timer just like Amazon to each of my product pages. I know there are a lot of plugins I can use for Shopify but none of them matches with the theme and style of my Shopify store so I planned to make one myself.
What I want is if User-1 opens my website and navigates to a product page, he should see a timer counting down to a specific time say 12:00:00 (hh:mm:ss). Suppose User-1 sees 'Deal ends in 11:20:10' Now if User-2 opens the same product page at the same time then he should also see 'Deal ends in 11:20:10' The whole point is the timer should not refresh back to 12:00:00 every time the browser loads/reloads the page and every user on the website should see the same time remaining on the countdown timer.
I starting with a bit of research and managed to run a timer on my store. It's not exactly what I want but here's the code:
var interval;
var minutes = 1;
var seconds = 5;
window.onload = function() {
countdown('countdown');
}
function countdown(element) {
interval = setInterval(function() {
var el = document.getElementById(element);
if(seconds == 0) {
if(minutes == 0) {
el.innerHTML = "countdown's over!";
clearInterval(interval);
return;
} else {
minutes--;
seconds = 60;
}
}
if(minutes > 0) {
var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
} else {
var minute_text = '';
}
var second_text = seconds > 1 ? 'seconds' : 'second';
el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
seconds--;
}, 1000);
}
</script>
And here's what it looks like:
It does work but has the following problems:
It refreshes with the browser refresh.
It doesn't have hours.
It does not auto-repeat when the timer reaches zero.
For every user time remaining varies.
As I mentioned I am almost a noob in JavaScript. Can anyone help me build a countdown timer that overcomes the above issues?

Explanation
Problem 1: It refreshes with the browser refresh.
Cause: Because you hardcoded the seconds and minutes (var seconds = 5, var minutes = 1), every user that visits the page will see the timer counting down from the exact same value being "1 minute and 5 seconds remaining" again and again.
Solution: Instead of hardcoding the start value of the countdown, hardcode the deadline. So instead of saying to each visitor of your page "Start counting down from 1 min and 5 sec to 0!", you have to say something like "Count down from whatever time it is now to midnight!". This way, the countdown will always be synced across different users of your website (assuming that their computer clock is set correctly).
Problem 2: It doesn't have hours.
Cause: Your code has only variables to keep track of seconds and minutes, there is no code written to keep track of the hours.
Solution: Like proposed in the solution for problem 1: don't keep track of the hours/minutes/seconds remaining, but only keep track of the deadline and then calculate hours/minutes/seconds remaining based on the current client time.
Problem 3: It does not auto-repeat when the timer reaches zero.
Cause: The nested ifs (that check seconds == 0 and m == 0) in your code explicitly state to display the text "countdown's over!" when the countdown is over.
Solution: Keep a conditional that checks when the countdown is over but instead of displaying "countdown's over!", reset the deadline to a next deadline.
Problem 4: For every user time remaining varies.
Cause: See Problem 1
Solution: See Problem 1
Sample code
here is a piece of code that integrates the solutions mentioned above:
const span = document.getElementById('countdown')
const deadline = new Date
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)
function displayRemainingTime() {
if (deadline < new Date) deadline.setDate(deadline.getDate() + 1)
const remainingTime = deadline - new Date
const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
const seconds = extract( 60000, 1000 )
const minutes = extract( 3600000, 60000 )
const hours = extract(86400000, 3600000)
const string = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
window.setInterval(displayRemainingTime, 1000)
displayRemainingTime()
<h3>
<span id="countdown"></span>
</h3>
Edit: make sure the client time is correct
If you don't trust your clients for having their time set up correctly. You can also send a correct timestamp from the server that serves your page. Since I don't know what kind of server you are using I can't give an example for your server code. But basically you need to replace the code (from the following example) after const trustedTimestamp = (being (new Date).getTime()) with a correct timestamp that you generate on the server. For the correct formatting of this timestamp refer to Date.prototype.getTime()
const span = document.getElementById('countdown')
const trustedTimestamp = (new Date).getTime()
let timeDrift = trustedTimestamp - (new Date)
const now = () => new Date(timeDrift + (new Date).getTime())
const deadline = now()
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)
function displayRemainingTime() {
if (deadline < now()) deadline.setDate(deadline.getDate() + 1)
const remainingTime = deadline - now()
const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
const seconds = extract( 60000, 1000 )
const minutes = extract( 3600000, 60000 )
const hours = extract(86400000, 3600000)
span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
window.setInterval(displayRemainingTime, 1000)
displayRemainingTime()
<h3>
<span id="countdown"></span>
</h3>
Time from google server
To create a working example I added this experiment where I get the correct time from a Google page. Do not use this code on your website because it is not guaranteed that google will keep hosting this web-page forever.
const span = document.getElementById('countdown')
const trustedTimestamp = (new Date).getTime()
let timeDrift = 0
const now = () => new Date(timeDrift + (new Date).getTime())
const deadline = now()
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)
window.setInterval(displayRemainingTime, 1000)
window.setInterval(syncClock, 3000)
displayRemainingTime()
syncClock()
function displayRemainingTime() {
if (deadline < now()) deadline.setDate(deadline.getDate() + 1)
const remainingTime = deadline - now()
const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
const seconds = extract( 60000, 1000 )
const minutes = extract( 3600000, 60000 )
const hours = extract(86400000, 3600000)
span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
function syncClock() {
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", "http://www.googleapis.com",true);
xmlhttp.onreadystatechange = function() {
const referenceTime = new Date
if (xmlhttp.readyState==4) {
const correctTime = new Date(xmlhttp.getResponseHeader("Date"))
timeDrift = correctTime - referenceTime
}
}
xmlhttp.send(null);
}
<h3>
<span id="countdown"></span>
</h3>

I am not sure how Shopify handles plugins, but there must be some way to edit CSS for them, so you could have them match the style of your theme.
What you've done here, in the JS you've provided, will always have that behavior because it is client-side code. The script will run after each refresh, and will always refresh the values minutes and seconds to the associated initial values namely 1 and 5. I believe this answers your first question.
As per your second question, you clearly have not coded hours in that JS script. So hours should not appear, it would be a mystery if they did!
With respect to your third question, you only call countdown once during the onload function.
And by the first answer, it is natural that the clock should not be synchronized among users since they would logically be refreshing your page at different times.
So I think your best bet is to use one of the plugins and just modify the CSS.

Related

How do I set a timestamp that updates daily on a website, with bootstrap and javascript, which could be useful for reviews and blogs etc.?

I'm interested in setting up a timer/date stamp on my website, which uses Bootstrap, using JavaScript or whatever is necessary. Basically, when you see a review on somewhere like Google, or a blog post, underneath the photo of the person who made the post and their name, there's always a small bit of text that says how long ago it was written, such as '3 hours ago' or '1 month ago'. See the image below.
How would I get something like this set up on an HTML/CSS/Bootstrap website, so that it automatically updates every day? For example, I would want it to say '1 hour ago' an hour after it was posted and '1 month ago' when a month has passed, automatically.
A simple but inefficient way to do such a thing involves two parts:
Make a function that takes two timestamps and creates a string that says 'X time units ago`. You can find several libraries that do that easily.
Every once in a while (with setInterval every 30 seconds, for example), call that function with the arguments Date.now() and websiteTimestamp (assuming that's how you'd name your website's timestamp) and put the result in the appropriate view.
It would do lots of unnecessary overwrites, but isn't it simple? I mean it should be OK to show how old your page is, but probably bad to do in a chat app to show how old every chat message is.
I just came across something like this from a previous post. Is this likely to do what I am looking for, updating automatically?
Also, what is the format of the 'date' input to the function (i.e. is it milliseconds, seconds etc. or a proper date format, such as YYYY-MM-DD)?
function timeSince(date) {
var seconds = Math.floor((new Date() - date) / 1000);
var interval = Math.floor(seconds / 31536000);
if (interval > 1) {
return interval + " years";
}
interval = Math.floor(seconds / 2592000);
if (interval > 1) {
return interval + " months";
}
interval = Math.floor(seconds / 86400);
if (interval > 1) {
return interval + " days";
}
interval = Math.floor(seconds / 3600);
if (interval > 1) {
return interval + " hours";
}
interval = Math.floor(seconds / 60);
if (interval > 1) {
return interval + " minutes";
}
return Math.floor(seconds) + " seconds";
}
var aDay = 24*60*60*1000
console.log(timeSince(new Date(Date.now()-aDay)));
console.log(timeSince(new Date(Date.now()-aDay*2)));

JavaScript redirect to another page when time reaches 5 minutes to top of hour

I want to redirect a user to another page when the time reaches 5 minutes to the top of the hour.
In 24 hour time that means I want the redirect to run on intervals like this...
11:55
12:55
13:55
14:55
15:55
etc
So far all I can figure out is the "count down" style of JS redirection like this, but instead of that I need something that runs based on time (5 minutes to the top of the hour) not a count down.
setTimeout("location.href = 'https://www.google.com';",1000);
I have also tried this but nothing happened.
var mins = new Date().getMinutes();
if (mins == 55) {
window.location.href = "https://www.google.com";
}
Is this possible with JavaScript (or jQuery)?
You're close - you just need to calculate the time remaining from when the user loads the page. I would throw this in the document.ready callback if you're already using jQuery.
const interval = 55*60*1000 // 55 min in ms
const msUntilNext = interval - new Date().getTime() % interval;
const timeout = setTimeout(() => {
location.href = 'https://www.google.com';
}, msUntilNext)
setTimeout() happens after a specified amount of time, where you have it defined to fire after 1 second (1000 milliseconds = 1 second) and then it will stop, unless you trigger the timeout again.
One solution is to check the timeout every 1 second using setInterval, and as soon as the minutes reach 55 minutes, do the redirect. You could also change this to trigger less frequently, say every 15 or 30 seconds (15000, 30000).
// function that triggers at the interval
function checkTimeout() {
var now = new Date();
var minutes = now.getMinutes();
console.log(minutes);
if (minutes >= 55) {
location.href = 'https://www.google.com';
}
}
setInterval(checkTimeout, 1000); // check interval every 1 second

Updating time without refreshing the page with Javascript

I'm building an app that displays the current time without have to refresh.
I'm calling the time below, but when I display it I have to refresh the page to update the time. How do I continuously update the time?
function GetTime(){
var today = new Date();
var hour = today.getHours();
var minute = today.getMinutes();
if (hour>=12){ //Adding endings
suffix = "P.M.";}
else{
suffix = "A.M.";}
minute = addZero(minute); //Call addZero function
hour = removeMilitary(hour); //Call removeMilitary Function
var fullTime = hour + ":" + minute + " " + suffix; //Combine hour minute and the suffix
function addZero(number){
if (number<10){
number = "0" + number;
}
return number;
}
function removeMilitary(hour){ //This function can be removed if desired by user.
if (hour > 0 && hour <= 12) {
hour = "" + hour;
} else if (hour > 12) {
hour = "" + (hour - 12);
} else if (hour == 0) {
hour= "12";
}
return hour;
}
return fullTime;
}
Javascript has a setInterval method.
You can get the time every second, by running your program every 1000 milliseconds:
setInterval(GetTime, 1000);
//if you incorporate updating the html
//within the getTime function
or
setInterval(function(){
document.getElementById("ID_of_the_time_element").innerHTML= getTime();
//do something else
}, 1000}
Why setInterval at 1000 ms isn't very accurate
The problem with that is unless you start the set interval exactly on the second, your program will not change the second exactly on the second. I would recommend you get it more accurate by adjusting the interval to every 100 ms, for example. Then it would update every 100ms, which means that the most your clock will be behind is a tenth of a second.
A better solution
Setting an interval every 100ms is ok, but if you want more accuracy, setting it to 10ms isn't necessarily the best option, because repeating a task every 10ms is a pretty large burden on the computer. You could also use find the number of ms until the next second, and then use the setTimeout method to wait until the next second arrives and start the set timeout then. You would still have some computational delay(the amount of time between when it gets the number of milliseconds until the next second and it starts the setTimeout), but that's probably a lot less than 100 ms.
I assume you will use the setInterval() function.
just add this to your button onclick;
setInterval(function(){Document.getElementById("div").innerHTML = getTime},1000);
setInterval (GetTime, 1000) will call your function every second (1000 milliseconds) to get the time. You don't show how you're putting the time on the page, so you'll need to incorporate that as well.
UPDATE I forgot the setInterval but someone else already pointed at it, so mix both answers and you're done!
Look at getElementById and innerHTML.
Something like the following snippet at the end of your function (instead of return fulltime ) should work:
...
var myElement = documentGetElementById('mydiv');
myElement.innerHTML = fulltime;
...
Of course you need to define some HTML element with <div id="mydiv"></div> or whatever you choose.

Define time as hours

Is it possible to define my time to countdown as hours now I have "December 25, 2014 00:01:00" but could I have something like this "14:00" and it runs every day. This is the code I have.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function cdtd() {
var xmas = new Date("December 25, 2014 00:01:00");
var now = new Date();
var timeDiff = xmas.getTime() - now.getTime();
if (timeDiff <= 0) {
clearTimeout(timer);
document.write("Christmas is here!");
}
var seconds = Math.floor(timeDiff / 1000);
var minutes = Math.floor(seconds / 60);
var hours = Math.floor(minutes / 60);
var days = Math.floor(hours / 24);
hours %= 24;
minutes %= 60;
seconds %= 60;
document.getElementById("daysBox").innerHTML = days;
document.getElementById("hoursBox").innerHTML = hours;
document.getElementById("minsBox").innerHTML = minutes;
document.getElementById("secsBox").innerHTML = seconds;
var timer = setTimeout('cdtd()',1000);
}
</script>
</head>
<body>
Days Remaining:
<div id="daysBox"></div>
Hours Remaining:
<div id="hoursBox"></div>
Minutes Remaining:
<div id="minsBox"></div>
Seconds Remaining:
<div id="secsBox"></div>
<script type="text/javascript">cdtd();</script>
</body>
</html>
EDIT
Countdown works fine, but to make it work I have to define time
(mm:dd:yy:hh:mm:ss). My question is can I define time like
this(hh:mm:ss) because the date doesn't matter, only thing that
matters is the time(hh:mm:ss) and when it comes to end countdown
restarts and start counting again to example 14:25:00(hh:mm:ss).
Some working result in this fiddle: http://jsfiddle.net/dehisok/6Wu9a/1/
var now = new Date();
var xmas = new Date(now.getFullYear(),now.getMonth(),now.getDate(),23,1,0);
But there is a bug: if needle time is in past - it crashes :( Unfortunatelly, have no more time to fix.
I have changed document.write to jquery, because d.write isn't supported by jsfiddle.
I hope, it's what you need.
Good luck!
Don't leave parsing date strings to the date constructor. ES5 defines a string format to be supported, but not all browsers support it. Prior to ES5, parsing of date strings was entirely implementation dependent. In this case, you know exactly the date and time you wish to set so use numeric arguments:
> var xmas = new Date(2014, 11, 25);
which will create a Date object for 2014-12-25T00:00:00 in the local time zone based on system settings.
> if (timeDiff <= 0) {
> clearTimeout(timer);
There is no need to clear any timeout, just don't call setTimeout again.
> document.write("Christmas is here!");
If called after the load event, that will clear the entire document. Probably not what you want to do for someone who has been watching the timer up to midnight, then loses the entire page. :-)
> var timer = setTimeout('cdtd()',1000);
Calling setTimeout every 1 second means that the timer will gradually drift and occassionally appear to skip a second. It will also not "tick" consistently if compared to the system clock. Instead, set the delay based on the current milliseconds, e.g.
> var timer = setTimeout('cdtd()', (1020 - now%1000));
so it next runs about 20ms after the next full second. And if the time has expired, just don't call setTimeout.
If you are just looking format the time in hours like hh:mm:ss then you need a small function to padd single digit numbers and then concatenate the values, e.g.
function pad(n){return (n<10? '0' ; '') + n;}
then:
document.getElementById("hoursBox").innerHTML = pad(hours) + ':' + pad(minutes) + ':' +
pad(seconds);
You can work out how to format the date part from there. Note that month:day:year format is very confusing to the vast majority of web users. Far better to use an ISO 8601 format like year-month-day hh:mm:ss

Countdown timer with cookies

I know there have been a lot of topics like this but I just have problem to which I couldn't find the answer.
My script is:
window.onload = function(){
// 200 seconds countdown
var countdown = 14400;
//current timestamp
var now = Date.parse(new Date());
//ready should be stored in your cookie
if ( !document.cookie )
{
document.cookie = Date.parse(new Date (now + countdown * 1000)); // * 1000 to get ms
}
//every 1000 ms
setInterval(function()
{
var diff = ( document.cookie - Date.parse(new Date()) );
if ( diff > 0 )
{
var message = diff/1000 + " seconds left";
}
else
{
var message = "finished";
}
document.body.innerHTML = message;
},1000);
}
I want to make countdown timer which tells user time how much left depending on his cookie value. So far I managed to calculate difference between two values but I don't know how to make format like, let's say, "dd/mm/yy hh:mm:ss" from difference timestamp (diff). Is it possible at all?
What you want is a function that converts difference in (mili)seconds to something like
5d 4h 3m 2s
If you don't mind having a large number of days for times periods > a few months, then you could use something like this:
function human_time_difference(diff) {
var s = diff % 60; diff = Math.floor(diff / 60);
var min = diff % 60; diff = Math.floor(diff / 60);
var hr = diff % 24; diff = Math.floor(diff / 24);
var days = diff;
return days + 'd ' + hr + 'h ' + min + 'm ' + s + 's';
}
If you have the difference in miliseconds, you'll need to pass the that number divided by 1000. You can also use Math.round to get rid of fractions, but you could just as well leave them on if you want that information displayed.
Getting months and years is a little trickier for a couple of reasons:
The number of days in a month varies.
When you're going from the middle of one month to the middle of the next, the time span doesn't cover any whole months, even if the number of days > 31 (e.g. How many months are there between the 2nd of June and the 30th of July??).
If you really want the number of months between two times, the number of seconds between them is not enough. You have to use calendar logic, which requires passing in the start and end date + time.
PS: When you post a question, avoid irrelevant details. For example, your question has nothing to do with cookies, setInterval, or onload handlers. The only part that you don't know is how to convert (mili)seconds to days, hours, etc. It might be helpful to supply some background on why you're trying to do something, but if it's not essential to understand the basic question, put it at the end so that people don't have to wade through it before getting to the essential part. The same advice applies to your title; make sure it's relevant by excluding irrelevant details (e.g. cookies and counting down).
JavaScript doesn't have any built in date formatting methods like you might expect if you've done any PHP. Instead, you have to build the string manually. However, there are a number of getter methods that will be useful to this end. See 10 ways to format time and date using JavaScript.
Also, just so you know. Date.parse doesn't return the millisecond portion of the time stamp (it rounds down). If you need the milliseconds, you can do either of the following
var d = new Date();
var timestamp_ms = Date.parse(d) + d.getMilliseconds();
or just
var timestamp_ms = +d;
I do not understand why you check the cookie by if ( !document.cookie ) But it doesnot work on my browser so I modified it into if ( document.cookie )
Try toString function and other. Look them up in javascript Date object reference. For example,
var t = new Date;
t.setTime(diff);
var message = t.toTimeString() + " seconds left";
This will print 11:59:58 seconds left on my browser.

Categories