A Flexible Countdown Timer


(Jonathan Deutsch) #1

Creating a countdown timer clock is a fairly common task, and often used in timed promotions. There’s a lot of solutions on the internet and even on these forums. I wanted to try creating one that would give content creators a lot of flexibility with the goal being able to use your own styling on the various time components.

Here’s my solution for a flexible countdown timer. You can see it in action with this document:

FlexibleCountdownTimer.hype.zip (65.8 KB)

First, copy this code to the Head HTML:

<script>

// replace the string value with the end date you want for the countdown timer
// this is an ISO 8601 string, like: YYYY-MM-DD(T)HH:MM:SS(+/-)TZD

var countDownDateString = "2017-05-15T00:00:00-08:00";


function updateCountdownClocks() {
	// some code from:
	//    https://www.w3schools.com/howto/howto_js_countdown.asp
	// 	  http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript

	var targetTimeInterval = new Date(countDownDateString).getTime();
	var currentTimeInterval = new Date().getTime();
	
	var deltaTimeInterval = targetTimeInterval - currentTimeInterval;
	if(deltaTimeInterval < 0) {
		// do special handling when timer is expired; default is to just set it to 0
		deltaTimeInterval = 0;
	}
	
	var timeComponents = {
		"days" : Math.floor(deltaTimeInterval / (1000 * 60 * 60 * 24)),
		"hours" :  Math.floor((deltaTimeInterval % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
		"minutes" : Math.floor((deltaTimeInterval % (1000 * 60 * 60)) / (1000 * 60)),
		"seconds" : Math.floor((deltaTimeInterval % (1000 * 60)) / 1000),
	};
	
	// utility function to add leading 0's
	function pad(n, width, z) {
		z = z || '0';
		n = n + '';
		return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
	}
	
	// utility function that gets the requested padding of zeros from a class name
	// of the format countdown_format_N where 'N' is an integer for the 
	// total width (ie 3 would be 088 008 888 8888)
	function determinePadding(element) {
		var matches = element.className.match(/countdown\_format\_([\d]+)/);
		if(matches == null || matches.length != 2) {
			return 0;
		}
		return Math.max(0, parseInt(matches[1]));
	}

	// for each component, try to find a matching class name that wants to be filled in
	for(var timeComponentKey in timeComponents) {
		if(timeComponents.hasOwnProperty(timeComponentKey) == false) {
			continue;
		}
		
		var timeValue = timeComponents[timeComponentKey];
		
		var timeElements = document.getElementsByClassName("countdown_" + timeComponentKey);
		for(var i = 0; i < timeElements.length; i++) {
			var timeElement = timeElements[i];
			var padding = determinePadding(timeElement);
			timeElement.innerHTML = pad(timeValue, padding);
		}
	}
	
	// repeat every second
	window.setTimeout(updateCountdownClocks, 1000);
}

// start after document has loaded (without use of handlers)
window.setTimeout(updateCountdownClocks, 0);

// use a Hype scene load handler to start:
if("HYPE_eventListeners" in window === false) {
	window.HYPE_eventListeners = Array();
}
window.HYPE_eventListeners.push({"type":"HypeSceneLoad", "callback":updateCountdownClocks});

</script>

In the above paste, you’ll want to change this line to reflect the date you are counting down to:

var countDownDateString = "2017-05-15T00:00:00-08:00";

You don’t need to modify anything else in the JavaScript code.

The way the countdown timer works is that it will replace the inner HTML of elements with these class names to their various values: countdown_days, countdown_hours, countdown_minutes, and countdown_seconds. So what you can do is make text elements in Hype and set the Class Name value in the Identity Inspector to each of these.

A common formatting problem with time is whether or not to display leading zeros. If you append another class name of countdown_format_2 then the value will always display two or more digits (in fact, you can use any number as the class name, so you could use countdown_format_3 for something like the countdown_days if you wanted). So a full Class Name might look like:

Because this just operates on class names, you don’t necessarily need to break out each component into a separate Hype element. It can also look into the inner HTML of a single element. So if you wanted to construct a timer that looked like “0d 0h 00m 00s”, you would set the Inner HTML of the element to this code:

<span class="countdown_days">0</span>d
<span class="countdown_hours">0</span>h
<span class="countdown_minutes countdown_format_2">00</span>m
<span class="countdown_seconds countdown_format_2">00</span>s

The code itself doesn’t really have any Hype dependencies, so you could even use it on non-Hype projects if you wanted, but hopefully the way this was put together will be useful when using Hype for countdown timers!

P.S. There are some items this countdown timer doesn’t handle:

  • When the countdown is done, the value is simply 0
  • There’s no way to represent other date values, like weeks or months
  • While you can have as many different views on the countdown date as you want, there can only be one date per document/html page.

When the countdown is done (JS)
(Mark Hunte) #2

Would this work correctly for weeks.
Seems to be ok when I test.

var timeComponents = {
	"weeks" : Math.floor((deltaTimeInterval / (1000 * 60 * 60 * 24))/7),
		"days" : Math.floor((deltaTimeInterval / (1000 * 60 * 60 * 24) - (Math.floor((deltaTimeInterval / (1000 * 60 * 60 * 24))/7)) *7 ) ),
		"hours" :  Math.floor((deltaTimeInterval % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
		"minutes" : Math.floor((deltaTimeInterval % (1000 * 60 * 60)) / (1000 * 60)),
		"seconds" : Math.floor((deltaTimeInterval % (1000 * 60)) / 1000),
	};

Added a element with the class names countdown_weeks countdown_format_1


(Jonathan Deutsch) #3

The reasons I didn’t add weeks is presumably you’d want a different value for days (the remainder). This gets far more complex if you want months, years, etc.


(Mark Hunte) #4

Yep , although the code above does give you the remaining days., my head started to hurt just trying to work that one out, hence my questioning it as I my brain was fried ):smile:


(Jonathan Deutsch) #5

Oh, I missed that since I was looking for a different key name :slight_smile:.


(joost) #7

Hi, I like the countdown clock, but what is the reason to copy the js into the head element?
It also works fine as a document js when you trigger it on scene load.


(Jonathan Deutsch) #8

I probably just had that as a lowest common denominator type of suggestion such that if people moved around scenes or had multiple clocks across different scenes it would still work without duplicating code/variables. If using the code within an On Scene Load handler is better for you, by all means do it that way :).