Skip to content

Instantly share code, notes, and snippets.

@schalkneethling
Created July 8, 2022 15:23
Show Gist options
  • Save schalkneethling/de2b103e3b9a9259f7a10477b943523d to your computer and use it in GitHub Desktop.
Save schalkneethling/de2b103e3b9a9259f7a10477b943523d to your computer and use it in GitHub Desktop.
A simple JavaScript countdown timer
(function () {
dayjs.extend(window.dayjs_plugin_duration);
const launchDay = Date.parse("2022-06-10T21:00:00Z");
const daysOutput = document.getElementById("countdown-days");
const hoursOutput = document.getElementById("countdown-hours");
const minutesOutput = document.getElementById("countdown-minutes");
const secondsOutput = document.getElementById("countdown-seconds");
let duration;
setInterval(() => {
duration = dayjs.duration(launchDay - Date.now());
daysOutput.textContent = duration["$d"].days;
hoursOutput.textContent =
duration["$d"].hours > 9
? duration["$d"].hours
: `0${duration["$d"].hours}`;
minutesOutput.textContent =
duration["$d"].minutes > 0
? duration["$d"].minutes
: `0${duration["$d"].minutes}`;
secondsOutput.textContent =
duration["$d"].seconds > 9
? duration["$d"].seconds
: `0${duration["$d"].seconds}`;
}, 100);
})();
@ctessmer
Copy link

ctessmer commented Sep 18, 2024

Hi there! Can you help me?

I want to make this work so badly because it seems so elegant. Thank you for writing and posting it!

But when I console.log(duration) [after line 14] all the elements (days, hours, etc) are all NaN.

My code

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="color-scheme" content="light dark" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Countdown test</title>
	<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
	<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/duration.js"></script>
</head>
<body>
	
<span id="countdown-days">00</span> day, <span id="countdown-hours">00</span> hour, <span id="countdown-minutes">00</span> min, <span id="countdown-seconds">00</span> second

<script>console.log(dayjs().format())</script>
<script src="/js/countdown.js"></script>

</body>
</html>

Actual output

Browser

Initial: 00 day, 00 hour, 00 min, 00 second
Next tick: NaN day, 0NaN hour, 0NaN min, 0NaN second

Console

console output of duration:

03:08:35.251
Object { "$d": {…}, "$l": "en", "$ms": NaN }
​
"$d": Object { years: NaN, months: NaN, days: NaN, … }
​
"$l": "en"
​
"$ms": NaN
​
<prototype>: Object { calMilliseconds: calMilliseconds(), parseFromMilliseconds: parseFromMilliseconds(), toISOString: toISOString(), … }
countdown.js:16:12

Expected output

Browser

03 day, 10 hour, 07 min, 23 second // example

Console

Not NaN

Question

Any ideas on where I'm going wrong?

@ctessmer
Copy link

ctessmer commented Sep 18, 2024

UPDATE: Fixed. My fault. I accidentally set this:

const launchDay = Date.parse("2024-10-1T00:00:00Z");

which is incorrect because I missed the leading 0 in the day. Once I set the date correctly, everything started working. Very nice! Here is the correct date format:

const launchDay = Date.parse("2024-10-01T00:00:00Z");

@schalkneethling
Copy link
Author

UPDATE: Fixed. My fault. I accidentally set this:

const launchDay = Date.parse("2024-10-1T00:00:00Z");

which is incorrect because I missed the leading 0 in the day. Once I set the date correctly, everything started working. Very nice! Here is the correct date format:

const launchDay = Date.parse("2024-10-01T00:00:00Z");

Awesome! I am glad you figured it out. 🙌

@ctessmer
Copy link

So kind of you to respond! Thank you again for posting it. So elegant!

@ctessmer
Copy link

@schalkneethling If you happen to be familiar with Alpine JS—based on Vue—do you have any ideas how to adapt this to Alpine?

This video got me started, but I'm struggling with how to integrate an anonymous function which responds to essentially an onload() event and not a click event.

Zero pressure, but if you happen to have any ideas on how to solve this, I would love to know!

@schalkneethling
Copy link
Author

Hey @ctessmer - I have not used Alpine, but if the primary problem is the anonymous function you can turn this into a named function. The primary reason this was written as is, is so that it will start as soon as the DOM is "ready" in a sense.

You could also do the following:

const startClock = () => {
  dayjs.extend(window.dayjs_plugin_duration);

  const launchDay = Date.parse("2022-06-10T21:00:00Z");

  const daysOutput = document.getElementById("countdown-days");
  const hoursOutput = document.getElementById("countdown-hours");
  const minutesOutput = document.getElementById("countdown-minutes");
  const secondsOutput = document.getElementById("countdown-seconds");

  let duration;

  setInterval(() => {
    duration = dayjs.duration(launchDay - Date.now());
    daysOutput.textContent = duration["$d"].days;
    hoursOutput.textContent =
      duration["$d"].hours > 9
        ? duration["$d"].hours
        : `0${duration["$d"].hours}`;
    minutesOutput.textContent =
      duration["$d"].minutes > 0
        ? duration["$d"].minutes
        : `0${duration["$d"].minutes}`;
    secondsOutput.textContent =
      duration["$d"].seconds > 9
        ? duration["$d"].seconds
        : `0${duration["$d"].seconds}`;
  }, 100);
};

Wherever you want to kick it off, you can call startClock(); - I hope this helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment