单个input无法满足验证码逐位输入、光标定位、视觉对齐及删除逻辑等需求;可行方案是多个maxlength="1"的独立input,配合JS控制焦点跳转、删除回退、粘贴处理及无障碍支持。

验证码输入框为什么不能用单个 <input type="text">
因为用户习惯逐位输入、光标定位困难、无法对齐视觉验证码格子,且移动端容易触发全选粘贴导致覆盖全部内容。单输入框还让「删除一位」操作变得反直觉——按一次 Delete 或 Backspace 本应只删当前格,而不是清空整个字符串。
真正可行的结构是:用多个独立的 <input type="text" maxlength="1"> 元素并排排列,每个只接受一位字符。它们不共享值,但需协同管理焦点和输入逻辑。
- 每个
input必须设maxlength="1",禁用粘贴(或拦截后拆分) - 禁止用户手动切换到下一位——得靠 JS 控制
focus()和blur() - 所有
input应设autocomplete="off"、autocapitalize="none"、spellcheck="false",避免干扰 - 用
tabindex="-1"禁用 Tab 键顺序跳转,改由方向键/输入逻辑接管
如何用原生 JS 实现「输入即跳转」和「退格回退」
核心是监听 input 和 keydown 事件,根据当前值和按键决定焦点移动方向。不需要框架,也不依赖第三方库。
关键逻辑点:
立即学习“前端免费学习笔记(深入)”;
- 输入非空字符时,自动
focus()下一个input(如果存在) - 按
Backspace或Delete且当前为空,则focus()上一个input并清空它 - 按方向键(
ArrowLeft/ArrowRight)时,手动控制焦点,不依赖浏览器默认行为 - 所有
input的value应实时同步到一个隐藏的<input type="hidden">中,供表单提交
示例片段(绑定在第一个 input 上,其余类似):
inputEl.addEventListener('input', () => {
if (inputEl.value) {
const next = inputEl.nextElementSibling;
if (next && next.tagName === 'INPUT') next.focus();
}
});
inputEl.addEventListener('keydown', e => {
if (e.key === 'Backspace' || e.key === 'Delete') {
if (!inputEl.value && inputEl.previousElementSibling) {
e.preventDefault();
inputEl.previousElementSibling.focus();
inputEl.previousElementSibling.value = '';
}
}
});
移动端软键盘弹出与聚焦异常怎么处理
Android 微信、iOS Safari 常出现:点击某格后键盘弹出但光标没落到对应 input,甚至焦点丢失。这不是 bug,是浏览器对 input 生命周期的优化策略——尤其是 type="text" 在无样式/无尺寸时可能被忽略。
- 每个
input必须有明确宽高(如width: 40px; height: 48px;),不能靠内容撑开 - 加
style="user-select: none;"防止长按唤起系统菜单干扰 - 首次点击时,用
setTimeout(() => el.focus(), 0)延迟聚焦,绕过渲染队列问题 - 避免在
touchstart中直接focus(),改用touchend或click - iOS Safari 对
input的autofocus支持极差,不要依赖
无障碍与可访问性容易被忽略的细节
屏幕阅读器用户依赖 aria-label 和焦点顺序,而默认的多 input 结构会被读作「编辑文本,编辑文本,编辑文本……」,完全无法理解这是「4位验证码」。
- 给每个
input加aria-label="验证码第1位",动态更新序号 - 外层容器加
role="group"和aria-label="请输入短信验证码" - 禁用
tabindex="-1"后,必须提供键盘替代路径:比如支持Tab进入第一格,Shift+Tab退出整个组 - 输入完成时,自动将焦点移到「提交」按钮,而非停留在最后一个
input
最常漏掉的是:当用户粘贴一串 4 位数字时,没做长度校验就直接分发到各格——若粘贴了 5 位,第五位会丢,但用户毫无感知。务必在 paste 事件中截取前 N 位,并显式清空多余格子。



















