vue 实现在文本框光标处插入内容


场景:封装一个组件 - 简易计算器,在文本框点击时显示计算器,点击计算器上的按钮即数字、运算符等,就将点击的按钮文本插入到文本框的光标处

计算器组件calculator.vue





父组件template

item
  label="姓名"
  prop="name">
  input
    ref="name"
    id="name"
    v-model="form.name"
    placeholder="请输入姓名"
    :max-length="20"
    @click="(event) => handleShowCalculator(event, 'calculator1', 'name')"
  />
  

父组件script

// 显示计算器
handleShowCalculator (event, calculatorRef, field) {
  this.$refs[calculatorRef].$el.style.display = 'block'
  event.target.setAttribute('field', field)
}
// 计算器文本点击回调
handleUpdateCalculatorValue (val) {
  const target = this.$refs.name.$el
  const pos = this.getCursorPosition(target)
  const frontStr = this.form.name.substring(0, pos)
  const behindStr = this.form.name.substring(pos, this.form.name.length)
  this.form.name = frontStr + val + behindStr
  /// 注意,定位光标需要在 Vue 数据下一次更新之后,两种方式:
  ///     方法1:将 handleUpdateCalculatorValue 函数变为异步函数,方法前加上 async,然后在光标定位代码 this.setCaretPosition(target, pos + val.length) 的前面加上等待数据更新后的代码 await this.$nextTick()
  ///     方法2:将光标定位代码 this.setCaretPosition(target, pos + val.length) 写在 this.$nextTick() 中,即 this.$nextTick(() => { this.setCaretPosition(target, pos + val.length) })
  this.$nextTick(() => {
    this.setCaretPosition(target, pos + val.length)
  })
}

封装的两个操作光标方法:

// 获取光标位置
getCursorPosition (el) {
  let pos = 0
  if ('selectionStart' in el) {
    pos = el.selectionStart
  } else if ('selection' in document) {
    el.focus()
    const selRange = document.selection.createRange()
    const selRangeLength = document.selection.createRange().text.length
    selRange.moveStart('character', -el.value.length)
    pos = selRange.text.length - selRangeLength
  }
  return pos
},
// 设置光标位置
setCaretPosition (el, pos) {
  if (el.setSelectionRange) {
    el.focus()
    el.setSelectionRange(pos, pos)
  } else if (el.createTextRange) {
    const range = el.createTextRange()
    range.collapse(true)
    range.moveEnd('character', pos)
    range.moveStart('character', pos)
    range.select()
  }
},

两个光标操作方法基于以下代码封装,也可以使用下面的代码,二选一,同样要注意,定位光标需要在 Vue 数据下一次更新之后

// IE浏览器
if (document.selection) {
  target.focus()
  const sel = document.selection.createRange()
  sel.text = val
} else if (target.selectionStart) { // 谷歌 Firefox 等
  const startPos = target.selectionStart
  const endPos = target.selectionEnd
  const restoreTop = target.scrollTop // 获取滚动条高度
  // 拼接字符
  this.form.name = this.form.name.substring(0, startPos) + val + this.form.name.substring(endPos, this.form.name.length)
  if (restoreTop > 0) {
    target.scrollTop = restoreTop
  }
  target.focus()
  target.selectionStart = startPos + val.length
  target.selectionEnd = startPos + val.length
} else {
  this.form.name += val
  target.focus()
}

界面效果

在光标处插入字符

vue