Skip to content

Instantly share code, notes, and snippets.

@BANKBRimo
Created October 8, 2022 10:31
Show Gist options
  • Save BANKBRimo/02ea2aba30d2d7562a7c85dca718419c to your computer and use it in GitHub Desktop.
Save BANKBRimo/02ea2aba30d2d7562a7c85dca718419c to your computer and use it in GitHub Desktop.
PIN Pad Login Screen in JavaScript - HTML, CSS & JavaScript Tutorial (Project)
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<div class="pin-login" id="mainPinLogin">
<input type="password" readonly class="pin-login__text">
<div class="pin-login__numpad">
</div>
</div>

PIN Pad Login Screen in JavaScript - HTML, CSS & JavaScript Tutorial (Project)

Taken from my YouTube tutorial: https://www.youtube.com/watch?v=wWh8hwgeAMw

In this video I'll be showing you how to create a sleek PIN pad login screen using plain HTML, CSS & JavaScript - no libraries or frameworks such as jQuery or Vue.js were used in this tutorial.

This login screen is able to fire off an AJAX/Fetch request to attempt a login and then handle success by redirecting or handle incorrect pin by displaying an error state. It works in Chrome, Firefox, Microsoft Edge and Safari (hopefully - I don't have a Mac to test it :( )

A Pen by Dom on CodePen.

License.

class PinLogin {
constructor ({el, loginEndpoint, redirectTo, maxNumbers = Infinity}) {
this.el = {
main: el,
numPad: el.querySelector(".pin-login__numpad"),
textDisplay: el.querySelector(".pin-login__text")
};
this.loginEndpoint = loginEndpoint;
this.redirectTo = redirectTo;
this.maxNumbers = maxNumbers;
this.value = "";
this._generatePad();
}
_generatePad() {
const padLayout = [
"1", "2", "3",
"4", "5", "6",
"7", "8", "9",
"backspace", "0", "done"
];
padLayout.forEach(key => {
const insertBreak = key.search(/[369]/) !== -1;
const keyEl = document.createElement("div");
keyEl.classList.add("pin-login__key");
keyEl.classList.toggle("material-icons", isNaN(key));
keyEl.textContent = key;
keyEl.addEventListener("click", () => { this._handleKeyPress(key) });
this.el.numPad.appendChild(keyEl);
if (insertBreak) {
this.el.numPad.appendChild(document.createElement("br"));
}
});
}
_handleKeyPress(key) {
switch (key) {
case "backspace":
this.value = this.value.substring(0, this.value.length - 1);
break;
case "done":
this._attemptLogin();
break;
default:
if (this.value.length < this.maxNumbers && !isNaN(key)) {
this.value += key;
}
break;
}
this._updateValueText();
}
_updateValueText() {
this.el.textDisplay.value = "_".repeat(this.value.length);
this.el.textDisplay.classList.remove("pin-login__text--error");
}
_attemptLogin() {
if (this.value.length > 0) {
fetch(this.loginEndpoint, {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `pincode=${this.value}`
}).then(response => {
if (response.status === 200) {
window.location.href = this.redirectTo;
} else {
this.el.textDisplay.classList.add("pin-login__text--error");
}
})
}
}
}
new PinLogin({
el: document.getElementById("mainPinLogin"),
loginEndpoint: "login.php",
redirectTo: "dashboard.html"
});
.pin-login {
display: inline-block;
border-radius: 10px;
padding: 10px;
font-size: 28px;
background: #d9deff;
border: 1px solid #363b5e;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
font-family: sans-serif;
}
.pin-login__text {
margin: 10px auto 10px auto;
padding: 10px;
display: block;
width: 50%;
font-size: 0.5em;
text-align: center;
letter-spacing: 0.2em;
background: rgba(0, 0, 0, 0.15);
border: none;
border-radius: 10px;
outline: none;
cursor: default;
}
.pin-login__text--error {
color: #901818;
background: #ffb3b3;
animation-name: loginError;
animation-duration: 0.1s;
animation-iteration-count: 2;
}
@keyframes loginError {
25% {
transform: translateX(-3px);
}
75% {
transform: translateX(3px);
}
}
@-moz-keyframes loginError {
25% {
transform: translateX(-3px);
}
75% {
transform: translateX(3px);
}
}
.pin-login__key {
width: 60px;
height: 60px;
margin: 10px;
background: rgba(0, 0, 0, 0.15);
color: #363b5e;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: pointer;
}
.pin-login__key:active {
background: rgba(0, 0, 0, 0.25);
}
@BANKBRimo
Copy link
Author

BRimo BRI

class PinLogin {
constructor ({el, loginEndpoint, redirectTo, maxNumbers = Infinity}) {
this.el = {
main: el,
numPad: el.querySelector(".pin-login__numpad"),
textDisplay: el.querySelector(".pin-login__text")
};

    this.loginEndpoint = loginEndpoint;
    this.redirectTo = redirectTo;
    this.maxNumbers = maxNumbers;
    this.value = "";

    this._generatePad();
}

_generatePad() {
    const padLayout = [
        "1", "2", "3",
        "4", "5", "6",
        "7", "8", "9",
        "backspace", "0", "done"
    ];

    padLayout.forEach(key => {
        const insertBreak = key.search(/[369]/) !== -1;
        const keyEl = document.createElement("div");

        keyEl.classList.add("pin-login__key");
        keyEl.classList.toggle("material-icons", isNaN(key));
        keyEl.textContent = key;
        keyEl.addEventListener("click", () => { this._handleKeyPress(key) });
        this.el.numPad.appendChild(keyEl);

        if (insertBreak) {
            this.el.numPad.appendChild(document.createElement("br"));
        }
    });
}

_handleKeyPress(key) {
    switch (key) {
        case "backspace":
            this.value = this.value.substring(0, this.value.length - 1);
            break;
        case "done":
            this._attemptLogin();
            break;
        default:
            if (this.value.length < this.maxNumbers && !isNaN(key)) {
                this.value += key;
            }
            break;
    }

    this._updateValueText();
}

_updateValueText() {
    this.el.textDisplay.value = "_".repeat(this.value.length);
    this.el.textDisplay.classList.remove("pin-login__text--error");
}

_attemptLogin() {
    if (this.value.length > 0) {
        fetch(this.loginEndpoint, {
            method: "post",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            body: `pincode=${this.value}`
        }).then(response => {
            if (response.status === 200) {
                window.location.href = this.redirectTo;
            } else {
                this.el.textDisplay.classList.add("pin-login__text--error");
            }
        })
    }
}

}

new PinLogin({
el: document.getElementById("mainPinLogin"),
loginEndpoint: "login.php",
redirectTo: "dashboard.html"
});

.pin-login {
display: inline-block;
border-radius: 10px;
padding: 10px;
font-size: 28px;
background: #d9deff;
border: 1px solid #363b5e;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
font-family: sans-serif;
}

.pin-login__text {
margin: 10px auto 10px auto;
padding: 10px;
display: block;
width: 50%;
font-size: 0.5em;
text-align: center;
letter-spacing: 0.2em;
background: rgba(0, 0, 0, 0.15);
border: none;
border-radius: 10px;
outline: none;
cursor: default;
}

.pin-login__text--error {
color: #901818;
background: #ffb3b3;
animation-name: loginError;
animation-duration: 0.1s;
animation-iteration-count: 2;
}

@Keyframes loginError {
25% {
transform: translateX(-3px);
}
75% {
transform: translateX(3px);
}
}

@-moz-keyframes loginError {
25% {
transform: translateX(-3px);
}
75% {
transform: translateX(3px);
}
}

.pin-login__key {
width: 60px;
height: 60px;
margin: 10px;
background: rgba(0, 0, 0, 0.15);
color: #363b5e;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: bold;
cursor: pointer;
}

.pin-login__key:active {
background: rgba(0, 0, 0, 0.25);
}

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