You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JavaScript正则在React输入框实时格式化驾照号码的修改问题

实现方案

核心格式化逻辑

实现思路如下:

  • 先对用户输入做预处理:自动转为大写,移除所有非字母、数字的字符(包括之前插入的分隔符,避免重复拼接)
  • 采用范围匹配的正则分组,无需等待全量输入完成,只要对应段有内容就插入分隔符

核心格式化函数代码:

const formatLicenseNumber = (input) => {
  // 预处理:仅保留大写字母和数字,过滤其他所有字符
  const cleaned = input.toUpperCase().replace(/[^A-Z0-9]/g, '');
  // 分组匹配:前2位字母、接下来3位字母、最后最多6位数字
  const match = cleaned.match(/^([A-Z]{0,2})([A-Z]{0,3})([0-9]{0,6})$/);
  if (!match) return cleaned;
  // 过滤空分组后用-拼接
  return match.slice(1).filter(Boolean).join('-');
}

可直接测试格式化效果:

  • 输入DLHGK → 输出DL-HGK
  • 输入DLHGK12 → 输出DL-HGK-12
  • 输入dLhgK123456 → 自动转大写输出DL-HGK-123456

React 组件完整实现

额外增加了光标位置修复逻辑,解决实时格式化导致的光标自动跳转到末尾的问题:

import { useRef, useState } from 'react';

const LicenseInput = () => {
  const [value, setValue] = useState('');
  const inputRef = useRef(null);

  const handleChange = (e) => {
    const input = e.target;
    const cursorPosition = input.selectionStart;
    const rawValue = input.value;

    // 计算格式化前光标位置对应的有效字符数(排除分隔符)
    const rawValidCharCount = rawValue.slice(0, cursorPosition).replace(/[^A-Z0-9]/g, '').length;

    const formatted = formatLicenseNumber(rawValue);
    setValue(formatted);

    // 计算格式化后对应的光标位置
    let newCursorPos = 0;
    let validCount = 0;
    for (let i = 0; i < formatted.length; i++) {
      if (/[A-Z0-9]/.test(formatted[i])) {
        validCount++;
      }
      newCursorPos++;
      if (validCount === rawValidCharCount) {
        break;
      }
    }

    // 异步更新光标位置,避免React渲染覆盖
    requestAnimationFrame(() => {
      if (inputRef.current) {
        inputRef.current.selectionStart = inputRef.current.selectionEnd = newCursorPos;
      }
    });
  };

  return (
    <input
      ref={inputRef}
      value={value}
      onChange={handleChange}
      placeholder="请输入驾照号码"
      maxLength={13} // 总长度:2位+分隔符+3位+分隔符+6位=13位
    />
  );
};

const formatLicenseNumber = (input) => {
  const cleaned = input.toUpperCase().replace(/[^A-Z0-9]/g, '');
  const match = cleaned.match(/^([A-Z]{0,2})([A-Z]{0,3})([0-9]{0,6})$/);
  if (!match) return cleaned;
  return match.slice(1).filter(Boolean).join('-');
};

export default LicenseInput;

内容的提问来源于stack exchange,提问作者Tanish Raj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 17:09:03