Event Countdown Timer

Hello all. I have an events website and a countdown timer. The countdown works, but I’m seeing some strange behaviour when the time elapses.

Here’s an example of the timer working. This event is tomorrow (20th June) - which is approximately 8 hours away (until midnight):

If the event is today (19th June) - from looking at the code I’d expect the counters to display 0, and my custom message to be shown - however, the numbers are appearing as negatives, and my custom message is still hidden:

It’s probably really obvious, but I didn’t write this snippet.

Any help would be greatly appreciated as always.

Codepen

(function () {
  const second = 1000,
    minute = second * 60,
    hour = minute * 60,
    day = hour * 24;

	// Set the target date and time for the countdown (format: "MM/DD/YYYY HH:mm:ss")
  const targetDateString = "06/20/2024 00:00:00";
  const targetDate = new Date(targetDateString).getTime();

  const updateCountdown = () => {
    const now = new Date().getTime(), // Get the current date and time
      distance = targetDate - now; // Calculate the time remaining until the target date

		// Update the HTML elements with the remaining time
    document.getElementById('days').innerText = Math.floor(distance / day);
    document.getElementById('hours').innerText = Math.floor((distance % day) / hour);
    document.getElementById('minutes').innerText = Math.floor((distance % hour) / minute);
    document.getElementById('seconds').innerText = Math.floor((distance % minute) / second);

    // Check if the target date has already passed
    if (distance < 0) {
      clearInterval(interval); // Stop the countdown timer
				document.getElementById('days').innerText = '0';
				document.getElementById('hours').innerText = '0';
				document.getElementById('minutes').innerText = '0';
				document.getElementById('seconds').innerText = '0';
      // You can display an element at the end of the countdown with this ID  
        document.getElementById('todaymessage').style.display = 'block';
    }
  };

  // Initial call to update countdown
  updateCountdown();

  // Update the countdown every second
  const interval = setInterval(updateCountdown, 1000);
})();

You’re issue is this line.

  // Initial call to update countdown
  updateCountdown();

And that’s because updateCountdown has this block of code in it. the interval variable hasn’t been declared yet, so the line fails, hence why you don’t get your custom message, nor do the numbers stop at 0.

    if (distance < 0) {
      clearInterval(interval); // Stop the countdown timer

To resolve it, just remove that line because it’s being initialized inside the declarattion for interval anyways.

  // Initial call to update countdown
 //  updateCountdown();   <== remove this line!!!

  // Update the countdown every second
  const interval = setInterval(updateCountdown, 1000);

Though I personally would tweak the code a bit to prevent the flash of negatives which show once you’ve reached the thresh hold, and to reduce redundant code

  const updateCountdown = () => {
    let days = 0, hours = 0, minutes = 0, seconds = 0;
    const now = new Date().getTime(), // Get the current date and time

    // Calculate the time remaining until the target date
    distance = targetDate - now;

    // Check if the target date has already passed
    if (distance <= 0) {
      clearInterval(interval); // Stop the countdown timer

      // Display a message at the end of the countdown with this ID  
      document.getElementById('todaymessage').style.display = 'block';
    } else {
      days = Math.floor(distance / day);
      hours = Math.floor((distance % day) / hour);
      minutes = Math.floor((distance % hour) / minute);
      seconds = Math.floor((distance % minute) / second);
    }

	// Update the HTML elements with the remaining time
    document.getElementById('days').innerText = days;
    document.getElementById('hours').innerText = hours;
    document.getElementById('minutes').innerText = minutes;
    document.getElementById('seconds').innerText = seconds;
  };

1 Like

That is awesome. Thank you so much for the quick reply.

1 Like

We have a slight issue!

This code is on a Webflow website, and the website is reading the Event Date from my CMS.

If I remove the following line:

  // Initial call to update countdown
  updateCountdown();

Without that line
when the page loads, you see the placeholder numbers first, and then it quickly loads the correct numbers.

With that line
the page shows the correct numbers straight away and there’s no quick change on load.

I’m guessing that line is there for that very purpose. Does that make sense?

Can we initiate higher up in the code or tweak it slightly?

And just to clarify, I get the Event Date dynamically like so:

I would just hide the placeholders until they’re initialized.

add a display none to the event-header9_number-wrapper class to hide all of the placeholders

.event-header9_number-wrapper {border: 1px solid #000; display:none;}

then add this to the bottom of the updateCountdown method which will show them when they’re initialized.

    document.querySelectorAll(".event-header9_number-wrapper").forEach((item) => {
  item.style.display = 'block';;
});

Good idea. Thank you. Let me put it all together,

1 Like

I am just trying to add an extra layer to this.

I need to differetiate between ‘today’ and ‘the past’

This is the code I’m trying:

<script>
(function() {
    const second = 1000,
        minute = second * 60,
        hour = minute * 60,
        day = hour * 24;

    // Set the target date and time for the countdown (format: "MM/DD/YYYY HH:mm:ss")
    const targetDateString = "{{wf {&quot;path&quot;:&quot;date&quot;,&quot;transformers&quot;:[{&quot;name&quot;:&quot;date&quot;,&quot;arguments&quot;:[&quot;MMM DD, YYYY&quot;]\}],&quot;type&quot;:&quot;Date&quot;\} }}";
    const targetDate = new Date(targetDateString).getTime();

    const updateCountdown = () => {
        let days = 0,
            hours = 0,
            minutes = 0,
            seconds = 0;

        const now = new Date().getTime(), // Get the current date and time
            distance = targetDate - now; // Calculate the time remaining until the target date

        // Check if the target date has already passed
        if (distance < 0) {
            clearInterval(interval); // Stop the countdown timer

            // Display a message at the end of the countdown with this ID  
            document.getElementById('past-message').style.display = 'block';
        } else if (distance === 0) {
            clearInterval(interval); // Stop the countdown timer

            // Display a message at the end of the countdown with this ID  
            document.getElementById('today-message').style.display = 'block';
        } else {
            days = Math.floor(distance / day);
            hours = Math.floor((distance % day) / hour);
            minutes = Math.floor((distance % hour) / minute);
            seconds = Math.floor((distance % minute) / second);
        }

        // Update the HTML elements with the remaining time
        document.getElementById('days').innerText = days;
        document.getElementById('hours').innerText = hours;
        document.getElementById('minutes').innerText = minutes;
        document.getElementById('seconds').innerText = seconds;
        document.getElementById('event-countdown').style.display = 'block';
    };

    // Update the countdown every second
    const interval = setInterval(updateCountdown, 1000);
})();
</script>

Future events are working (shows countdown).
Past events are working (shows ‘past-message’)

Events that are today are not working, they are showing ‘past-message’ as opposed to ‘today-message’.

I’ve used if (distance === 0) to try and check whether the date is today, but it always seems to think it’s (distance < 0)

Well, the distance is only 0 at midnight on the day of the event. So on the day of the event, distance is between 0 and minus the number of seconds that fit in a day, so:

<script>
(function() {
    const second = 1000,
        minute = second * 60,
        hour = minute * 60,
        day = hour * 24;

    // Set the target date and time for the countdown (format: "MM/DD/YYYY HH:mm:ss")
    const targetDateString = "{{wf {&quot;path&quot;:&quot;date&quot;,&quot;transformers&quot;:[{&quot;name&quot;:&quot;date&quot;,&quot;arguments&quot;:[&quot;MMM DD, YYYY&quot;]\}],&quot;type&quot;:&quot;Date&quot;\} }}";
    const targetDate = new Date(targetDateString).getTime();

    const updateCountdown = () => {
        let days = 0,
            hours = 0,
            minutes = 0,
            seconds = 0;

        const now = new Date().getTime(), // Get the current date and time
            distance = targetDate - now; // Calculate the time remaining until the target date

        // Check if the target date has already passed
        if (distance < 0 && distance > -1 * day) {
            clearInterval(interval); // Stop the countdown timer

            // Display a message at the end of the countdown with this ID  
            document.getElementById('today-message').style.display = 'block';
        } else if (distance < 0) {
            clearInterval(interval); // Stop the countdown timer

            // Display a message at the end of the countdown with this ID  
            document.getElementById('past-message').style.display = 'block';
        } else {
            days = Math.floor(distance / day);
            hours = Math.floor((distance % day) / hour);
            minutes = Math.floor((distance % hour) / minute);
            seconds = Math.floor((distance % minute) / second);
        }

        // Update the HTML elements with the remaining time
        document.getElementById('days').innerText = days;
        document.getElementById('hours').innerText = hours;
        document.getElementById('minutes').innerText = minutes;
        document.getElementById('seconds').innerText = seconds;
        document.getElementById('event-countdown').style.display = 'block';
    };

    // Update the countdown every second
    const interval = setInterval(updateCountdown, 1000);
})();
</script>

Personally i’d invert the order.

if distance > 0, show time. (It’s some number of days in the future)
elseif targetDate > now, show today
else show past.

Side note: Why do I need a hour/minute/second countdown if the clock is only counting down to “the day of”? (The above logic assumes that 0 = midnight.)

Slightly better logic to handle non-midnight events:

Global scope:
Get the Date for the Event Timestamp event.
Get the Timestamp for the Event eventTS = event.getTime().
Get the localized Date string for event, eventDate = event.toDateString()

The UpdateClock logic then goes as follows:
Get the Date now.
Set distance = max(eventTS - now.getTime() , 0)
Update all of the counters. (Note: You only use days/minutes/hours/seconds once, so its questionable whether you need to separate the logic or not.)
if distance == 0, show your special message for the past.
Elseif now.toDateString() == eventDate, show your special message for today.

Thank you all for your help.

There has been a slight change in the logic.

Each event will now have both a Start Date and an End Date.

I have tried to get my head around the logic but have been unable to do so.

I have declared the end date and renamed the start date which should hopefully put me on the right track:

<script>
    (function() {
        const second = 1000,
            minute = second * 60,
            hour = minute * 60,
            day = hour * 24;

        // Set the target date and time for the countdown (format: "MM/DD/YYYY HH:mm:ss")
        const startDateString = "{{wf {&quot;path&quot;:&quot;date&quot;,&quot;transformers&quot;:[{&quot;name&quot;:&quot;date&quot;,&quot;arguments&quot;:[&quot;MMM DD, YYYY&quot;]\}],&quot;type&quot;:&quot;Date&quot;\} }}";
        const startDate = new Date(startDateString).getTime();

        const endDateString = "{{wf {&quot;path&quot;:&quot;end-date&quot;,&quot;transformers&quot;:[{&quot;name&quot;:&quot;date&quot;,&quot;arguments&quot;:[&quot;MMM DD, YYYY&quot;]\}],&quot;type&quot;:&quot;Date&quot;\} }}";
        const endDate = new Date(endDateString).getTime();

        const updateCountdown = () => {
            let days = 0,
                hours = 0,
                minutes = 0,
                seconds = 0;

            // Get the current date and time
            const now = new Date().getTime(),

            // Calculate the time remaining until the start date
            distanceToStart = startDate - now;

            // Calculate the time remaining until the end date
            distanceToEnd = endDate - now;

            // Check if the start date has already passed
            if (distanceToStart < 0 && distanceToStart > -1 * day) {

                // Stop the countdown timer
                clearInterval(interval);

                // Display a message at the end of the countdown with this ID  
                document.getElementById('current-message').style.display = 'block';

            } else if (distanceToStart < 0) {

                // Stop the countdown timer    
                clearInterval(interval);

                // Display a message at the end of the countdown with this ID  
                document.getElementById('past-message').style.display = 'block';

            } else {

                days = Math.floor(distanceToStart / day);
                hours = Math.floor((distanceToStart % day) / hour);
                minutes = Math.floor((distanceToStart % hour) / minute);
                seconds = Math.floor((distanceToStart % minute) / second);
                
            }

            // Update the HTML elements with the remaining time
            document.getElementById('days').innerText = days;
            document.getElementById('hours').innerText = hours;
            document.getElementById('minutes').innerText = minutes;
            document.getElementById('seconds').innerText = seconds;
            document.getElementById('event-countdown').style.display = 'block';
        };

        // Update the countdown every second
        const interval = setInterval(updateCountdown, 1000);
    })(); 
</script>

So here are 3 events in my system, and they are being looked at today (24th June 2024):

Event 1
Start: 1st June 2024
End: 1st June 2024
This is a past event and should show countdown 0 and past-message

Event 2
Start: 14th June 2024
End: 14th July 2024
This is a current event and should show countdown 0 and current-message

Event 3
Start: 27th June 2024
End: 29th June 2024

This is a future event and should show countdown timer.

I tried myself to use the end date within the current-message part but it went horribly wrong!

Quick and dirty, you’d need to reverse your logic in the if statement.

            // Check if the start date has already passed
            if (distanceToEnd <= 0) {

                // Stop the countdown timer    
                clearInterval(interval);

                // Display a message at the end of the countdown with this ID  
                document.getElementById('past-message').style.display = 'block';

            } else if (distanceToStart < 0 && distanceToStart > -1 * day) {

                // Stop the countdown timer
                clearInterval(interval);

                // Display a message at the end of the countdown with this ID  
                document.getElementById('current-message').style.display = 'block';
            } else {

though I personally would tweak it a bit differently.

            // Check if the start date has already passed
            if (distanceToStart < 0) {
                // Stop the countdown timer    
                clearInterval(interval);

                // Display a message at the end of the countdown with this ID  
                if (distanceToEnd < 0) {
                    document.getElementById('past-message').style.display = 'block'; 
                } else {
                    document.getElementById('current-message').style.display = 'block';
                }        
            } else {

(Surely you’d only want to stop the Interval once you’ve finished distanceToEnd, to update current to past)

Dave your quick and dirty solution is working a treat. Thank you!

1 Like

To me, stopping the counter when the start passed made sense because the event has started (which is what it was counting down to). What changed with this new iteration was controlling when the current and ended message displays.

right, but its not just stopping the counter, its stopping all changes. So if my event starts at 6 pm, ends at 7pm, and you leave your browser open from 5:30 until 9, you would see the event is cirrently occuring, because you stopped the interval at 6pm and the interval will never be checked again.

2 Likes

Hello all, there is one more issue that needs resolving.

I need to add an additional rule.

Today’s date is June 27th and the code is working for the following dated events:

Event 1
Start: June 23rd
End. June 25th
Shows past message (CORRECT)

Event 2
Start: June 23rd
End: June 29th
Shows current message (CORRECT)

Event 3
Start: June 29th
End: June 29th:
Shows countdown only (CORRECT)

The problem now is for events that start and finish on the same day - which happens to be today!

Event 4 (today)
Start: June 27th
End: June 27th

This type of event is showing the ‘past-message’ and basically telling my website visitors 'This event has finished (on the actual day of the event). I need to tweak the code so if the end date happens to be today, show the ‘current’ message also.

Please help me Dave!

Current code:

<script>
    (function() {
        const second = 1000,
            minute = second * 60,
            hour = minute * 60,
            day = hour * 24;

        // Set the target date and time for the countdown (format: "MM/DD/YYYY HH:mm:ss")
        const startDateString = "{{wf {&quot;path&quot;:&quot;date&quot;,&quot;transformers&quot;:[{&quot;name&quot;:&quot;date&quot;,&quot;arguments&quot;:[&quot;MMM DD, YYYY&quot;]\}],&quot;type&quot;:&quot;Date&quot;\} }}";
        const startDate = new Date(startDateString).getTime();

        const endDateString = "{{wf {&quot;path&quot;:&quot;end-date&quot;,&quot;transformers&quot;:[{&quot;name&quot;:&quot;date&quot;,&quot;arguments&quot;:[&quot;MMM DD, YYYY&quot;]\}],&quot;type&quot;:&quot;Date&quot;\} }}";
        const endDate = new Date(endDateString).getTime();

        const updateCountdown = () => {
            let days = 0,
                hours = 0,
                minutes = 0,
                seconds = 0;

            // Get the current date and time
            const now = new Date().getTime(),

                // Calculate the time remaining until the start date
                distanceToStart = startDate - now;

            // Calculate the time remaining until the end date
            distanceToEnd = endDate - now;

            // Check if the start date has already passed
            if (distanceToStart < 0) {
                // Stop the countdown timer    
                clearInterval(interval);

                // Display a message at the end of the countdown with this ID  
                if (distanceToEnd < 0) {
                    document.getElementById('past-message').style.display = 'block'; 
                } else {
                    document.getElementById('current-message').style.display = 'block';
                }        
            } else {
                days = Math.floor(distanceToStart / day);
                hours = Math.floor((distanceToStart % day) / hour);
                minutes = Math.floor((distanceToStart % hour) / minute);
                seconds = Math.floor((distanceToStart % minute) / second);
            }

            // Update the HTML elements with the remaining time
            document.getElementById('days').innerText = days;
            document.getElementById('hours').innerText = hours;
            document.getElementById('minutes').innerText = minutes;
            document.getElementById('seconds').innerText = seconds;
            document.getElementById('event-countdown').style.display = 'block';
        };

        // Update the countdown every second
        const interval = setInterval(updateCountdown, 1000);
    })(); 
</script>

This event occurs for exactly 0 seconds.

Your “End” of an all-day event is midnight the next day.

(This is the consequence of using dates instead of datetimes)

If you specify the event as occuring from June 27th 00:00:00 to June 27th 23:59:59, it would work.

Unfortunately it’s happening throughout the entire day. This event has a start date and finish date of June 27th (today), and you can see the ‘past-message’ displaying:

It would be very difficult to adjust the end dates at this stage to 23:59.