input格式化输入框内容后光标位置错位问题及其解决办法 您所在的位置:网站首页 输入法框位置怎么设置不变 input格式化输入框内容后光标位置错位问题及其解决办法

input格式化输入框内容后光标位置错位问题及其解决办法

2024-07-18 07:16| 来源: 网络整理| 查看: 265

input格式化输入框内容后定位错位问题 最近公司有一个 汇率换算的小项目 用的 nuxt 服务端渲染,UI用的是饿了么的UI框架,总共 7 个输入框,数据是根据一个输入框中的值进行双向绑定换算的,这些都不是重点。需求是用户实时输入的时候 input输入框中的值是实时改变的,但是在实时改变的时候,由于数据是千分位格式化了的,当输入的时候,数据就会格式,导致光标会错位,从而导致输入错误出现这个BUG的时候,首先考虑到是因为格式化的原因,然后去格式化操作哪里debug数据,最后发现 数据格式化并没有问题,问题在于实时输入数据,然后又格式化数据,然后就是一顿 爆 百度 谷歌 必应 ,最后发现一个大佬的 文章,这里先贴出来

这段时间进行项目升级工作,期间遇到一个需求:针对十六进制数输入框(输入框内只能输入十六进制数),输入时在每一个字节间插入空格,便于阅读。基本效果如下图所示。

hexinput

这个功能倒不是很难,只用将原始数据格式化后重新设置到输入框中即可。但是,这里却有一个问题:如果从数据中间某处开始编辑,编辑一次后光标就会跳转到最右,在修改时体验不是很好。

hexinput_cursor

这个问题的原因也很容易理解,当我们把光标移到中间部分开始编辑时,数据的内容会因重新格式化导致出现差异,此时把数据设置到输入框中,浏览器无法得知光标应该定位到哪里,所有就只好设置到最右边。

想要修正这个问题,我们就得根据编辑时的光标位置计算出编辑并格式化之后期望光标出现的位置,然后手动设置。这里主要有两个问题:

如何获取/设置输入框光标的位置?如何计算期望的位置? 获取/设置光标的位置

浏览器给我们提供了输入框的选中控制 API:

setSelectionRange():设置输入框选中区域,只有起始和结束在同一个位置,就可以设置光标位置。selectionStart:选取区域的起始位置。selectionEnd:选取区域的结束位置。

浏览器的支持情况还不错。

selection

计算光标的期望位置

有了浏览器提供的 API,我们现在已经可以获取和设置光标的位置,那么如何计算出光标的期望位置呢?

起初的我设想的是在 keydown 事件中,获取当前光标的位置。如果是键入,那么光标位置 +1,如果是删除,那么光标位置不变。 但是,并不是这么简单,应该格式化的原因我们需要把空格考虑进去,并不是每次键入都需要 +1,也并不是每次删除都不变。考虑到这个思路需要考虑的因素较多,最终放弃。

转变思路后,我开始想:我是如何判断光标的位置呢?我是根据字符来判断的。当输入一个字符后,光标应该在此字符的后面,无论进行了什么样的格式化操作;同理,当删除一个字符后,光标应该在此字符前一个字符的后面。 这个思路就比较简单,减少了可能出现的变量。

具体计算规则 监听 keydown 事件,获取当前光标的位置。根据输入框的内容和光标位置,计算出当前光标位置的前一个字符的下标(不计算空格)。判断用户按了哪个键,是退格还是[0-9a-f]。如果是退格,光标应该定位在前一个字符,否则定位到后一个字符,从而计算出期望光标定位在哪个字符后面(不计算空格)。格式化数据,根据字符位置计算出光标位置(计算空格)。

这里需要注意几点:

当进行选中删除操作时,光标应该不需要前移。其他输入框不支持的字符需要处理,例如:ctrl等功能键。 scss .container { text-align: center; padding-top: 46px; } #input { font-variant: tabular-nums; box-sizing: border-box; margin: 0; display: inline-block; padding: 4px 11px; width: 300px; height: 32px; font-size: 14px; line-height: 1.5; color: rgba(0, 0, 0, 0.65); background-color: #fff; border: 1px solid #d9d9d9; border-radius: 4px; transition: all .3s; &:focus { border-color: #40a9ff; outline: 0; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); border-right-width: 1px !important; } }

HTML

Babel

const input = document.querySelector('#input'); let expectCharIndex; // 期望光标所处位置前一个字符的下标(不包括空格) input.onkeydown = function(e) { const curValue = input.value; // 当前值 console.log('keydown 阶段,此时 value 为:', curValue || '空'); const selectionStart = input.selectionStart; // 当前选中的区域起始位置 const selectionEnd = input.selectionEnd; // 当前选中区域结束位置 console.log('当前光标位置:', selectionStart); const code = e.keyCode || e.which || e.charCode; const preCharIndex = getPreviousCharIndex(curValue, selectionStart); console.log('当前光标前一个字符的下标:', preCharIndex); // 计算编辑之后光标前一个字符 if (code === 8) { if (selectionStart !== selectionEnd) { // 选中区间删除,此时光标应该不前移动 expectCharIndex = preCharIndex; } else { expectCharIndex = preCharIndex - 1; } } else { expectCharIndex = preCharIndex + 1; } console.log('格式化后,期望光标前一个字符的下标:', expectCharIndex); }; input.onkeyup = function() { const value = formatValue(input.value); console.log('keyup 阶段,此时 value 为:', value); input.value = value; const cursorIndex = getCursorIndex(value, expectCharIndex); console.log('期望光标的位置:', cursorIndex); console.log('============================='); // TODO 增加判断,如果是功能键等则不设置 setTimeout(() => { input.setSelectionRange(cursorIndex, cursorIndex); }, 10); }; /** * 格式化输入框内容,每两个字节中间插入一个空格。 * * @param {string} value - 输入框内容 * @return {string} */ function formatValue(value) { if (!value) { return value; } value = value.replace(/\s*/g, ''); if (value.length


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有