
本文介绍如何使用原生 javascript 将多个单字符 otp 输入框(如 6 位验证码)实时合并为一个完整字符串,并同步填充至用户名和密码两个表单字段,支持键盘导航、粘贴解析与自动聚焦。
本文介绍如何使用原生 javascript 将多个单字符 otp 输入框(如 6 位验证码)实时合并为一个完整字符串,并同步填充至用户名和密码两个表单字段,支持键盘导航、粘贴解析与自动聚焦。
在构建登录或验证流程时,常需用户输入多位数字(如短信验证码、PIN 码),并将其作为统一凭证用于用户名和密码字段(例如临时通行码场景)。传统做法依赖 jQuery 或冗余监听逻辑,易出错且维护成本高。本文提供一套轻量、健壮、无框架依赖的原生实现方案。
核心思路
- 所有单字符输入框统一标记为 name="pin[]",便于批量选取;
- 使用 input 事件实时捕获每个框的值变化;
- 利用 Array.prototype.reduce() 遍历所有输入框,拼接其 value,生成完整 PIN 字符串;
- 将该字符串同步写入 <input name="username"> 和 <input name="password">;
- 同时增强交互体验:输入后自动跳转至下一框、退格时回退至上一框、支持粘贴(自动过滤空格/连字符)。
完整可运行示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>PIN 合并输入示例</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.flex { display: flex; gap: 8px; }
.pin > input {
text-align: center;
width: 2.5rem;
height: 2.5rem;
font-size: 1.2rem;
border: 1px solid #ced4da;
border-radius: 4px;
outline: none;
transition: border-color 0.2s;
}
.pin > input:focus { border-color: #0d6efd; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); }
</style>
</head>
<body class="p-4">
<div class="container">
<h4>复制以下任意 PIN 码粘贴到下方输入框:</h4>
<pre class="bg-light p-2 rounded">123329<br>6 5 0 3 2 1<br>444-998<br>x1A23z<br>12</pre>
<div class="col-md-4 mt-4">
<form>
<h5 class="mb-3">请输入 6 位 PIN</h5>
<div class="otpCont flex">
<div class="pin">
<input type="text" name="pin[]" maxlength="1" autocomplete="off" required autofocus>
<input type="text" name="pin[]" maxlength="1" autocomplete="off" required>
<input type="text" name="pin[]" maxlength="1" autocomplete="off" required>
<input type="text" name="pin[]" maxlength="1" autocomplete="off" required>
<input type="text" name="pin[]" maxlength="1" autocomplete="off" required>
<input type="text" name="pin[]" maxlength="1" autocomplete="off" required>
</div>
</div>
<div class="mt-4">
<label class="form-label">用户名(自动同步)</label>
<input type="text" name="username" class="form-control mb-2" readonly>
<label class="form-label">密码(自动同步)</label>
<input type="text" name="password" class="form-control mb-3" readonly>
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
</div>
</div>
<script>
// DOM 工具函数(简洁安全)
const els = (sel, par = document) => par.querySelectorAll(sel);
const el = (sel, par = document) => par.querySelector(sel);
// 获取目标字段
const elUN = el('[name="username"]');
const elPW = el('[name="password"]');
// 处理每组 PIN 输入框(支持多组扩展)
els('.pin').forEach(elGroup => {
const inputs = [...els('input[name="pin[]"]', elGroup)];
const len = inputs.length;
const handlePaste = (ev) => {
ev.preventDefault();
const clip = ev.clipboardData.getData('text');
const pin = clip.replace(/[\s\-]/g, ''); // 清洗空格与连字符
const chars = [...pin];
inputs.forEach((inp, i) => inp.value = chars[i] ?? '');
inputs[Math.min(len - 1, chars.length - 1)]?.focus();
};
const handleInput = () => {
const PINValue = inputs.reduce((acc, inp) => acc + inp.value, '');
elUN.value = PINValue;
elPW.value = PINValue;
};
const handleKeydown = (ev) => {
const idx = inputs.indexOf(ev.target);
if (ev.key === 'Backspace' && !ev.target.value && idx > 0) {
inputs[idx - 1].focus();
}
};
const handleKeypress = (ev) => {
const idx = inputs.indexOf(ev.target);
if (ev.key.length === 1 && idx < len - 1 && ev.target.value) {
inputs[idx + 1].focus();
}
};
// 绑定事件
inputs.forEach(inp => {
inp.addEventListener('paste', handlePaste);
inp.addEventListener('input', handleInput);
inp.addEventListener('keydown', handleKeydown);
inp.addEventListener('keypress', handleKeypress);
});
});
</script>
</body>
</html>关键注意事项
- ✅ maxlength="1" 是必需的:防止用户意外输入多字符,破坏单格单字逻辑;
- ✅ 禁用 autocomplete:避免浏览器自动填充干扰;
- ✅ 使用 input 而非 keyup:兼容剪贴板粘贴、语音输入、IME 输入等所有变更方式;
- ⚠️ 避免重复 ID:原文中两个 <input id="maxVoucherCode"> 违反 HTML 规范,应改用 name 或 class 选择器;
- ? 若用于真实密码场景,请注意:此处仅为演示“值同步”,生产环境切勿将密码明文显示在 type="text" 字段中,应结合 type="password" 及服务端校验。
通过本方案,你获得了一个高性能、无障碍友好、可复用的 PIN 合并组件——无需外部依赖,开箱即用,且易于集成进任何现代前端项目。

















