0

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:

enter image description here

It does work but has the following problems:

  1. It refreshes with the browser refresh.
  2. It doesn't have hours.
  3. It does not auto-repeat when the timer reaches zero.
  4. 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?

Bharata
  • 13,509
  • 6
  • 36
  • 50
Kuldeep Kumar
  • 905
  • 3
  • 9
  • 22
  • 4
    Since you want that mulitpile users will see the same timer, js alone won't help cause it's client side – A. Meshu Jul 12 '18 at 16:06
  • You need some call to your server which needs to handle that atomically. Js alone won’t help you – Regis Portalez Jul 12 '18 at 16:10
  • You first need to calculate the difference between the current time (ie. the time that the user lands on your page) and the target time (when the timer should hit zero). Then use the time difference to fill in the hours, minutes, seconds used in your function. I'm not sure why people are suggesting that this should be done server side. That just seems silly. – Turnip Jul 12 '18 at 16:14
  • 2
    Possible duplicate of [how to countdown to a date](https://stackoverflow.com/questions/9335140/how-to-countdown-to-a-date) – Turnip Jul 12 '18 at 16:17
  • 1
    Don't think of how long the countdown should run. Think about when it should end, and only store that info (then make your GUI count down towards that timestamp). – Bergi Jul 12 '18 at 16:19

2 Answers2

2

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>
Community
  • 1
  • 1
Damiaan Dufaux
  • 4,427
  • 1
  • 22
  • 33
  • Damiann you are the man :D ... Thanks a lot .. you just save me a lot of pain :D .. Great explanation, really appreciate all the help :) The answer is perfect but I was wondering about something. As you told it relies on the time of user's machine, so I was thinking if we can somehow check for the reference time from the internet and then display the time left accordingly? That way every user will see the correct time left regardless of what time they have on their machine. Is it doable in js ? – Kuldeep Kumar Jul 13 '18 at 01:57
  • I edited my answer with a solution on how to use a reference time from the internet. Please consider [accepting my answer](https://stackoverflow.com/help/someone-answers) if this solves your issues. – Damiaan Dufaux Jul 13 '18 at 07:32
  • Hi Damiaan :) I have accepted your answer and it's working great on the website. Would like to know if I use the send version of your code (one which fetches time from the google server?) .. then how likely it is that the server will stop sending the time info ? – Kuldeep Kumar Jul 13 '18 at 12:36
  • It is very likely that this experiment will stop working. The web (technology) changes continuously. No website stays the same for 1 year, they get updates. – Damiaan Dufaux Jul 13 '18 at 13:40
  • Hmm .. you are right ... is there any way that the solution remains permanent .. I mean if there exist any website/ web service/ api that offers time and remains constant throughout years ..? – Kuldeep Kumar Jul 13 '18 at 15:50
  • Yes just add it to your own server. – Damiaan Dufaux Jul 13 '18 at 15:51
  • Sorry If I sound stupid .. :P .. but add what .. you wrote the api yourself .. and published it to google server ? – Kuldeep Kumar Jul 13 '18 at 15:53
  • No need to feel sorry. This site is made to ask questions. I didn’t write any API. I just fetched the date from a google website. I meant add a service that returns the current time stamp, like I suggested in my answer. – Damiaan Dufaux Jul 13 '18 at 19:58
  • Okay .. thanks .. will try to do that :) Just one more thing .. The current timer will count down to 24 hours right ... what if I want it to count down to just 12 hours ... or just 6 hours .. or just 3 hours ... how can I achieve this? – Kuldeep Kumar Jul 14 '18 at 15:59
  • Then you need to change the deadline with `.setHours(...)` – Damiaan Dufaux Jul 14 '18 at 17:26
  • Tried changing it .. didn't get it to work .. maybe I am doing it wrong .. would you mind changing your code if I need the timer to repeat every 6 hours .... I mean it should start saying 6 hours remaining to 5,4,3,2,1 and then again start from 6... – Kuldeep Kumar Jul 15 '18 at 11:26
  • Show me your edited version, I'll explain what you did wrong. – Damiaan Dufaux Jul 15 '18 at 12:35
  • I just set hours to 6 ... deadline.setHours(6) .... I think I am just using hit and trial to work things out :P – Kuldeep Kumar Jul 15 '18 at 13:22
  • 1
    Well by writing `deadline.setHours(6)` you are setting the deadline to 6'o clock in the morning. But that is not what you say you were intending to do. When you hit the deadline you will need to move it 6 hours further. So in other words you will have to `setHours` to something like `Math.floor(now().getHours() + 6 - now().getHours()%6)` – Damiaan Dufaux Jul 15 '18 at 14:10
  • Thanks for the explaining .. so all this time I was changing the time to which the timer was going to countdown to ... I am however still not clear about the Math.floor(now().getHours() + 6 - now().getHours()%6) thing .. where exactly should I put this in code to get this to work. – Kuldeep Kumar Jul 15 '18 at 14:49
1

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.

gezibash
  • 36
  • 1
  • 2