大神原文
https://segmentfault.com/a/1190000005869372
//1. data()
lastEditRange: ''
// 2. 给可编辑div绑定keyup事件、click事件
recordLastEditRange() {
// 获取选定对象
const selection = getSelection()
// 设置最后光标对象
this.lastEditRange = selection.getRangeAt(0)
}
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)
},