前言
需求是这样的,输入加油卡号,每隔输入4位自动加上一个横杠,如图:
这个光标问题是个坑,,加班到10点还没解决好。。。
解决方法
首先,这里我使用的方法是监听输入,使用正则匹配。
<p className="addcard_tip">卡号:</p> <input type="tel" id="cardNum" value={this.state.cardid} className="input_info" onClick={() => { this.inputClick() }} onChange={(e) => { this.inputCardid(e) }} style={{ "left": "66px" }} maxLength={this.state.type ? CARDID_LEN_SHIYOU+3 : CARDID_LEN_SHIHUA+4} placeholder={`请输入${this.state.type ? CARDID_LEN_SHIYOU : CARDID_LEN_SHIHUA}位卡号`} />
inputCardid(e) { var input = document.getElementById('cardNum'), deforeValue = ''; let cardid = input.value.replace(/\-/g, '').replace(/(.{4})(?=.)/g, '$1-'); //把input输入框的值,通过正则每隔4位插入横杠'-' if (e) { var start = this.getSelectionStart(input); //获取现在光标的位置 this.setState({ cardid: input.value.replace(/\-/g, '').replace(/(.{4})(?=.)/g, '$1-') //把通过正则匹配后的值设置为input的value,同时也和state做一个双向绑定 },()=>{ this.setSelectionStart(input, start);//setState完了以后,再重新设置实际上的光标 } } else { cardid = this.state.cardid; } }
getSelectionStart(input) { var realStart = input.value.substring(0, input.selectionStart || 0).replace(/\-/g, '').length; return realStart + (parseInt((realStart - 1) / 4));//这里,获取的是实际上正则匹配后光标的位置 } setSelectionStart(input,start) { input.setSelectionRange(start, start) } inputClick(){ var input = document.getElementById('cardNum'); var start = this.getSelectionStart(input); this.setSelectionStart(input,start); }
这里,在ios上是完美表现的,然而在安卓上就会光标出现问题,就是我输入了第五位,在第五位前面通过js改变了input的值,但是,我的光标还是停留在'-'的后面,不是预期的在输入的值的最后。
然后,我就开始了一个疯狂的踩坑模式,陷入一个死循环...把光标强制在最后一位,但是这样无法修改中间的值..
后来,开始怀疑是不是设置光标的时机不对,但是已经设置在setState完成的回调里面了。
后来,尝试给设置光标的方法再加一点延迟。
inputCardid(e) { var input = document.getElementById('cardNum'), deforeValue = ''; let cardid = input.value.replace(/\-/g, '').replace(/(.{4})(?=.)/g, '$1-'); //把input输入框的值,通过正则每隔4位插入横杠'-' if (e) { var start = this.getSelectionStart(input); //获取现在光标的位置 this.setState({ cardid: input.value.replace(/\-/g, '').replace(/(.{4})(?=.)/g, '$1-') //把通过正则匹配后的值设置为input的value,同时也和state做一个双向绑定 },()=>{ setTimeout(() => { this.setSelectionStart(input, start); }, 30);//setState完了以后,再重新设置实际上的光标 } } else { cardid = this.state.cardid; } }
然后,就解决了。。
这里, 在setState完成后进行光标重置,但是还没有把input里面的value写入,因此再加一点点延迟,就可以重置光标了。