
本文讲解为何otp输入框的change事件无法触发自动聚焦,以及如何改用input或keyup事件来正确实现数字输入后自动跳转到下一个输入框的功能。
在构建6位一次性密码(OTP)输入组件时,一个常见需求是:用户在某个输入框中输入一位数字后,焦点自动移至下一个输入框。然而,若使用 change 事件监听值变化,会发现该逻辑几乎从不生效——因为 change 事件仅在输入框失去焦点(blur)且内容发生过修改时才触发,而非实时响应每一次输入。
这正是原代码失效的根本原因:
input.onchange = (e) => { /* ... */ }; // ❌ 错误时机:用户尚未离开当前输入框,change 不会触发✅ 正确做法是监听 input 事件(推荐)或 keyup 事件:
- input 事件在 值每次变更时立即触发(包括粘贴、删除、键盘输入),语义最准确;
- keyup 事件则在按键释放后触发,适合需区分按键类型的场景(如过滤非数字键)。
以下是修复后的核心逻辑(已适配动态创建流程):
for (let i = 0; i < OTP_LENGTH; i++) {
const input = document.createElement('input');
input.type = 'text'; // ⚠️ 改为 text 更稳妥(number 类型在部分浏览器中可能触发 blur)
input.maxLength = 1;
input.dataset.otpPos = i;
input.className = 'otp-input';
// ✅ 使用 input 事件替代 onchange
input.addEventListener('input', function(e) {
const target = e.target;
const pos = parseInt(target.dataset.otpPos, 10);
// 确保只输入单个数字(可选增强)
target.value = target.value.replace(/[^0-9]/g, '').slice(0, 1);
if (pos < OTP_LENGTH - 1 && target.value) {
// 清空并聚焦下一个输入框(避免重复触发)
const nextInput = textInputs[pos + 1];
nextInput.value = '';
nextInput.focus();
}
});
// 可选:支持退格键回退(提升体验)
input.addEventListener('keydown', function(e) {
if (e.key === 'Backspace' && !this.value && this.previousElementSibling) {
e.preventDefault();
this.previousElementSibling.focus();
this.previousElementSibling.select();
}
});
otpContainer.appendChild(input);
textInputs.push(input);
}⚠️ 注意事项:
- 避免重复 focus() 调用:确保 nextInput.focus() 前不触发其他干扰逻辑(如多次绑定事件);
- 禁用 type="number":因其在移动端可能引发软键盘异常、自动 blur 或值校验延迟,推荐统一用 type="text" + 正则过滤;
- ID 唯一性:原代码中所有输入框 id="otp-input" 违反 HTML 规范,应改用 data-* 属性或 class 定位;
- 移动端兼容性:input.focus() 在 iOS Safari 中需在用户手势上下文中调用(本例满足),但避免在异步回调(如 setTimeout)中调用。
总结:OTP 自动聚焦失败的本质是事件选型错误。将 change 替换为 input,配合合理的值清理与边界判断,即可稳定实现“输完即跳”的交互体验。










