瀏覽器端實(shí)現(xiàn)類(lèi)似input限制輸入兩位小數(shù),輸入時(shí)光標(biāo)從輸入位置移動(dòng)到最后

1.問(wèn)題描述展示

示例代碼所做限制為不允許輸入字母d,其他限制規(guī)則可以根據(jù)需求自己調(diào)整,使用React編寫(xiě),其他框架或原生均可根據(jù)該代碼理解原理進(jìn)行轉(zhuǎn)變,特意使用了中文鍵盤(pán)可以看到輸入框下面白色框閃出,就是我按下了鍵盤(pán)d鍵

1) gif圖示例
輸入框未處理情況演示.gif
2)代碼
import { useState } from "react"
export default function () {
  const [value, setValue] = useState(1234)
  const onChange = (e) => {
    const inputValue = e.nativeEvent.target.value
    const value = inputValue.replace('d', '')
    setValue(value)
  }
  return <div style={{marginTop:200}}>
    <h3>焦點(diǎn)修改實(shí)例:不允許輸入字母 d 無(wú)處理情況</h3>
    <input value={value} onChange={onChange}></input>
  </div>
}
3)原因分析

假設(shè)目前輸入值為1234,光標(biāo)移動(dòng)到2后面并輸入字母d ,此時(shí)輸入框得到的輸入值為12d34,而我們通過(guò)代碼將本來(lái)應(yīng)該是12d34的值修改為了1234,也就導(dǎo)致了光標(biāo)定位到了最后一位
之所以這樣處理,猜想大概是因?yàn)橥ㄟ^(guò)代碼進(jìn)行值的修改的時(shí)候存在多種情況比如
a. 1234 => 12456 位數(shù)增多
b. 1234 => 12 位數(shù)變少
c. 1234 => 5678 值全變化

瀏覽器實(shí)現(xiàn)時(shí)為了統(tǒng)一這種情況 只要當(dāng)瀏覽器輸入框獲取的輸入值與設(shè)置的值不等的時(shí)候都重置光標(biāo)位置
react使用value對(duì)輸入框輸入值做控制的流程為: (以輸入框已經(jīng)輸入1234 光標(biāo)移動(dòng)到2后面輸入d為例)
1.按下d鍵
2.輸入框獲得值12d34,但是此時(shí)輸入框的值受value控制,onChange事件中如果不對(duì)value做修改則不會(huì)改變輸入框?qū)嶋H展示值 (也就是說(shuō)12d34瀏覽器輸入框內(nèi)部已經(jīng)獲取到的值為12d34,只是頁(yè)面并未變化,頁(yè)面變化由input上的value控制)
3.輸入框onChang事件觸發(fā),此時(shí)代碼處理,會(huì)將輸入框獲取值由本應(yīng)該設(shè)置為12d34,修改為了1234,光標(biāo)重置到最后面 (即輸入框內(nèi)部獲取的值如果不等于value值就會(huì)光標(biāo)重置到最后面)

2.解決方案

1)宏任務(wù)延時(shí)處理

原理為通過(guò)input.setSelectionRange改變光標(biāo)位置,具體api細(xì)節(jié)自行百度參考文檔

import { useState } from "react"
export default function () {
  const [value1, setValue1] = useState(1234)
  const onChange1 = (e) => {
    const start = e.target.selectionStart
    const inputValue = e.nativeEvent.target.value
    const value = inputValue.replace('d', '')
    const input = e.nativeEvent.target
    setValue1(value)
    setTimeout(() => {
      inputValue.includes('d') && input.setSelectionRange(start - 1, start - 1)
    }, 0)
  }
  return <div style={{marginTop:200}}>
    <h3>焦點(diǎn)修改實(shí)例:不允許輸入字母 d 宏任務(wù)處理</h3>
    <input value={value1} onChange={onChange1}></input>
  </div>
}
輸入框微任務(wù)處理.gif

雖然能夠讓光標(biāo)位置正確,但是多次輸入的時(shí)候存在光標(biāo)從最后面移動(dòng)到原位置的情況

2) 微任務(wù)處理(關(guān)于微任務(wù)宏任務(wù)可以自行百度了解)
import { useState } from "react"
export default function () {
  const [value2, setValue2] = useState(1234)
  const onChange2 = (e) => {
    const start = e.target.selectionStart
    const inputValue = e.nativeEvent.target.value
    const value = inputValue.replace('d', '')
    const input = e.nativeEvent.target
    setValue2(value)
    new Promise(res => {
      res()
    // promist.then的回調(diào)函數(shù)屬于微任務(wù)
    }).then(() => {
      inputValue.includes('d') && input.setSelectionRange(start - 1, start - 1)
    })
  }


  return <div style={{marginTop:200}}>
    <h3>焦點(diǎn)修改實(shí)例:不允許輸入字母 d 微任務(wù)處理</h3>
    <input value={value2} onChange={onChange2}></input>
  </div>
}

輸入框宏任務(wù)處理.gif

3.總結(jié)

輸入框onChange修改value值導(dǎo)致的光標(biāo)定位到最后的時(shí)機(jī)在微任務(wù)和宏任務(wù)的前面,
即 順序?yàn)?1.重置光標(biāo) => 2.微任務(wù) =>(瀏覽器內(nèi)部重置光標(biāo))界面重新渲染 => 3.宏任務(wù)
其中使用宏任務(wù)二者執(zhí)行間隔時(shí)間跨度大,導(dǎo)致重置光標(biāo)界面已經(jīng)發(fā)生變化,宏任務(wù)執(zhí)行恢復(fù)光標(biāo),導(dǎo)致看到光標(biāo)從最后移動(dòng)到代碼設(shè)置的位置(即:瀏覽器執(zhí)行恢復(fù)光標(biāo) =>界面重新渲染 ,光標(biāo)移動(dòng)到最后 => 代碼設(shè)置光標(biāo)位置 => 瀏覽器重新渲染,恢復(fù)到代碼設(shè)置的位置)
微任務(wù)則縮小了間隔或者說(shuō)順序 (即:瀏覽器執(zhí)行恢復(fù)光標(biāo) => 代碼設(shè)置光標(biāo)位置 => 瀏覽器重新渲染,設(shè)置為代碼設(shè)置的位置)
但是該代碼僅僅處理了簡(jiǎn)單的情況即每次只輸入一位的情況,還存在一些邊界情況未處理,例如直接復(fù)制dddd,調(diào)整輸入位置,粘貼就會(huì)導(dǎo)致光標(biāo)位置后移,此種情況就需要對(duì)原值與新輸入值做對(duì)比并進(jìn)行處理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容