如何在没有 JavaScript 的情况下创建 4 位输入序列?

How to create a 4 digit input series without JavaScript?

我有一个表格输入,我想在其中输入一个四位数字(验证码)。我的问题是,在输入第四个数字后,PIN 码的结构和顺序发生了故障。因为当我定义了四个字符时,文本指针指向第五个字符。

有没有办法用纯CSS解决这个问题?或者至少用纯 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 class="row my-4">
    <div class="col-12 text-center">
      <div class="pinBox">
        <input class="pinEntry" name="token" type="text" maxlength="4" value="">
      </div>
    </div>
  </div>

  <div class="text-center">
    <button type="submit" class="btn btn-primary mt-2">submit</button>
  </div>
</form>

这是一个jsfidde demo

经过一番调查后,我意识到奇怪的是,通过将 overflow: hidden; 分配给父元素,一旦插入第 4 个值,就会导致输入到 hop/move 上下文 X 位置。

解决方案:

  • <input> 元素上使用 CSS clip
  • 将方形网格指定为父元素的 background-image(无需使用 ::before 伪元素!)

.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 class="pinBox">
  <input class="pinEntry" name="token" type=text maxlength=4 autocomplete=off >
</div>

上面的一个缺点是:一旦输入了所有四个值,并且如果用户单击在第四个值 之后 - 插入符号将不可见,因为它被 clipped (letter-spaced 在第 5 个位置内)。你可以忍受它 - 很好,但在我看来它很糟糕 UX/UI。

也许你可以在上面添加一些非常小的 JS:

  • 如果输入有 4 个值长度 - select 所有 text-value 使用 myInput.select()

示例:

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 class="pinBox">
  <input class="pinEntry" name="token" type=text maxlength=4 autocomplete=off >
</div>

另一个 cons 我会通过使用上面的(和原来的)想法来改进:A11Y (Accessibility).
许多视力不好或有问题的用户在浏览网站时应该能够看到输入 outline。出于设计原因,上面必须删除 INPUT 元素上的 CSS outline。这是一个 no-no.

目前我没有其他 better/simpler 想法,只能使用 4 个不同的输入 并将它们的值连接到一个隐藏的最终将与 FORM 一起提交。