vue 實現在文本框光標處插入內容


場景:封裝一個組件 - 簡易計算器,在文本框點擊時顯示計算器,點擊計算器上的按鈕即數字、運算符等,就將點擊的按鈕文本插入到文本框的光標處

 

計算器組件calculator.vue

<template>
  <!-- 計算器 -->
  <div ref="calculator" class="calculator" :data-value="value">
    <div class="texts">
      <span v-for="(item, index) in calculatorTexts" :key="index" @click="handleTextClick(item)">
        {{ item }}
      </span>
    </div>
    <div class="close" @click.stop="handleClose">
      <a-icon type="close-circle" />
    </div>
    <!-- <div class="operator">
      <span>確定</span>
      <span>取消</span>
    </div> -->
  </div>
</template>

<script>
export default {
  name: 'Calculator',
  props: {
    initValue: {
      type: String,
      require: true,
      default: ''
    }
  },
  data() {
    return {
      // 計算器文本數組
      calculatorTexts: (() => {
        let texts = []
        for (let index = 0; index < 10; index++) {
          texts.push(index.toString())
        }
        texts = texts.concat(['.', '+', '-', '*', '/', '(', ')'])
        return texts
      })(),
      // 計算器是否可見
      calculatorVisible: true,
      // 樣式
      style: {
        top: '0px',
        left: '0px'
      },
      // 光標是否處於計算器內
      calculatorFocus: false,
      // 文本值
      value: this.initValue
    }
  },
  mounted() {
  },
  methods: {
    // 文本點擊
    handleTextClick(text) {
      // 累積文本 或 覆蓋文本
      this.value = text
      this.$emit('updateCalculatorValue', this.value)
    },
    // 關閉計算器
    handleClose() {
      this.$refs.calculator.style.top = '0px'
      this.$refs.calculator.style.left = '0px'
      this.$refs.calculator.style.display = 'none'
      this.value = ''
    },
    // 設置坐標
    setLocatioin(point) {
      Object.assign(this.style, point)
    }
  }
}
</script>

<style lang="less" scoped>
.calculator {
  display: none;
  width: 122px;
  height: auto;
  border: 1px #f0f0f0 solid;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 9999;
  background-color: white;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

  .texts {
    display: flex;
    flex-wrap: wrap;

    span {
      flex-shrink: 0;
      display: inline-block;
      width: 30px;
      border-right: 1px #f0f0f0 solid;
      border-bottom: 1px #f0f0f0 solid;
      text-align: center;
      padding: 5px 10px;

      &:hover {
        color: orange;
        border-bottom: 1px orange solid;
        cursor: pointer;
      }
    }
  }

  .operator {
    display: flex;
    justify-content: center;

    span {
      display: inline-block;
      border: 1px #f0f0f0 solid;
      text-align: center;
      padding: 5px 10px;
      margin: 8px 5px;

      &:hover {
        color: orange;
        border-bottom: 1px orange solid;
        cursor: pointer;
      }
    }
  }

  .close {
    display: inline-block;
    position: absolute;
    top: -22px;
    right: -15px;
    width: auto;
    font-size: 22px;
    cursor: pointer;

    &:hover {
      color: red;
    }
  }
}
</style>

 

父組件template

<a-form-model-item
  label="姓名"
  prop="name">
  <a-input
    ref="name"
    id="name"
    v-model="form.name"
    placeholder="請輸入姓名"
    :max-length="20"
    @click="(event) => handleShowCalculator(event, 'calculator1', 'name')"
  />
  <calculator ref="calculator1" @updateCalculatorValue="handleUpdateCalculatorValue" style="top: 44px;"></calculator>
</a-form-model-item>

 

父組件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()
}

 

界面效果

在光標處插入字符

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM