OTP Input Field With HTML, CSS & JavaScript ( Download Code )

otp input field

Hey Programmers! In this post, we will make the OTP Input Field with pure HTML, CSS & JavaScript. OTP Input fields are often used in the web application when you have to authenticate the user with a mobile number or email.

We will explore topics like DOM manipulation, event handling, and form validation also we will understand how to handle user input dynamically.

Also Read: Cool Email Input Feild With HTML & CSS

We will understand important concepts used in the code step by step. At the end of this blog post, I will share the entire source code so that you can run this project on your local device.

Concepts Used To Make OTP Input Field

  1. CSS Flex Box & Box Model
  2. HTML Text Input Type With Maxlenght Attribute
  3. CSS Transitions
  4. JavaScript Prevent default, focus, Keyboard Events & Spread Operator

Understanding HTML To Make OTP Input Field

  1. <div class="relative font-inter antialiased">: This is the outermost container that applies some basic styles like font and anti-aliasing for smooth edges.
  2. <main class="main-container">: The main section of the page containing the form and its related content.
  3. <div class="content-container">: Centers the content within the main container.
  4. <div class="form-wrapper">: Wraps the form content for centering and styling purposes.
  5. <div class="form-content">: Contains the form header, form, and additional text.
  6. <header class="form-header">: Includes the title and subtitle of the form.
  7. <form id="otp-form">: The form for the OTP inputs and verification button.
  8. <div class="input-group">: Groups the OTP input fields.
  9. <input type="text" class="otp-input" pattern="\d*" maxlength="1" />: Four input fields for the OTP, each accepting one digit.
  10. <div class="button-container">: Contains the verification button.
  11. <button type="submit" class="verify-button">Verify Account</button>: Button to submit the form.
  12. <div class="resend-text">: Text for the resend link.<footer class="page-footer">: Footer containing a link to the source.
  13. <div class="banner" x-data="{ bannerOpen: true }">: A banner with additional links and a close button.

Understanding CSS To Make OTP Input Field

  1. main-container: Centers the main content vertically and horizontally with a full-height view and a light background.
  2. content-container: Sets a maximum width and centers the content within the container.
  3. form-wrapper: Centers the form horizontally.
  4. form-content: Styles the form with padding, background color, and a shadow for a card-like appearance.
  5. form-header: Adds spacing below the header elements.
  6. input-group: Aligns the OTP inputs horizontally with spacing between them.
  7. otp-input: Styles each input to be large, centered, and easy to read. The focus state highlights the active input.
  8. button-container: Centers the submit button below the inputs.
  9. verify-button: Styles the button with padding, color transitions, and a shadow.
  10. resend-text: Styles the text and link for resending the OTP.
  11. page-footer: Fixes the footer at the bottom with a link.
  12. banner: Adds a fixed banner at the bottom with links and a close button.

Understanding JavaScript To Make OTP Input Field

  1. document.addEventListener('DOMContentLoaded', () => {...}): Ensures the script runs after the DOM has fully loaded.
  2. const form = document.getElementById('otp-form'): Gets the form element.
  3. const inputs = [...form.querySelectorAll('input[type=text]')]: Selects all text input fields within the form.
  4. const submit = form.querySelector('button[type=submit]'): Selects the submit button.
  5. handleKeyDown: Prevents non-numeric characters, handles backspace/delete for moving focus.
  6. handleInput: Moves focus to the next input or the submit button when a digit is entered.
  7. handleFocus: Selects the text in the input when it gains focus.
  8. handlePaste: Allows pasting of a 4-digit code directly into the inputs.
  9. inputs.forEach((input) => {...}): Adds event listeners to each input for handling input, keydown, focus, and paste events.

Source Code Of OTP Input Field

HTML:

<div class="relative font-inter antialiased">
    <main class="main-container">
        <div class="content-container">
            <div class="form-wrapper">
                <div class="form-content">
                    <header class="form-header">
                        <h1 class="form-title">Mobile Phone Verification</h1>
                        <p class="form-subtitle">Enter the 4-digit verification code that was sent to your phone number.</p>
                    </header>
                    <form id="otp-form">
                        <div class="input-group">
                            <input type="text" class="otp-input" pattern="\d*" maxlength="1" />
                            <input type="text" class="otp-input" maxlength="1" />
                            <input type="text" class="otp-input" maxlength="1" />
                            <input type="text" class="otp-input" maxlength="1" />
                        </div>
                        <div class="button-container">
                            <button type="submit" class="verify-button">Verify Account</button>
                        </div>
                    </form>
                    <div class="resend-text">Didn't receive code? <a class="resend-link" href="#0">Resend</a></div>
                </div>
            </div>
        </div>
    </main>
    <footer class="page-footer">
        <a class="footer-link" href="https://cruip.com">&copy;Cruip - Tailwind CSS templates</a>
    </footer>
    <div class="banner" x-data="{ bannerOpen: true }">
        <div class="banner-content">
            <div class="banner-links">
                <a class="banner-link" href="https://cruip.com/otp-form-example-made-with-tailwind-css-and-javascript/" target="_blank">Read Tutorial</a>
                <span class="banner-separator">or</span>
                <a class="download-link" href="https://github.com/cruip/cruip-tutorials/blob/main/otp-form/index.html" target="_blank" rel="noreferrer">
                    <span>Download</span>
                    <svg class="download-icon" xmlns="http://www.w3.org/2000/svg" width="9" height="9">
                        <path d="m1.649 8.514-.91-.915 5.514-5.523H2.027l.01-1.258h6.388v6.394H7.158l.01-4.226z" />
                    </svg>
                </a>
            </div>
            <button class="close-banner" @click="bannerOpen = false">
                <span class="sr-only">Close</span>
                <svg class="close-icon" viewBox="0 0 16 16">
                    <path d="M12.72 3.293a1 1 0 00-1.415 0L8.012 6.586 4.72 3.293a1 1 0 00-1.414 1.414L6.598 8l-3.293 3.293a1 1 0 101.414 1.414l3.293-3.293 3.293 3.293a1 1 0 001.414-1.414L9.426 8l3.293-3.293a1 1 0 000-1.414z" />
                </svg>
            </button>
        </div>
    </div>
</div>

CSS:

<style>
    /* Main container styles */
.main-container {
    position: relative;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    background-color: #f8fafc;
    overflow: hidden;
}

/* Content container */
.content-container {
    width: 100%;
    max-width: 96rem;
    margin: 0 auto;
    padding: 6rem 1rem;
    padding-left: 1.5rem;
    padding-right: 1.5rem;
}

/* Form wrapper */
.form-wrapper {
    display: flex;
    justify-content: center;
}

/* Form content */
.form-content {
    max-width: 32rem;
    margin: 0 auto;
    text-align: center;
    background-color: #fff;
    padding: 2.5rem 1rem;
    padding-left: 2rem;
    padding-right: 2rem;
    border-radius: 0.75rem;
    box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
}

/* Form header */
.form-header {
    margin-bottom: 2rem;
}

/* Form title */
.form-title {
    font-size: 2rem;
    font-weight: bold;
    margin-bottom: 0.25rem;
}

/* Form subtitle */
.form-subtitle {
    font-size: 0.9375rem;
    color: #64748b;
}

/* Input group */
.input-group {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
}

/* OTP input */
.otp-input {
    width: 3.5rem;
    height: 3.5rem;
    text-align: center;
    font-size: 2rem;
    font-weight: 800;
    color: #0f172a;
    background-color: #f1f5f9;
    border: none;
    border-radius: 0.375rem;
    padding: 1rem;
    outline: none;
    appearance: none;
    transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
}

.otp-input:hover {
    border: 1px solid #e2e8f0;
}

.otp-input:focus {
    background-color: #fff;
    border: 1px solid #6366f1;
    box-shadow: 0px 0px 0px 2px rgba(99, 102, 241, 0.25);
}

/* Button container */
.button-container {
    max-width: 16.25rem;
    margin: 1rem auto 0;
}

/* Verify button */
.verify-button {
    width: 100%;
    display: inline-flex;
    justify-content: center;
    white-space: nowrap;
    border-radius: 0.5rem;
    background-color: #6366f1;
    padding: 0.625rem 0.875rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: #fff;
    box-shadow: 0px 2px 4px rgba(99, 102, 241, 0.1);
    transition: background-color 0.15s ease-in-out;
    outline: none;
}

.verify-button:hover {
    background-color: #4f46e5;
}

.verify-button:focus {
    outline: none;
    box-shadow: 0px 0px 0px 2px rgba(99, 102, 241, 0.3);
}

/* Resend text */
.resend-text {
    font-size: 0.875rem;
    color: #64748b;
    margin-top: 1rem;
}

.resend-link {
    font-weight: 500;
    color: #6366f1;
    text-decoration: none;
    transition: color 0.15s ease-in-out;
}

.resend-link:hover {
    color: #4f46e5;
}

/* Page footer */
.page-footer {
    position: absolute;
    left: 1.5rem;
    right: 1.5rem;
    bottom: 1rem;
    text-align: center;
}

.page-footer a {
    font-size: 0.75rem;
    color: #64748b;
    text-decoration: none;
}

.page-footer a:hover {
    text-decoration: underline;
}

/* Banner */
.banner {
    position: fixed;
    bottom: 0;
    right: 0;
    width: 100%;
    max-width: auto;
    z-index: 50;
    padding: 0 1.5rem;
}

.banner-content {
    background-color: #1e293b;
    font-size: 0.875rem;
    padding: 0.75rem;
    border-radius: 0.375rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
}

/* Banner links */
.banner-links {
    display: inline-flex;
    align-items: center;
    color: #94a3b8;
}

.banner-link {
    font-weight: 500;
    color: #cbd5e1;
    text-decoration: none;
}

.banner-link:hover {
    text-decoration: underline;
}

.banner-separator {
    font-style: italic;
    padding: 0 0.375rem;
}

.download-link {
    font-weight: 500;
    color: #6366f1;
    display: flex;
    align-items: center;
    text-decoration: none;
}

.download-link span {
    margin-right: 0.25rem;
}

.download-icon {
    fill: #6366f1;
}

/* Close banner */
.close-banner {
    color: #94a3b8;
    display: flex;
    align-items: center;
    border-left: 1px solid #334155;
    padding-left: 0.5rem;
    margin-left: 0.75rem;
}

.close-banner:hover {
    color: #e2e8f0;
}

.close-icon {
    width: 1rem;
    height: 1rem;
    fill: currentColor;
}

</style>

JavaScript:

<script>
    document.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('otp-form')
    const inputs = [...form.querySelectorAll('input[type=text]')]
    const submit = form.querySelector('button[type=submit]')

    const handleKeyDown = (e) => {
        if (
            !/^[0-9]{1}$/.test(e.key)
            && e.key !== 'Backspace'
            && e.key !== 'Delete'
            && e.key !== 'Tab'
            && !e.metaKey
        ) {
            e.preventDefault()
        }

        if (e.key === 'Delete' || e.key === 'Backspace') {
            const index = inputs.indexOf(e.target);
            if (index > 0) {
                inputs[index - 1].value = '';
                inputs[index - 1].focus();
            }
        }
    }

    const handleInput = (e) => {
        const { target } = e
        const index = inputs.indexOf(target)
        if (target.value) {
            if (index < inputs.length - 1) {
                inputs[index + 1].focus()
            } else {
                submit.focus()
            }
        }
    }

    const handleFocus = (e) => {
        e.target.select()
    }

    const handlePaste = (e) => {
        e.preventDefault()
        const text = e.clipboardData.getData('text')
        if (!new RegExp(`^[0-9]{${inputs.length}}$`).test(text)) {
            return
        }
        const digits = text.split('')
        inputs.forEach((input, index) => input.value = digits[index])
        submit.focus()
    }

    inputs.forEach((input) => {
        input.addEventListener('input', handleInput)
        input.addEventListener('keydown', handleKeyDown)
        input.addEventListener('focus', handleFocus)
        input.addEventListener('paste', handlePaste)
    })
})  
</script>

Last Updated: June 18, 2024

By JSM Hemant

About Author

Hello, Myself Hemant. I Am Web Developer & Likes To Create Awesome Projects By Using MERN, Java, Python, C++. In This Website You Will Get Cool Projects Made With Above Technologies With Full Source Code. You Can Also Follow This Website On Author Social Media:

Leave a Reply

Your email address will not be published. Required fields are marked *

Categories

Recent Posts