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.