需求
企业微信自建应用-APP端需要实现@提及他人的功能,没有合适的第三方组件,只能自己造轮子了
具体实现
<view contenteditable v-html="val" @keydown="handleKeyDown" @input="inputChange"></view>
contenteditable 可以指定一个可编辑元素,这其实也算是实现一个简化版的富文本编辑器了
判断@字符输入
// 获取当前光标
getCurrentRange() {
const selection = window.getSelection()
let range = null
if (selection && selection.rangeCount > 0) {
range = selection.getRangeAt(0)
}
if (range) {
const r = range.cloneRange()
range.collapse(true)
return range
}
return
},
// 判断当前输入的是否是@
inputAtValid(event) {
const range = getCurrentRange()
if (range) {
this.range = range // 保存后续兼容IOS需(伪)实时更新range
const rangeIndex = range.endOffset
const text = range.endContainer.innerHTML || range.endContainer.textContent
if (text.substr(rangeIndex - 1, 1) === '@') {
return true
}
}
return false
},
添加@人
@要求不可编辑,即
<span contenteditable="false" class="at-name">@张三<span>
上面的HTML直接拼接到输入的值后面,光标会有问题,这时候我们可以创建一个节点在光标处插入,同时把光标移到插入元素的后面
先删除原来输入的@字符
// 去除光标前输入的@
clearAtChart() {
const range = this.range
applyRange(range)
if (range) {
let offset = (range.endOffset - 1) < 0 ? 0 : range.endOffset - 1
range.setStart(range.endContainer, offset);
range.setEnd(range.endContainer, range.endOffset);
range.deleteContents();
}
},
光标后插入HTML
insertTextAtSelection(text) {
const range = this.range
if (window.getSelection) {
if (range) {
var el = document.createElement("span");
el.innerHTML = text;
const frag = el.firstChild
const t = document.createTextNode('')
range.insertNode(t)
range.insertNode(frag);
range.setEndAfter(frag)
range.collapse(false) // 参数在IE下必传
applyRange(range)
this.saveCurrentRange() // 实时保存光标
}
}
},
删除
删除的时候需要判断是否是@人,是的话需要整块删除
// 监听键盘事件判断是否是删除
// ...
// 删除触发时处理
handleDelete() {
const range = this.getCurrentRange()
if (range) {
if (range.endOffset >= 1) {
let a =
range.endContainer.childNodes[range.endOffset] ||
range.endContainer.childNodes[range.endOffset - 1]
if (!a || (a.nodeType === Node.TEXT_NODE && !/^\s?$/.test(a.wholeText))) {
return
} else if (a.nodeType === Node.TEXT_NODE) {
if (a.previousElementSibling) a = a.previousElementSibling
var ch = [].slice.call(a.childNodes)
} else {
var ch = [].slice.call(a.childNodes)
}
ch = [].reverse.call(ch)
ch.unshift(a)
let last;
[].some.call(ch, (c) => {
// 判断最后一个元素是否是@人,这个可以添加一个自定义的属性判断
if (...) {
last = c
return true
}
})
if (last) {
window.event.preventDefault()
window.event.stopPropagation()
const r = this.getCurrentRange()
if (r) {
r.setStartBefore(last)
r.deleteContents()
r.collapse(false)
applyRange(r)
this.handleInput()
}
}
return
}
}
}
注意---光标问题
刚开始功能完成后还信心满满地提测,当时开发的时候是用安卓机自测的,没想到到了IOS上,光标各种问题。要么没了,要么在输入框最后面不动,解决方案是实时保存光标信息,在此基础上去做一些操作
参考
最后
这是第一次尝试写文章,说实话感觉有点像流水账哈哈|ू・ω・` )