Created
January 24, 2026 22:15
-
-
Save delaneyj/dbf5769add0fb36c1f56aa954862ea34 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class OtpInput extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: "open" }); | |
| this.values = []; | |
| } | |
| connectedCallback() { | |
| this.length = Number(this.getAttribute("length") || 6); | |
| this.values = Array(this.length).fill(""); | |
| this.render(); | |
| } | |
| render() { | |
| const inputs = Array.from({ length: this.length }, () => { | |
| return '<input type="number" inputmode="numeric" />'; | |
| }).join(""); | |
| this.shadowRoot.innerHTML = ` | |
| <div> | |
| <div id="inputs">${inputs}</div> | |
| <p>Entered Code: <span id="code"></span></p> | |
| </div> | |
| `; | |
| this.inputs = Array.from(this.shadowRoot.querySelectorAll("input")); | |
| const container = this.shadowRoot.getElementById("inputs"); | |
| container.addEventListener("keydown", (event) => this.handleKeydown(event)); | |
| container.addEventListener("paste", (event) => this.handlePaste(event)); | |
| } | |
| updateCode() { | |
| this.code = this.values.join(""); | |
| this.shadowRoot.getElementById("code").textContent = this.code; | |
| } | |
| handleKeydown(event) { | |
| const input = event.target; | |
| if (!input || input.tagName !== "INPUT" || event.ctrlKey || event.metaKey || event.altKey) return; | |
| const isDigit = /^[0-9]$/.test(event.key); | |
| if (!isDigit && event.key !== "Backspace" && event.key !== "ArrowLeft" && event.key !== "ArrowRight" && event.key !== "Tab") return; | |
| const index = this.inputs.indexOf(input); | |
| if (event.key === "ArrowLeft") { | |
| event.preventDefault(); | |
| const prevInput = input.previousElementSibling; | |
| if (prevInput && prevInput.tagName === "INPUT") prevInput.focus(); | |
| } else if (event.key === "ArrowRight") { | |
| event.preventDefault(); | |
| const nextInput = input.nextElementSibling; | |
| if (nextInput && nextInput.tagName === "INPUT") nextInput.focus(); | |
| } else if (event.key === "Tab") { | |
| return; | |
| } else if (event.key === "Backspace") { | |
| event.preventDefault(); | |
| input.value = ""; | |
| this.values[index] = ""; | |
| const prevInput = input.previousElementSibling; | |
| if (prevInput && prevInput.tagName === "INPUT") prevInput.focus(); | |
| this.updateCode(); | |
| } else if (isDigit) { | |
| event.preventDefault(); | |
| input.value = event.key; | |
| this.values[index] = event.key; | |
| const nextInput = input.nextElementSibling; | |
| if (nextInput && nextInput.tagName === "INPUT") { | |
| nextInput.value = ""; | |
| nextInput.focus(); | |
| } | |
| this.updateCode(); | |
| } | |
| } | |
| handlePaste(event) { | |
| const input = event.target; | |
| if (!input || input.tagName !== "INPUT") return; | |
| event.preventDefault(); | |
| const pasteData = (event.clipboardData || window.clipboardData).getData("text"); | |
| if (!/^\d+$/.test(pasteData)) return; | |
| const inputs = this.inputs; | |
| let pasteIndex = inputs.indexOf(input); | |
| for (let i = 0; i < pasteData.length && pasteIndex + i < inputs.length; i++) { | |
| inputs[pasteIndex + i].value = pasteData[i]; | |
| this.values[pasteIndex + i] = pasteData[i]; | |
| } | |
| this.updateCode(); | |
| const nextInput = inputs[pasteIndex + pasteData.length - 1]?.nextElementSibling; | |
| if (nextInput && nextInput.tagName === "INPUT") nextInput.focus(); | |
| } | |
| } | |
| if (!customElements.get("otp-input")) { | |
| customElements.define("otp-input", OtpInput); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <h3>Web Component (Minimal)</h3> | |
| <otp-input length="6"></otp-input> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment