贝利信息

如何实现动态生成的OTP输入框自动聚焦与跳转

日期:2026-01-18 00:00 / 作者:花韻仙語

本文详解为何otp输入框的focus/blur逻辑在change事件中失效,并提供基于input或keyup事件的可靠解决方案,确保用户输入单个数字后立即自动跳转至下一个输入框。

在构建六位OTP(One-Time Password)输入组件时,一个常见需求是:用户在某个输入框中输入一位数字后,焦点自动移至下一个输入框。许多开发者会自然选择 change 事件来监听值的变化并触发 focus(),但实际运行中却常发现跳转失败——控制台日志显示 blur 和 focus 被调用,但光标并未真正落到下一个输入框上。

根本原因在于 change 事件的触发时机:它仅在输入框失去焦点(blur)且值发生变更后才触发,而非实时响应输入。这意味着当用户刚键入一个数字(如“5”),此时输入框仍处于聚焦状态,change 事件尚未触发;只有当用户手动点击其他区域、按 Tab 键或主动失焦后,change 才执行——这完全违背了“即时跳转”的交互预期。

✅ 正确做法是改用 input 事件(推荐)或 keyup 事件:

以下是优化后的核心代码(已修复ID重复、maxlength拼写、焦点竞争等问题):

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(非 max-length)
  input.id = `otp-input-${i}`; // ✅ 避免重复 ID(原代码所有 input id='otp-input')
  input.dataset.otpPos = i;
  input.className = 'otp-input';

  input.addEventListener('input', (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) { // 延迟聚焦以避免浏览器焦点调度冲突(关键!) setTimeout(() => { const nextInput = textInputs[position + 1]; if (nextInput) { nextInput.focus(); } }, 0); } }); input.addEventListener('keydown', (e) => { // 支持 Backspace 删除时反向跳转(可选增强) if (e.key === 'Backspace' && !e.target.value && e.target.dataset.otpPos > 0) { setTimeout(() => { const prevInput = textInputs[parseInt(e.target.dataset.otpPos, 10) - 1]; if (prevInput) prevInput.focus(); }, 0); } }); textInputs.push(input); otpContainer.appendChild(input); } // 自动聚焦第一个输入框 if (textInputs[0]) textInputs[0].focus();

⚠️ 关键注意事项:

总结:OTP自动跳转的核心在于选择实时响应的事件源(input > keyup ≫ change),配合异步聚焦与输入校验,即可实现流畅、健壮、跨端一致的用户体验。