'How to partition input field to appear as separate input fields on screen?

Look at the image:

enter image description here

I want design something like in the image, where a 4 digit one time password (OTP) is to be entered by user. Right now I have achieved this by 4 separate inputs and then combining values in javascript:

<input type="text" class="form-control" placeholder="0" maxlength="1"  />
<input type="text" class="form-control" placeholder="0" maxlength="1" />
<input type="text" class="form-control" placeholder="0" maxlength="1" />
<input type="text" class="form-control" placeholder="0" maxlength="1" />

I am not sure if this is correct approach. I think there must be some styling options by which one input textbox would appear as partitioned one like in the image. Is it possible using bootstrap? How to style one input control to be appeared as partitioned field of inputs?



Solution 1:[1]

You dont have to keep 4 separate fields;

First you should adjust the character spacing, and than adjust border style of bottom...

#partitioned {
  padding-left: 15px;
  letter-spacing: 42px;
  border: 0;
  background-image: linear-gradient(to left, black 70%, rgba(255, 255, 255, 0) 0%);
  background-position: bottom;
  background-size: 50px 1px;
  background-repeat: repeat-x;
  background-position-x: 35px;
  width: 220px;
  outline : none;
}
<input id="partitioned" type="text" maxlength="4" />

--EDIT to fix 5 underlines for 4 character ugliness--

var obj = document.getElementById('partitioned');
obj.addEventListener('keydown', stopCarret); 
obj.addEventListener('keyup', stopCarret); 

function stopCarret() {
    if (obj.value.length > 3){
        setCaretPosition(obj, 3);
    }
}

function setCaretPosition(elem, caretPos) {
    if(elem != null) {
        if(elem.createTextRange) {
            var range = elem.createTextRange();
            range.move('character', caretPos);
            range.select();
        }
        else {
            if(elem.selectionStart) {
                elem.focus();
                elem.setSelectionRange(caretPos, caretPos);
            }
            else
                elem.focus();
        }
    }
}
#partitioned {
  padding-left: 15px;
  letter-spacing: 42px;
  border: 0;
  background-image: linear-gradient(to left, black 70%, rgba(255, 255, 255, 0) 0%);
  background-position: bottom;
  background-size: 50px 1px;
  background-repeat: repeat-x;
  background-position-x: 35px;
  width: 220px;
  min-width: 220px;
}

#divInner{
  left: 0;
  position: sticky;
}

#divOuter{
  width: 190px; 
  overflow: hidden;
}
<div id="divOuter">
    <div id="divInner">
        <input id="partitioned" type="text" maxlength="4" />
    </div>
</div>

I think this can be a starting point... hope this will help...

Solution 2:[2]

I know this question is old. I stumbled upon this when I was trying to achieve the same for my project.

I couldn't find what I needed. So I made one myself with the ideas from here

You can use this with any number of characters and needs no change in javascript as long as all the input fields are inside a div with id="otp".


Here is a preview:

enter image description here


Take a look at the code here

function OTPInput() {
  const inputs = document.querySelectorAll('#otp > *[id]');
  for (let i = 0; i < inputs.length; i++) {
    inputs[i].addEventListener('keydown', function(event) {
      if (event.key === "Backspace") {
        inputs[i].value = '';
        if (i !== 0)
          inputs[i - 1].focus();
      } else {
        if (i === inputs.length - 1 && inputs[i].value !== '') {
          return true;
        } else if (event.keyCode > 47 && event.keyCode < 58) {
          inputs[i].value = event.key;
          if (i !== inputs.length - 1)
            inputs[i + 1].focus();
          event.preventDefault();
        } else if (event.keyCode > 64 && event.keyCode < 91) {
          inputs[i].value = String.fromCharCode(event.keyCode);
          if (i !== inputs.length - 1)
            inputs[i + 1].focus();
          event.preventDefault();
        }
      }
    });
  }
}
OTPInput();
@import url('https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css');
.form-control {
  -webkit-transition: none;
  transition: none;
  width: 32px;
  height: 32px;
  text-align: center
}

.form-control:focus {
  color: #3F4254;
  background-color: #ffffff;
  border-color: #884377;
  outline: 0;
}

.form-control.form-control-solid {
  background-color: #F3F6F9;
  border-color: #F3F6F9;
  color: #3F4254;
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.form-control.form-control-solid:active,
.form-control.form-control-solid.active,
.form-control.form-control-solid:focus,
.form-control.form-control-solid.focus {
  background-color: #EBEDF3;
  border-color: #EBEDF3;
  color: #3F4254;
  transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
<div class="mb-6 text-center">
  <div id="otp" class="flex justify-center">
    <input class="m-2 text-center form-control form-control-solid rounded focus:border-blue-400 focus:shadow-outline" type="text" id="first" maxlength="1" />
    <input class="m-2 text-center form-control form-control-solid rounded focus:border-blue-400 focus:shadow-outline" type="text" id="second" maxlength="1" />
    <input class="m-2 text-center form-control form-control-solid rounded focus:border-blue-400 focus:shadow-outline" type="text" id="third" maxlength="1" />
    <input class="m-2 text-center form-control form-control-solid rounded focus:border-blue-400 focus:shadow-outline" type="text" id="fourth" maxlength="1" />
    <input class="m-2 text-center form-control form-control-solid rounded focus:border-blue-400 focus:shadow-outline" type="text" id="fifth" maxlength="1" />
    <input class="m-2 text-center form-control form-control-solid rounded focus:border-blue-400 focus:shadow-outline" type="text" id="sixth" maxlength="1" />
  </div>
</div>

Solution 3:[3]

This works for me without any extra javascript code.

#partitioned {
  padding-left: 15px;
  letter-spacing: 42px;
  border: 0;
  background-image: linear-gradient(to left, black 70%, rgba(255, 255, 255, 0) 0%);
  background-position: bottom;
  background-size: 50px 1px;
  background-repeat: repeat-x;
  background-position-x: 35px;
  width: 220px;
  min-width: 220px;
}

#divInner{
  left: 0;
  position: sticky;
}

#divOuter{
  width: 190px; 
  overflow: hidden;
}
<div id="divOuter">
  <div id="divInner">
<input id="partitioned" type="text" maxlength="4" oninput="this.value = this.value.replace(/[^0-9.]/g, '').replace(/(\..*)\./g, '$1');"  onKeyPress="if(this.value.length==4) return false;"/>
  </div>
</div>

Solution 4:[4]

Hope this solution helps you. You can remove onfocus event from input elements if you want.

<body>
  <head>
    <style>
      input[type=number] {
          height: 45px;
          width: 45px;
          font-size: 25px;
          text-align: center;
          border: 1px solid #000000;
      }
      input[type=number]::-webkit-inner-spin-button,
      input[type=number]::-webkit-outer-spin-button {
        -webkit-appearance: none;
        margin: 0;
      }
    </style>
    <script>
      function getCodeBoxElement(index) {
        return document.getElementById('codeBox' + index);
      }
      function onKeyUpEvent(index, event) {
        const eventCode = event.which || event.keyCode;
        if (getCodeBoxElement(index).value.length === 1) {
          if (index !== 4) {
            getCodeBoxElement(index+ 1).focus();
          } else {
            getCodeBoxElement(index).blur();
            // Submit code
            console.log('submit code ');
          }
        }
        if (eventCode === 8 && index !== 1) {
          getCodeBoxElement(index - 1).focus();
        }
      }
      function onFocusEvent(index) {
        for (item = 1; item < index; item++) {
          const currentElement = getCodeBoxElement(item);
          if (!currentElement.value) {
              currentElement.focus();
              break;
          }
        }
      }
    </script>
  </head>
  <body>
    <form>
        <input id="codeBox1" type="number" maxlength="1" onkeyup="onKeyUpEvent(1, event)" onfocus="onFocusEvent(1)"/>
        <input id="codeBox2" type="number" maxlength="1" onkeyup="onKeyUpEvent(2, event)" onfocus="onFocusEvent(2)"/>
        <input id="codeBox3" type="number" maxlength="1" onkeyup="onKeyUpEvent(3, event)" onfocus="onFocusEvent(3)"/>
        <input id="codeBox4" type="number" maxlength="1" onkeyup="onKeyUpEvent(4, event)" onfocus="onFocusEvent(4)"/>
    </form>
  </body>
</body>

Solution 5:[5]

I would just keep this 4 separate fields approach, and add the same event handler to all of them, which will:

  1. Check whether the input is valid (in the character class, you're willing to accept)
  2. Check which field you are at, and then move the focus to the next field, or the ok button.

You can even write a little separate JS for that, and re-use it.

Solution 6:[6]

You can use the below directive if using AngularJS

In your HTML add

<div otp-input-directive options="otpInput"></div>

In your controller add

$scope.otpInput={
        size:6,
        type:"text",
        onDone: function(value){
            console.log(value);
        },
        onChange: function(value){
            console.log(value);
        }
    };

Plunker link

UI component look and feel

https://github.com/amstel91/otp-input-directive

Solution 7:[7]

If your using input type number, then use below code, It's Working


#otpOuterBlock{
  width: 190px; 
  overflow: hidden;
}
#otpInnerBlock{
  left: 0;
  position: sticky;
}

#otpVerifyText{
  padding-left: 15px;
  letter-spacing: 42px;
  border: 0;
  background-image: linear-gradient(to left, black 70%, rgba(255, 255, 255, 0) 0%);
  background-position: bottom;
  background-size: 50px 1px;
  background-repeat: repeat-x;
  background-position-x: 35px;
  width: 220px;
  min-width: 220px;
}

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}


input[type=number] {
    -moz-appearance: textfield;
}



<div id="otpOuterBlock">
    <div id="otpInnerBlock">
        <input id="otpVerifyText" type="number" maxlength="4" />
    </div>
</div>

Solution 8:[8]

Just changed Midhun Monachan's snippet to add copy-paste feature.

document.addEventListener("DOMContentLoaded", function(event) {
   
    function OTPInput() {
        const editor = document.getElementById('first');
        editor.onpaste = pasteOTP;

        const inputs = document.querySelectorAll('#otp > *[id]');
        for (let i = 0; i < inputs.length; i++) { 
            inputs[i].addEventListener('input', function(event) { 
                if(!event.target.value || event.target.value == '' ){
                    if(event.target.previousSibling.previousSibling){
                        event.target.previousSibling.previousSibling.focus();    
                    }
                
                }else{ 
                    if(event.target.nextSibling.nextSibling){
                        event.target.nextSibling.nextSibling.focus();
                    }
                }               
            });             
        } 
    } 
    OTPInput(); 
});

function pasteOTP(event){
    event.preventDefault();
    let elm = event.target;
    let pasteVal = event.clipboardData.getData('text').split("");
    if(pasteVal.length > 0){
        while(elm){
            elm.value = pasteVal.shift();
            elm = elm.nextSibling.nextSibling;
        }
    }
}
input {
text-align: center!important;
    display: block;
    width: 100%;
    height: calc(1.5em + 0.75rem + 2px);
    padding: 0.375rem 0.75rem;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid #ced4da;
    border-radius: 0.25rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
        border-radius: 0.25rem !important;
            margin: 0.5rem !important;
                overflow: visible;
                word-wrap: break-word;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap-grid.min.css" integrity="sha512-Xj2sd25G+JgJYwo0cvlGWXoUekbLg5WvW+VbItCMdXrDoZRWcS/d40ieFHu77vP0dF5PK+cX6TIp+DsPfZomhw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<form id="otpForm" >
              <div class="container height-100 d-flex justify-content-center align-items-center">
                  <div class="position-relative">
                      <div class="card p-2 text-center">
                          
                          <div id="otp" class="inputs d-flex flex-row justify-content-center mt-2"> 
                            <input class="m-2 text-center form-control rounded" type="text" id="first" maxlength="1" /> 
                            <input class="m-2 text-center form-control rounded" type="text" id="second" maxlength="1" /> 
                            <input class="m-2 text-center form-control rounded" type="text" id="third" maxlength="1" /> 
                            <input class="m-2 text-center form-control rounded" type="text" id="fourth" maxlength="1" /> 
                            <input class="m-2 text-center form-control rounded" type="text" id="fifth" maxlength="1" /> 
                            <input class="m-2 text-center form-control rounded" type="text" id="sixth" maxlength="1" /> 
                          </div>
                         
                      </div>                    
                  </div>
              </div>
            </form>

Solution 9:[9]

I don't know how to split an input in html5, maybe in css you can use the same class to controll your input and you can style input, something like :

div{
   text-align:center;
   background:#eee;
}
input{
    border: 0;
    outline: 0;
    background: transparent;
    border-bottom: 2px solid black;
    width: 100px;
    text-align:center;
    padding : 5px;
    margin-left:10px;
}
button{
  margin-top:20px !important;
  margin: 0 auto;
  color: white;
  border-radius: 4px;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
<div>
    <input class="form-control" placeholder="0" maxlength="1"  />
    <input class="form-control" placeholder="0" maxlength="1"  />
    <input class="form-control" placeholder="0" maxlength="1"  />
    <input class="form-control" placeholder="0" maxlength="1"  />
    <br><button type="button" onclick="myFunction()">Submit</button>
    <p id="optRes"></p>
</div>    

Define myFunction() and You can get your array by your class : form-control, convert to string then into int if you have to check it . Helps this ?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mohammad reza Golshahi
Solution 2 Midhun Monachan
Solution 3
Solution 4
Solution 5 Dan
Solution 6
Solution 7 Pushkar Dayal
Solution 8
Solution 9 Teshtek