zoukankan      html  css  js  c++  java
  • 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;
       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;
           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;
         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()
    }

    界面效果

    在光标处插入字符

  • 相关阅读:
    云开发数据库 Firebase Firestore 零基础入门视频实战教程(7 个视频)
    在 2021 年你需要掌握的 7 种关于 JavaScript 的数组方法
    2021 年写 JavaScript 代码的 17 个优化技巧
    Redis 学习笔记系列文章之 Redis 的安装与配置 (一)
    selenium webdriver 删除元素
    FFT板子
    pytest一:运行几个简单的测试用例终端显示的信息
    JS 日期取年月日
    将博客搬至CSDN
    c语言编译器
  • 原文地址:https://www.cnblogs.com/jardeng/p/14113138.html
Copyright © 2011-2022 走看看