Welcome to a tutorial on how to create a time picker using pure Javascript and CSS. Yes, there are plenty of time picker plugins available on the Internet. But some of them require a third-party library, and it simply does not make sense to load an entire library for a single-time picker. So here it is, a sharing of my simple time picker – Read on!
ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
TABLE OF CONTENTS
DOWNLOAD & DEMO
Here is the link to download the script, and a short examples section on how to quickly use it in your own projects.
QUICK NOTES
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.
EXAMPLE CODE DOWNLOAD
Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
DEMO & HOW TO USE
BASIC TIME PICKER
<!-- (A) LOAD THE CSS + JS -->
<link href="time-pick.css" rel="stylesheet">
<script src="time-pick.js"></script>
<!-- (B) POPUP TIME PICKER -->
<input type="text" id="demoA">
<!-- (C) ATTACH -->
<script>
tp.attach({
target: document.getElementById("demoA")
});
</script>
- Include the time picker Javascript and CSS files. Captain Obvious at your service.
- Define a
<input type="text">
as usual. - Finally, use
tp.attach({ target : HTML FIELD })
to attach the time picker.
TIME PICKER OPTIONS
tp.attach({
target: document.getElementById("demoB"),
"24": true, // 24 hours
after : time => alert(time) // run function after select
});
That’s all. This simple time picker only has 2 optional options:
24
Use 24 hour time format, defaults tofalse
.after
Function to call after selecting the time.
HOW IT WORKS
With that, let us now move into more details on how the time picker works – This section is for the people who want “deep customizations” on the time picker.
PART 1) TIME PICKER INITIALIZE
// (A) INIT - GENERATE TIME PICKER HTML
hwrap : null, // entire html time picker
hhr : null, // html hour value
hmin : null, // html min value
hap : null, // html am/pm value
init : () => {
// (A1) ADD TIME PICKER TO BODY
tp.hwrap = document.createElement("div");
tp.hwrap.id = "tp-wrap";
document.body.appendChild(tp.hwrap);
// (A2) TIME PICKER INNER HTML
tp.hwrap.innerHTML =
`<div id="tp-box">
<div class="tp-cell" id="tp-hr">
<div class="tp-up">︿</div> <div class="tp-val">0</div> <div class="tp-down">﹀</div>
</div>
<div class="tp-cell" id="tp-min">
<div class="tp-up">︿</div> <div class="tp-val">0</div> <div class="tp-down">﹀</div>
</div>
<div class="tp-cell" id="tp-ap">
<div class="tp-up">︿</div> <div class="tp-val">AM</div> <div class="tp-down">﹀</div>
</div>
<button id="tp-close" onclick="tp.hwrap.classList.remove('show')">Close</button>
<button id="tp-set" onclick="tp.set()">Set</button>
</div>`;
// (A3) GET VALUE ELEMENTS + SET CLICK ACTIONS
for (let segment of ["hr", "min", "ap"]) {
let up = tp.hwrap.querySelector(`#tp-${segment} .tp-up`),
down = tp.hwrap.querySelector(`#tp-${segment} .tp-down`);
tp["h"+segment] = tp.hwrap.querySelector(`#tp-${segment} .tp-val`);
if (segment=="ap") {
up.onclick = () => tp.spin(true, segment);
down.onclick = () => tp.spin(true, segment);
} else {
up.onmousedown = () => tp.spin(true, segment);
down.onmousedown = () => tp.spin(false, segment);
up.onmouseup = () => tp.spin(null);
down.onmouseup = () => tp.spin(null);
up.onmouseleave = () => tp.spin(null);
down.onmouseleave = () => tp.spin(null);
}
}
},
document.addEventListener("DOMContentLoaded", tp.init);
The first thing that runs on page load is tp.init()
. It may look intimidating, but keep calm and look carefully. All it does is generate the time picker HTML <div id="tp-wrap">
to the page – All the inner HTML is right there are A2.
PART 2) TIME PICKER HOUR/MIN/AM/PM SPINNER
// (B) SPIN HOUR/MIN/AM/PM
// direction : true (up), false (down), null (stop)
// segment : "hr", "min", "ap" (am/pm)
timer : null, // for "continous" time spin
minhr : 1, // min spin limit for hour
maxhr : 12, // max spin limit for hour
minmin : 0, // min spin limit for minute
maxmin : 59, // max spin limit for minute
spin : (direction, segment) => {
// (B1) CLEAR TIMER
if (direction==null) { if (tp.timer!=null) {
clearTimeout(tp.timer);
tp.timer = null;
}}
// (B2) SPIN FOR AM/PM
else if (segment=="ap") { tp.hap.innerHTML = tp.hap.innerHTML=="AM" ? "PM" : "AM"; }
// (B3) SPIN FOR HR/MIN
else {
// (B3-1) INCREMENT/DECREMENT
let next = +tp["h"+segment].innerHTML;
next = direction ? next+1 : next-1;
// (B3-2) MIN/MAX
if (segment=="hr") {
if (next > tp.maxhr) { next = tp.maxhr; }
if (next < tp.minhr) { next = tp.minhr; }
} else {
if (next > tp.maxmin) { next = tp.maxmin; }
if (next < tp.minmin) { next = tp.minmin; }
}
// (B3-3) SET VALUE
if (next<10) { next = "0"+next; }
tp["h"+segment].innerHTML = next;
// (B3-4) KEEP ROLLING - LOWER TIMEOUT WILL SPIN FASTER
tp.timer = setTimeout(() => tp.spin(direction, segment), 100);
}
},
Next, I figured that it will be stupid to click on the hour/minute hundreds of times to set it. So this function is the essential driver behind “hold mouse down to spin”.
PART 3) ATTACH TIME PICKER
// (C) ATTACH TIME PICKER TO HTML FIELD
// target : html field to attach to
// 24 : 24 hours time? default false.
// after : optional, function to run after selecting time
attach : instance => {
// (C1) READONLY FIELD + NO AUTOCOMPLETE
// IMPORTANT, PREVENTS ON-SCREEN KEYBOARD
instance.target.readOnly = true;
instance.target.setAttribute("autocomplete", "off");
// (C2) DEFAULT 12 HOURS MODE
if (instance["24"]==undefined) { instance["24"] = false; }
// (C3) CLICK TO OPEN TIME PICKER
instance.target.addEventListener("click", () => tp.show(instance));
},
You already know this one, we call tp.attach()
to attach the time picker to an <input>
field. Basically, we attach an onclick
to display the time picker.
PART 4) DISPLAY THE TIME PICKER
// (D) SHOW TIME PICKER
setfield : null, // set selected time to this html field
set24 : false, // 24 hours mode? default false.
setafter : null, // run this after selecting time
show : (instance) => {
// (D1) INIT FIELDS TO SET + OPTIONS
tp.setfield = instance.target;
tp.setafter = instance.after;
tp.set24 = instance["24"];
tp.minhr = tp.set24 ? 0 : 1 ;
tp.maxhr = tp.set24 ? 23 : 12 ;
// (D2) READ CURRENT VALUE
let val = tp.setfield.value;
if (val=="") {
tp.hhr.innerHTML = "0"+tp.minhr;
tp.hmin.innerHTML = "0"+tp.minmin;
tp.hap.innerHTML = "AM";
} else {
tp.hhr.innerHTML = val.substring(0, 2);
if (tp.set24) {
tp.hmin.innerHTML = val.substring(2, 4);
} else {
tp.hmin.innerHTML = val.substring(3, 5);
tp.hap.innerHTML = val.substring(6, 8);
}
}
// (D3) SHOW TIME PICKER
if (tp.set24) { tp.hwrap.classList.add("tp-24"); }
else { tp.hwrap.classList.remove("tp-24"); }
tp.hwrap.classList.add("show");
},
tp.show()
is called when the user clicks on an attached <input>
field. Not going to explain line-by-line, but it sets some internal flags, then adapts the selected time from the field to the time picker.
PART 5) SET SELECTED TIME
// (E) SET TIME
set : () => {
// (E1) TIME TO FIELD
if (tp.set24) {
tp.setfield.value = tp.hhr.innerHTML + tp.hmin.innerHTML;
} else {
tp.setfield.value = tp.hhr.innerHTML + ":" + tp.hmin.innerHTML + " " + tp.hap.innerHTML;
}
tp.hwrap.classList.remove("show");
// (E2) RUN AFTER, IF SET
if (tp.setafter) { tp.setafter(tp.setfield.value); }
}
Lastly, this should be very obvious – Set the selected time onto the HTML field.
EXTRA BITS & LINKS
That’s all for this project, and here is a small section on some extras that may be useful to you.
CHANGING THE TIME FORMAT
- Modify
set()
, sectionE1
. - Also, remember to change how it adapts existing values in
show()
, sectionD2
.
COMPATIBILITY CHECKS
- Arrow Functions – CanIUse
- Literal Template – CanIUse
This time picker will work on all Grade A modern browsers.
LINKS & REFERENCES
- Pure Javascript Date Picker – Code Boxx
- Example on CodePen – Javascript Timepicker
THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you to create a better website, and if you have anything to share with this guide, please feel free to comment below. Good luck and happy coding!