收到过一个需求,要求手机号输入框按下图的样式显示
项目环境:vue2.x
简单琢磨了一下,发现可以通过两种方式去实现
- 自行模拟一个用户输入框实现(好像还没搞定~😅)
- 监听 input 的输入事件,然后插入空格实现
这里主要分享下第二种解决方案的过程
<input
type="tel"
ref="tel"
v-model="bPhone"
@input="handlePhoneInput"
placeholder="请输入您的手机号码"
@blur="validPhone"
maxlength="13"
/>
<!-- tips: -->
<!-- 这里 v-model 使用的是 bPhone,即处理后的号码,实际提交数据的号码存在 phone 中 -->
<!-- maxlength = 13 是因为bPhone是处理过的手机号码,中间会有两个空格,因此是13位 -->
handlePhoneInput() {
let arr = this.bPhone.replace(/\s/gi, '') // 记录不包含空格的内容
let len = this.bPhone.length // 初始化已输入的长度,包括空格
let temp = arr.split('')
if(arr.length > 3) {
temp.splice(3, 0, ' ')
if(arr.length > 7) {
temp.splice(8, 0, ' ')
}
}
this.bPhone = temp.join('') // 处理后的手机号
this.phone = arr // 存储真实的手机号
}
// 下为扩展
const Mphone = /^1\d{10}$/ // 校验正则,vue中自行切换至data中
validPhone() {
if(!this.phone || !this.phone.match(this.Mphone)) {
this.$msg('请输入正确格式的手机号码')
} else {
// ...
}
}
基本功能已经初步实现出来了,但尝试了下发现几个问题:
- 移动光标并添加/删除一位(非最后一位),删除后光标会自动跳到最后
- 移动光标到空格后,按删除不生效,并且光标同样会跳转到最后
思考了一下,着手先解决光标的问题。
- 首先,需要判断当前用户是输入还是删除。这里需要借助一下 event 对象中的 inputType🚀 属性。(inputType 兼容主流浏览器,IE 除外,这里暂未处理兼容问题)
- 其次需要获取到当前光标的位置,然后重新设置异常情况下光标的位置
基于以上两点,对代码做出以下改动
handlePhoneInput(e) {
let arr = this.bPhone.replace(/\s/gi, '') // 记录不包含空格的内容
let len = this.bPhone.length // 初始化已输入的长度,包括空格
let temp = arr.split('')
let pos = this.getPosition(this.$refs.tel) // 获取当前光标位置,
let status = e.inputType === 'deleteContentBackward' ? 'minus' : 'plus' // 用于判断当前是输入还是删除
// 初始化光标位置为当前位置,这里需要着重处理删除的情况
let cursorPos = pos
if (status === 'minus') {
// 当删除空格时,需要手动将空格前一位也一并删除,因此光标位置也要减一
// 获取当前的位置是包含空格的,但删除需要注意,是要删除没有空格下的数据(未处理),也就是temp
if (pos === 3) {
temp.splice(2, 1)
cursorPos--
}
if (pos === 8) {
temp.splice(6, 1)
cursorPos--
}
}
if (arr.length > 3) {
// 位数超过3,则补充一个空格
temp.splice(3, 0, ' ')
if (arr.length > 7) {
// 位数超过7,同样需要补充一个空格
// 因为是根据没有空格的手机号去判断,因此这里是: arr.length > 7
// 因为一开始第四位补充了一个空格,因此这里要在第八位插入一个空格
temp.splice(8, 0, ' ')
}
// 重新计算光标的位置,区分输入与删除两种情况
status === 'plus' && (pos === 4 || pos === 9) ? cursorPos++ : ''
status === 'minus' && (pos === 4 || pos === 9) ? cursorPos-- : ''
}
this.bPhone = temp.join('') // 处理后的手机号
this.phone = arr // 存储真实的手机号
setTimeout(() => {
this.setPosition(this.$refs.tel, cursorPos)
}, 0)
},
// 获取当前光标位置
getPosition(el) {
let cursorPos = 0
if (document.selection) {
var selectRange = document.selection.createRange()
selectRange.moveStart('character', -el.value.length)
cursorPos = selectRange.selectionStart
} else if (el.selectionStart || el.selectionStart === '0') {
cursorPos = el.selectionStart
}
return cursorPos
}
// 设置光标位置
setPosition(textDom, pos) {
if (textDom.setSelectionRange) {
textDom.focus()
textDom.setSelectionRange(pos, pos)
} else if (textDom.createTextRange) {
var range = textDom.createTextRange()
range.collapse(true)
range.moveEnd('character', pos)
range.moveStart('character', pos)
range.select()
}
}
其实空格删除失效的问题,在上面的代码就已经解决了。在每次执行删除操作时,判断只要是第三位/第八位,也就是在空格后执行操作时,主动把空格前一位的数字一并删除,并且将 cursorPos 左移一位,保证光标位置正确显示
if (status === 'minus') {
// 当删除空格时,需要手动将空格前一位也一并删除,因此光标位置也要减一
// 获取当前的位置是包含空格的,但删除需要注意,是要删除没有空格下的数据(未处理),也就是temp
if (pos === 3) {
temp.splice(2, 1)
cursorPos--
}
if (pos === 8) {
temp.splice(6, 1)
cursorPos--
}
}
TODO
- 代码中使用了较多的 if else,需进一步优化,简化代码
- 兼容性完善:inputType 在 IE 不可用,可以考虑通过记录操作前后字符的长度来判断执行的操作
- 光标会“闪一下”的问题
- 封装成 vue 指令