应使用input事件实时格式化手机号,核心是清除非数字字符、按3-4-4加空格、手动维护光标位置以避免跳位,并在验证和提交时统一去除空格。
用 input 的 input 事件监听并格式化手机号
直接在 layui 表单的 <input type="text"> 上绑定原生 input 事件,不要用 change 或 blur——后者无法实时响应删除、粘贴等操作,会导致光标跳位或格式错乱。
核心逻辑是:取当前值 → 清除所有非数字字符 → 按 3-4-4 截取并插入空格 → 设置回输入框 → 手动恢复光标位置(关键!)。
- 必须用
event.target.value获取原始输入,Layui 不会劫持该属性 - 正则替换用
value.replace(/\D/g, ''),比parseInt更稳妥(防粘贴带符号字符串) - 截取时注意长度:不足 11 位不加空格,超过 11 位只取前 11 位
- 光标位置需在设置新值后重置,否则用户在中间修改时会跳到末尾
手动维护光标位置避免跳位
Layui 对表单元素无特殊光标控制,所以必须自己读写 selectionStart 和 selectionEnd。常见坑是:格式化后光标跑到最后,用户想删中间一位却删了末尾空格。
修复方式是在格式化前记录原始光标位置,再根据新字符串中数字位置映射回空格插入后的坐标。简单可靠的做法是:先保存原光标偏移(只计数字位),格式化后再按相同数字偏移找新位置。
- 用
let rawNums = value.replace(/\D/g, '')提前提取纯数字串 - 计算光标前有多少数字:
let numBeforeCursor = [...rawNums.slice(0, cursorPos)].filter(c => /\d/.test(c)).length - 格式化后,在新字符串里找到第
numBeforeCursor个数字的位置,作为新光标点 - 用
input.setSelectionRange(newStart, newEnd)设置
兼容 Layui 的表单验证与重置
Layui 的 form.verify 默认校验的是最终显示值(含空格),但后端通常只要纯数字。如果验证规则写成 /^1[3-9]\d{9}$/,会因空格匹配失败。
- 验证函数里务必先清理空格:
value.replace(/\s/g, '') - 提交前也建议统一处理:
data.phone = data.phone.replace(/\s/g, '') - 调用
form.reset()后,输入框值清空,但不会触发input事件,所以格式化逻辑不会自动重置——无需额外处理 - 若用
form.val('formFilter', {phone: ''})赋空值,同样不会触发事件,安全
完整示例:带光标修复的初始化代码
layui.use(['form'], function(){
const form = layui.form;
const phoneInput = document.getElementById('phone');
phoneInput.addEventListener('input', function(e){
const input = e.target;
let value = input.value;
let raw = value.replace(/\D/g, '');
let cursorPos = input.selectionStart;
// 计算光标前数字个数
let numBefore = 0;
for(let i = 0; i 0){
raw = raw.slice(0, 11);
if(raw.length >= 4){
raw = raw.slice(0, 3) + ' ' + raw.slice(3);
}
if(raw.length >= 9){
raw = raw.slice(0, 8) + ' ' + raw.slice(8);
}
}
input.value = raw;
// 重置光标
let newCursor = 0;
let digitCount = 0;
for(let i = 0; i
<p>这段代码没依赖 Layui 的事件代理,直接操作 DOM,和 Layui 表单共存无冲突。真正麻烦的不是格式化,而是光标映射——多数人卡在这一步,反复调试后发现删第 4 位总删成空格,其实就是没做数字位对齐。</p>











