
本文详解为何otp输入框的change事件无法触发自动聚焦,以及如何通过input或keyup事件正确实现数字输入后自动跳转到下一个输入框的功能。
在构建六位一次性密码(OTP)输入组件时,一个常见需求是:用户在某个输入框中输入一位数字后,焦点自动移至下一个输入框。然而,若使用 change 事件监听值变化并调用 focus(),往往失效——原因在于 change 事件仅在输入框失去焦点(blur)且值发生改变后才触发,而非实时响应每一次输入。这意味着用户尚未离开当前输入框时,change 根本不会被触发,自然无法执行后续的 focus() 操作。
正确的做法是改用 input 事件(推荐)或 keyup 事件:
- ✅ input:在 值每次变更时立即触发(包括键盘输入、粘贴、拖放等),语义准确、兼容性好(IE9+);
- ✅ keyup:按键释放时触发,适合需区分按键行为的场景,但不捕获非键盘输入(如粘贴);
- ❌ change:仅在 blur 后校验变更,不适用于实时跳转逻辑。
以下是修正后的核心代码示例(基于原逻辑优化):
const OTP_LENGTH = 6;
const textInputs = [];
const otpContainer = document.getElementById('otp-container');
for (let i = 0; i < OTP_LENGTH; i++) {
const input = document.createElement('input');
input.type = 'text'; // 使用 text 而非 number,避免移动端数字键盘限制及浏览器自动验证干扰
input.maxLength = 1; // 使用原生 maxLength 属性(更可靠)
input.dataset.otpPos = i;
input.className = 'otp-input';
// 实时监听输入
input.addEventListener('input', function (e) {
const target = e.target;
const position = parseInt(target.dataset.otpPos, 10);
// 清空非数字字符(可选增强)
target.value = target.value.replace(/[^0-9]/g, '');
// 输入有效数字后,自动聚焦下一个
if (target.value && position < OTP_LENGTH - 1) {
const nextInput = textInputs[position + 1];
if (nextInput) {
nextInput.focus();
}
}
});
// 可选:支持退格键回退(提升体验)
input.addEventListener('keydown', function (e) {
if (e.key === 'Backspace' && !this.value && this.previousElementSibling) {
e.preventDefault();
this.previousElementSibling.focus();
}
});
textInputs.push(input);
otpContainer.appendChild(input);
}
// 首个输入框初始聚焦
if (textInputs[0]) textInputs[0].focus();关键注意事项:
- 避免为多个输入框设置相同 id="otp-input"(ID 必须唯一),改用 data-* 属性或 class 进行标识;
- type="number" 在部分移动端可能触发两次输入或阻止粘贴,建议统一用 type="text" 并手动校验数字;
- 调用 focus() 前请确保目标元素已挂载到 DOM 且可聚焦(本例中因顺序创建,无此问题);
- 若需支持粘贴多位数字(如用户粘贴 "123456"),可额外监听 paste 事件并分发到各输入框。
通过将事件从 change 切换至 input,即可实现流畅、可靠的 OTP 自动跳转体验,兼顾可用性与健壮性。










