Welcome to a tutorial on how to create an on-screen numeric keypad using only pure Javascript. Yes, HTML has gotten a lot more powerful and convenient these days. We can even define a field that only accepts numbers, but there is just one problem with it…
It will only display an on-screen keypad with mobile devices, and we cannot customize it. So here it is, this guide will walk you through a simple and lightweight custom numeric keypad – 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
Firstly, here is the download link to the example code as promised.
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.
NUMERIC KEYPAD DEMO (HOW TO USE)
<!-- (A) LOAD JS + CSS -->
<!-- <link rel="stylesheet" href="numpad-light.css"/> -->
<link rel="stylesheet" href="numpad-dark.css"/>
<script src="numpad.js"></script>
<!-- (B) INPUT FIELDS -->
Field A: <input type="text" id="demoA"/>
Field B: <textarea id="demoB"></textarea>
<!-- (C) ATTACH NUMPAD -->
<script>
window.addEventListener("load", () => {
// (C1) BASIC NUMPAD
numpad.attach({target: document.getElementById("demoA")});
// (C2) WITH ALL POSSIBLE OPTIONS
numpad.attach({
target: document.getElementById("demoB"),
max: 10, // MAX 10 DIGITS
decimal: false, // NO DECIMALS ALLOWED
onselect : () => { // CALL THIS AFTER SELECTING NUMBER
alert("DEMO B number set.");
},
oncancel : () => { // CALL THIS AFTER CANCELING
alert("DEMO B canceled.");
}
});
});
</script>
Field A:
Field B:
For you guys who don’t want to read the entire tutorial and just want to use this as a “plugin”:
- Simply include the CSS and Javascript files in your own project.
- Define the
<input>
or<textarea>
fields. - On window load, use
numpad.attach()
attach the Numpad to the fields.
HOW IT WORKS
With that, let us now move into the details of how the Numpad works – This is for you guys who want to do some “deep customizations”.
PART 1) INITIALIZE NUMPAD (ATTACH HTML)
// (A) CREATE NUMPAD HTML
hwrap: null, // numpad wrapper container
hpad: null, // numpad itself
hdisplay: null, // number display
hbwrap: null, // buttons wrapper
hbuttons: {}, // individual buttons
init: () => {
// (A1) WRAPPER
numpad.hwrap = document.createElement("div");
numpad.hwrap.id = "numWrap";
// (A2) ENTIRE NUMPAD ITSELF
numpad.hpad = document.createElement("div");
numpad.hpad.id = "numPad";
numpad.hwrap.appendChild(numpad.hpad);
// (A3) DISPLAY
numpad.hdisplay = document.createElement("input");
numpad.hdisplay.id = "numDisplay";
numpad.hdisplay.type = "text";
numpad.hdisplay.disabled = true;
numpad.hdisplay.value = "0";
numpad.hpad.appendChild(numpad.hdisplay);
// (A4) NUMBER BUTTONS
numpad.hbwrap = document.createElement("div");
numpad.hbwrap.id = "numBWrap";
numpad.hpad.appendChild(numpad.hbwrap);
// (A5) BUTTONS
let buttonator = (txt, css, fn) => {
let button = document.createElement("div");
button.innerHTML = txt;
button.classList.add(css);
button.onclick = fn;
numpad.hbwrap.appendChild(button);
numpad.hbuttons[txt] = button;
};
// 7 TO 9
for (let i=7; i<=9; i++) { buttonator(i, "num", () => { numpad.digit(i); }); }
// BACKSPACE
buttonator("⤆", "del", numpad.delete);
// 4 TO 6
for (let i=4; i<=6; i++) { buttonator(i, "num", () => { numpad.digit(i); }); }
// CLEAR
buttonator("C", "clr", numpad.reset);
// 1 to 3
for (let i=1; i<=3; i++) { buttonator(i, "num", () => { numpad.digit(i); }); }
// CANCEL
buttonator("✖", "cx", () => { numpad.hide(1); });
// 0
buttonator(0, "zero", () => { numpad.digit(0); });
// .
buttonator(".", "dot", numpad.dot);
// OK
buttonator("✔", "ok", numpad.select);
// (A6) ATTACH NUMPAD TO HTML BODY
document.body.appendChild(numpad.hwrap);
}
window.addEventListener("DOMContentLoaded", numpad.init);
numpad.init()
is the first thing that gets called on page load, and all it does is to create the Numpad HTML. We will go through the layout below, but yes, there is only one copy of Numpad shared between all the input fields.
PART 2) BUTTON ACTIONS
// (B) BUTTON ACTIONS
// (B1) CURRENTLY SELECTED FIELD + MAX LIMIT
nowTarget: null, // Current selected input field
nowMax: 0, // Current max allowed digits
// (B2) NUMBER (0 TO 9)
digit: (num) => {
let current = numpad.hdisplay.value;
if (current.length < numpad.nowMax) {
if (current=="0") { numpad.hdisplay.value = num; }
else { numpad.hdisplay.value += num; }
}
},
// (B3) ADD DECIMAL POINT
dot: () => {
if (numpad.hdisplay.value.indexOf(".") == -1) {
if (numpad.hdisplay.value=="0") { numpad.hdisplay.value = "0."; }
else { numpad.hdisplay.value += "."; }
}
},
// (B4) BACKSPACE
delete: () => {
var length = numpad.hdisplay.value.length;
if (length == 1) { numpad.hdisplay.value = 0; }
else { numpad.hdisplay.value = numpad.hdisplay.value.substring(0, length - 1); }
},
// (B5) CLEAR ALL
reset: () => { numpad.hdisplay.value = "0"; },
// (B6) OK - SET VALUE
select: () => {
numpad.nowTarget.value = numpad.hdisplay.value;
numpad.hide();
numpad.nowTarget.dispatchEvent(new Event("numpadok"));
}
Not going to explain line-by-line, but these should be pretty self-explanatory – Handle the button clicks.
PART 3) ATTACH NUMPAD
// (C) ATTACH NUMPAD TO INPUT FIELD
attach: (opt) => {
// OPTIONS
// target: required, target field.
// max: optional, maximum number of characters. Default 255.
// decimal: optional, allow decimal? Default true.
// onselect: optional, function to call after selecting number.
// oncancel: optional, function to call after canceling.
// (C1) DEFAULT OPTIONS
if (opt.max === undefined) { opt.max = 255; }
if (opt.decimal === undefined) { opt.decimal = true; }
// (C2) GET + SET TARGET OPTIONS
opt.target.readOnly = true; // PREVENT ONSCREEN KEYBOARD
opt.target.dataset.max = opt.max;
opt.target.dataset.decimal = opt.decimal;
opt.target.addEventListener("click", () => { numpad.show(opt.target); });
// (C3) ATTACH CUSTOM LISTENERS
if (opt.onselect) {
opt.target.addEventListener("numpadok", opt.onselect);
}
if (opt.oncancel) {
opt.target.addEventListener("numpadcx", opt.oncancel);
}
},
// (D) SHOW NUMPAD
show: (target) => {
// (D1) SET CURRENT DISPLAY VALUE
let cv = target.value;
if (cv == "") { cv = "0"; }
numpad.hdisplay.value = cv;
// (D2) SET MAX ALLOWED CHARACTERS
numpad.nowMax = target.dataset.max;
// (D3) SET DECIMAL
if (target.dataset.decimal == "true") {
numpad.hbwrap.classList.remove("noDec");
} else {
numpad.hbwrap.classList.add("noDec");
}
// (D4) SET CURRENT TARGET
numpad.nowTarget = target;
// (D5) SHOW NUMPAD
numpad.hwrap.classList.add("open");
},
// (E) HIDE NUMPAD
hide: (manual) => {
if (manual) { numpad.nowTarget.dispatchEvent(new Event("numpadcx")); }
numpad.hwrap.classList.remove("open");
}
- (C) You already know this one, we use
numpad.attach()
to attach the Numpad. It simply sets the options as customdataset
. - Remember that there is only one shared HTML Numpad? To keep track of the currently selected input field:
- (C2) Clicking on the input field will fire
numpad.show()
. - (D4) The selected input field will be registered in the
numbpad.nowTarget
flag. - (D2 & D3) The maximum length and decimal options will also be read from the
dataset
and registered accordingly.
- (C2) Clicking on the input field will fire
- (D & E) To show the Numpad, we add an
open
CSS class to the HTML wrapper. So to close it, we simply removeopen
.
NUMPAD HTML
Wondering how the complete keypad looks like? Here we go:
<div id="numWrap">
<div id="numPad" >
<input id="numDisplay" type="text" disabled="">
<div id="numBWrap" >
<!-- FIRST ROW -->
<div class="num">7</div> <div class="num">8</div> <div class="num">9</div> <div class="del">⤆</div>
<!-- SECOND ROW -->
<div class="num">4</div> <div class="num">5</div> <div class="num">6</div> <div class="clr">C</div>
<!-- THIRD ROW -->
<div class="num">1</div> <div class="num">2</div> <div class="num">3</div> <div class="cx">X</div>
<!-- FORTH ROW -->
<div class="zero">0</div> <div class="dot">.</div> <div class="ok">✔</div>
<div>
</div>
</div>
USEFUL BITS
That’s all for this guide, and here is a small section on some extras that may be useful to you.
THE SUMMARY
Function | Description |
numpad.init() |
Runs when the window loads. Creates the necessary HTML for the keypad. |
numpad.digit() |
Adds selected number digit to the current value. |
numpad.dot() |
Adds a decimal point to the current value. |
numpad.delete() |
“Backspace”, remove a digit from the current value. |
numpad.reset() |
Reset the current value to 0. |
numpad.select() |
Set the current value onto the attached input field. |
numpad.attach() |
Attach the numpad onto a selected input field. |
numpad.show() |
Show the numpad. Fired when an attached input field is clicked. |
numpad.hide() |
Hide the numpad. |
Property | Description |
numpad.hwrap |
HTML Reference to the keypad wrapper container. |
numpad.hpad |
The numpad itself. |
numpad.hdisplay |
The numeric display. |
numpad.hbwrap |
Buttons wrapper container. |
numpad.hbuttons |
An object containing all the keypad buttons. |
numpad.nowTarget |
Set by numpad.show() , the currently selected input field. |
numpad.nowMax |
Set by numpad.show() , the current maximum character limit. |
Event | Description |
numpadok |
On pressing the “OK” button. |
numpadcx |
On pressing the “CANCEL” button. |
CHANGE BUTTON LAYOUT OR ADD CUSTOM BUTTONS
- To change the layout, simply reshuffle
numpad.js
section (A5). - If you don’t want 4 buttons per row, update CSS sections (D) and (E).
If you want buttons for funny characters like # - *
, that will be a little more challenging.
- Append a new function in section (B) to handle the button press. For example,
hash : () => { ... }
. - Then change section (A5) in the Javascript to add your “custom button”. For example,
buttonator("#", "hash", numpad.hash)
. - Lastly, update the CSS button layout where applicable.
COMPATIBILITY CHECKS
- Arrow Functions – CanIUse
- Viewport Units (vw vh) – CanIUse
- HTML Dataset API – CanIUse
Works well across all modern browsers.
THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
as a novice I am struggling with how to pass through a form input.
This keypad is perfect for what I need but I can’t figure out how to get the entered value into $_POST
what goes in the value?
ps love the keyboard but know nothing about js
Just set the form to
method="post"
and addname="XYZ"
to the input field? Don’t skip the basics and follow up with your own studies. Good luck.https://www.tutorialspoint.com/html/html_forms.htm
https://www.php.net/manual/en/tutorial.forms.php
https://code-boxx.com/faq/#help “Answer all over the Internet”
Hello, thanks for this code! I added the below snippet to add keyboard functionality, in case you wanted to copy it in to your source.
Thanks for sharing, it is a better idea to attach the keydown listener only when the keypad is open – Will add this in the next update.
Good Morning! I am using this GREAT code (Thanks so much for this) into a time clock system and was wondering how can I make the numberpad show automatically? I currently have a field called PIN and that is the trigger when people click on the field. That’s what I want to remove from the process… any help would be great
The numpad is a fullscreen overlay. If you want it to “show it permanently on the screen without blocking other content”, I am afraid the only way is to change the entire CSS. Good luck.
Is there any auto clicking method on page load that could be added to cause the full screen overlay to appear? I tired the .click() method but it didn’t work, any help would be greatly appreciated.
As above –
numpad.show()
I’m attempting to use the numpad so that the user can enter a currency value (international). Is there a way I can limit the number of decimal places depending on the currency used?
Modify the Javascript B2/B3. Otherwise, I will take this as a feature request. Good luck.
https://code-boxx.com/faq/#help “Requests for new features will not be immediately answered.”
Thank you for sharing! I think it is brilliantly simple and neat.
No dependencies is great! The files i use it in are sometimes opened from the file system (no server), so this was perfect.
Can’t help thinking about how much work it would be to make it into a full keyboard 🙂
How to add a leading zero? Example if i want the input to be 0123 because as is it would only input 123
Modify section B2,
digit()
. Otherwise, I shall take this as a request and possible upgrade in the next update. Good luck.https://code-boxx.com/faq/#help
Hi, im looking why the input put me undefined instead 0.
Witch value i can force ?
Not quite sure what you mean here, I shall assume that you are trying to add a custom button to the keypad – It is nothing but a text input field, you can “force” any character.