Home > Enterprise >  How to create a 4 digit input series without JavaScript?
How to create a 4 digit input series without JavaScript?

Time:03-07

I have a form input in which I want to enter a four-digit number (verification code). My problem is that after entering the fourth number, the structure and order of the PIN-code breaks down. Because the text pointer goes to the fifth character while I have defined four characters.

Is there a way to solve this problem with pure CSS? Or at least with pure JavaScript?

.pinBox {
  display: inline-block;
  overflow: hidden;
  position: relative;
  direction: ltr;
  text-align: left;
}

.pinBox:before {
  position: absolute;
  left: 0;
  right: 0;
  content: '';
  pointer-events: none;
  display: block;
  height: 75px;
  width: 300px;
  background-image: url(https://i.stack.imgur.com/JbkZl.png);
}

.pinEntry {
  position: relative;
  padding: 16px 29px;
  font-family: courier, monospaced;
  font-size: xx-large;
  border: none;
  outline: none;
  width: 302px;
  letter-spacing: 55px;
  background-color: transparent;
  overflow: hidden;
  text-align: left;
  direction: ltr;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.1/css/bootstrap.css"/>

<form role="form" method="POST" action="">
  <div >
    <div >
      <div >
        <input  name="token" type="text" maxlength="4" value="">
      </div>
    </div>
  </div>

  <div >
    <button type="submit" >submit</button>
  </div>
</form>

here is a jsfidde demo

CodePudding user response:

After a bit of investigating I realized that strangely, by assigning overflow: hidden; to the parent element resulted in the input to hop/move contextual X position as soon the 4th value was inserted.

Solution:

  • Use CSS clip on the <input> element!
  • Assign the squared grid as the background-image of your parent element (no need to use ::before pseudo elements!)

.pinBox {
  --width: 296px;
  --height: 74px;
  --spacing: 47px;
  
  display: inline-block;
  position: relative;
  width: var(--width);
  height: var(--height);
  background-image: url(https://i.stack.imgur.com/JbkZl.png); 
}

.pinEntry {
  position: absolute;
  padding-left: 21px;
  font-family: courier, monospaced;
  font-size: var(--spacing);
  height: var(--height);
  letter-spacing: var(--spacing);
  background-color: transparent;
  border: 0;
  outline: none;
  clip: rect(0px, calc(var(--width) - 21px), var(--height), 0px);
}
<div >
  <input  name="token" type=text maxlength=4 autocomplete=off >
</div>

One cons of the above is that: once all four values are inputted, and if the user clicks after the fourth value - the caret will not be visible since it's clipped (letter-spaced inside the 5th position). It you can live with it - good, but it's a bad UX/UI in my opinion.

Perhaps you can add to the above some really small JS that does:

  • IF the input has 4 values length - select all the text-value using myInput.select()

Example:

const ELS_pinEntry = document.querySelectorAll(".pinEntry");
const selectAllIfFull = (evt) => {
  const EL_input = evt.currentTarget;
  if (EL_input.value.length >= 4) EL_input.select();
};
ELS_pinEntry.forEach(el => {
  el.addEventListener("focusin", selectAllIfFull);
});
.pinBox {
  --width: 296px;
  --height: 74px;
  --spacing: 47px;
  
  display: inline-block;
  position: relative;
  width: var(--width);
  height: var(--height);
  background-image: url(https://i.stack.imgur.com/JbkZl.png); 
}

.pinEntry {
  position: absolute;
  padding-left: 21px;
  font-family: courier, monospaced;
  font-size: var(--spacing);
  height: var(--height);
  letter-spacing: var(--spacing);
  background-color: transparent;
  border: 0;
  outline: none;
  clip: rect(0px, calc(var(--width) - 21px), var(--height), 0px);
}
<div >
  <input  name="token" type=text maxlength=4 autocomplete=off >
</div>

Another cons I would improve by using the above (and the original) idea is: A11Y (Accessibility).
The many users with bad or challenged sight should be able to see an input outline while they tab trough a website. For design reasons the above had to remove the CSS outline on the INPUT element. Which is a no-no.

Currently I have no other better/simpler idea but to use 4 different inputs and join their values into a single hidden one that will ultimately be submitted with the FORM.

  • Related