当前位置: 首页 > 工具软件 > emoji-mart > 使用案例 >

Vue可编辑div组件------解决插入emoji时的光标问题

习狐若
2023-12-01

大神原文
https://segmentfault.com/a/1190000005869372

1.记录光标最后位置

//1. data()
lastEditRange: ''

// 2. 给可编辑div绑定keyup事件、click事件
recordLastEditRange() {
    // 获取选定对象
    const selection = getSelection()
    // 设置最后光标对象
   this.lastEditRange = selection.getRangeAt(0)
}

2. 插入emoji

selection: 点击任何一个可编辑区域,就会产生一个selection选中对象(鼠标选中一段文字变成蓝色的那个方块),有且只存在一个。

range:不断闪烁的光标,是selection对象的一个东西,这里主要操作range

    selectEmoji(emoji) {
      // 可编辑div
      const inp = this.$refs.inpMsgRef
      inp.focus()
      // 获取选定对象
      var selection = getSelection()
     
      // 1. 判断是否有最后光标对象存在
      if (this.lastEditRange) {
        // 清除所有存在的光标,添加光标还原最后的记录状态
        selection.removeAllRanges()
        selection.addRange(this.lastEditRange)
      }
      
      // 2.判断光标是否处于文本节点,没有标签
      if (selection.anchorNode.nodeName !== '#text') {
        // 创建表情文本节点进行插入
        var emojiText = document.createTextNode(emoji)
        if (inp.childNodes.length > 0) {
          // 如果文本框的子元素大于0,则表示有其他元素,则按照位置插入表情节点

          for (var i = 0; i < inp.childNodes.length; i++) {
            if (i === selection.anchorOffset) {
            // 谷歌和edge回车生成 <div><br></div>, ie生成<p><br></p>,
            //这个判断是否在插入emoji之前进行了回车换行时,此时直接插入会造成光标位置不能正确定位
              if (inp.childNodes[inp.childNodes.length - 1].innerHTML === '<br>') {
              	//移除br,留下div,在div内插入emoji,约等于换行。本人新手,没想到更好的办法
                inp.childNodes[inp.childNodes.length - 1].removeChild(inp.childNodes[inp.childNodes.length - 1].childNodes[0])
                inp.childNodes[inp.childNodes.length - 1].appendChild(emojiText)
              } else {
                inp.insertBefore(emojiText, inp.childNodes[i])
              }
            }
          }
        } else {
          // 输入框没有内容,直接插入一个表情元素
          inp.appendChild(emojiText)
        }
        
        // 3. 创建新的光标对象
        var range = document.createRange()
        // 定义光标选中范围
        range.selectNodeContents(emojiText)
        // 光标位置定位在表情节点的后面
        range.setStart(emojiText, emojiText.length)
        // 使光标开始和光标结束重叠
        range.collapse(true)
        // 清除选定对象的所有光标对象
        selection.removeAllRanges()
        // 插入新的光标对象
        selection.addRange(range)
      } else {
        // 如果是光标位于文本节点,则先获取光标对象
        const range = selection.getRangeAt(0)
        // 获取光标对象的范围界定对象,一般就是textNode对象
        var textNode = range.startContainer
        // 获取光标位置
        var rangeStartOffset = range.startOffset
        // 光标位置处插入新的表情内容
        textNode.insertData(rangeStartOffset, emoji)
        // 光标移动到到原来的位置加上新内容的长度
        range.setStart(textNode, rangeStartOffset + emoji.length)
        // 光标开始和光标结束重叠
        range.collapse(true)
        // 清除选定对象的所有光标对象
        selection.removeAllRanges()
        // 插入新的光标对象
        selection.addRange(range)
      }
      // 无论如何都要记录最后光标对象
      this.lastEditRange = selection.getRangeAt(0)
    },
 类似资料: