Skip to content

Instantly share code, notes, and snippets.

@r8928
Created December 22, 2022 08:08
Show Gist options
  • Save r8928/b2bcff51668df67cca5cb26d3d63fb32 to your computer and use it in GitHub Desktop.
Save r8928/b2bcff51668df67cca5cb26d3d63fb32 to your computer and use it in GitHub Desktop.
Webcomponent to take OTP code
(() => {
class OtpInput extends HTMLElement {
static formAssociated = true;
inputs = [];
element;
otp_length = 6;
value_ = "";
constructor() {
super();
this.internals = this.attachInternals();
const shadowRoot = this.attachShadow({
mode: "open",
});
shadowRoot.innerHTML = `
<style>
div {
display: flex;
justify-content: center;
gap: 10px;
}
.otp-field {
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
vertical-align: middle;
border: 1px solid #eee
}
.otp-field:focus {
outline: 2px solid black;
border-radius: 5px;
}
</style>
<div></div>`;
}
connectedCallback() {
if (Number(this.getAttribute("otp-length")) > 0) {
this.otp_length = Number(this.getAttribute("otp-length"));
}
this.element = this.shadowRoot.querySelector(`div`);
for (let i = 0; i < this.otp_length; i++) {
let inputField = document.createElement("input");
inputField.id = "otp-field" + i;
inputField.setAttribute("class", "otp-field");
inputField.maxLength = 1;
this.element.appendChild(inputField);
this.inputs.push(inputField);
}
this.inputs.forEach((i) => {
i.addEventListener("paste", (event) => this.onPaste(event, i));
i.addEventListener("keydown", (event) => this.onKeydown(event, i));
i.addEventListener("input", (event) => this.onInput(event, i));
});
if (this.getAttribute("value")) {
this.value = this.getAttribute("value");
}
}
onPaste = (event, i) => {
event.preventDefault();
let elm = i;
let value = this.sanitizeInput(event.clipboardData.getData("text"));
while (value.length > 0 && elm) {
elm.value = value.shift();
if (!elm.nextSibling || !value.length) {
elm.focus();
}
elm = elm.nextSibling;
}
this.updateValue();
};
onKeydown = (event, i) => {
if (event.key === "Backspace") {
i.value = "";
i.previousSibling && i.previousSibling.focus();
event.preventDefault();
} else if (event.key === "ArrowLeft" && i !== 0) {
i.previousSibling && i.previousSibling.focus();
} else if (event.key === "ArrowRight" && i !== this.otp_length - 1) {
i.nextSibling && i.nextSibling.focus();
} else if (
["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(event.key)
) {
i.setAttribute("type", "text");
i.value = event.key;
setTimeout(() => {
i.nextSibling && i.nextSibling.focus();
});
} else if ("v" == event.key && event.ctrlKey) {
} else if (!["Tab"].includes(event.key)) {
if ("preventDefault" in event) {
event.preventDefault();
}
}
this.updateValue();
};
onInput = (event, i) => {
event.preventDefault();
return false;
};
get value() {
return this.value_;
}
set value(value) {
value = this.sanitizeInput(value);
for (let i = 0; i < this.otp_length; i++) {
this.onKeydown(
{
key: value[i],
},
this.inputs[i]
);
}
}
updateValue() {
const value = this.inputs.reduce(
(carry, item) => (carry += item.value),
""
);
if (value.length === this.otp_length) {
this.value_ = value;
this.internals.setFormValue(this.value_);
} else {
this.internals.setFormValue("");
}
}
sanitizeInput(value) {
value = String(value).replace(/\D+/g, "").split("");
return value.slice(0, this.otp_length);
}
get form() {
return this.internals.form;
}
get name() {
return this.getAttribute("name");
}
get id() {
return this.getAttribute("id");
}
get type() {
return this.localName;
}
}
customElements.define("otp-input", OtpInput);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment