input 獲取游標位置與設定游標位置

時傾發表於2023-01-04

需求

輸入框,支援鍵盤輸入快捷按鍵 輸入,UI 如下「基於antd」:

關鍵點:鍵盤輸入直接使用 input onChange 事件即可,快捷按鍵輸入需要根據游標位置插入,插入後游標在新插入的字元後。

解決方案

獲取 input 游標位置

透過 element.selectionStart 獲取游標位置。

const inputDom = document.getElementById("input")
const selectionStart = inputDom.selectionStart

如圖,此時的 selectionStart 是 3。

設定 input 游標

透過 element.setSelectionRange() 設定游標位置,前提是 input 被 focus。

inputDom.focus()
// focus() 非同步,所以加了 setTimeout
setTimeout(() => {
  const nextSelection = selectionStart + 1
  inputDom.setSelectionRange(nextSelection, nextSelection)
}, 0)

element.setSelectionRange 語法

element.setSelectionRange(selectionStart, selectionEnd [, selectionDirection]);

  • selectionStart: 被選中的第一個字元的位置索引, 從 0 開始。如果這個值比元素的 value 長度還大,則會被看作 value 最後一個位置的索引。
  • selectionEnd: 被選中的最後一個字元的下一個位置索引。如果這個值比元素的 value 長度還大,則會被看作 value 最後一個位置的索引。
  • selectionDirection:選擇方向。forward/backward/none

如果 selectionStart 與 selectionEnd 相同,不選中任何,游標聚集在 selectionStart/selectionEnd。

如果 selectionEnd 小於 selectionStart,不選中任何,游標聚集在在 selectionEnd。

inputDom.setSelectionRange(0, 4)表現如下圖:

完整程式碼

import React, { useState, useRef } from 'react'
import { throttle } from 'lodash'
import { Input, Button, Tag } from 'antd'

const SHORTCUT = [0, '*', '#', 9]

const InputPro = () => {
  const [value, setValue] = useState('')
  const inputRef = useRef()

  const handleSubmit = throttle(() => {
    console.log('value', value)
  }, 3000)

  const handleInputChange = e => {
    setValue(e.target.value?.trim())
  }

  const handleTagChange = val => {
    const inputDom = inputRef.current?.input || {}
    let selectionStart = inputDom.selectionStart
    if (typeof selectionStart !== 'number') {
      selectionStart = value.length
    }
    const data = `${value.slice(
      0,
      selectionStart,
    )}${val}${value.slice(selectionStart)}`
    if (data.length <= 25) {
      setValue(data)
      inputDom.focus()
      setTimeout(() => {
        const nextSelection = selectionStart + 1
        inputDom.setSelectionRange(nextSelection, nextSelection)
      }, 0)
    }
  }

  return (
    <div>
      <Input.Group compact>
        <Input
          allowClear
          style={{ width: 160 }}
          maxLength={25}
          placeholder='請輸入'
          value={value}
          onChange={handleInputChange}
          ref={inputRef}
        />
        <Button
          type='primary'
          onClick={handleSubmit}
          disabled={value.length === 0}
        >
          確認
        </Button>
      </Input.Group>
      <div style={{ display: 'flex', alignItems: 'center', marginTop: 8 }}>
        {SHORTCUT.map(itm => (
          <div key={itm} onClick={() => handleTagChange(itm)}>
            <Tag color="#108ee9">{itm}</Tag>
          </div>
        ))}
      </div>
    </div>
  )
}

export default InputPro

相關文章