思路
- 键入 @ 后将选择框显示出来
- 点击选择框中的选项时,返回输入框
- 输入框中显示 @xxx
- 将光标定位在 @xxx 以后
- 删除 @xxx 时须要整个 @xxx 一块儿删除
实现
主要是使用 selection 和 range 这两个关于选区对象和光标对象,结合div 的 contenteditable 属性实现
步骤
- 先有一个可编辑的 div 元素
<div
id="atInput"
contenteditable
onKeyDown="onKeyDown"
/>
- 输入 @ 时,弹出选人的框
// 输入监听
function onKeyDown(e) {
// ...
// 如果触发 @
if (e.code == 'Digit2' && e.shiftKey) {
// 打开选择弹窗
open()
}
}
open方法
// 打开选择框
function open() {
const selection = window.getSelection()
const range = selection?.getRangeAt(0)
// 缓存光标所在节点
state.focusNode = selection.focusNode
// 缓存光标所在节点位置
state.focusOffset = selection.focusOffset
// 显示选择框
state.visible = true
// 输入框失去焦点
let atInput = document.getElementById('atInput')
atInput.blur()
}
- 选择好人之后, 删除输入的 @ 字符,将选择的人包装成一个标签,放在原先的 @ 的位置且让光标到标签的后方
function onSelectSubmit(val) {
let atInput = document.getElementById('atInput');
let selection = window.getSelection();
let range = window.getSelection().getRangeAt(0);
//选中输入的@符号
range.setStart(focusNode, focusOffset - 1);
range.setEnd(focusNode, focusOffset);
// //删除输入的@符号
range.deleteContents();
var spanNode1= document.createElement('span');
var spanNode2 = document.createElement('span');
spanNode1.className = 'atFont';
spanNode1.innerHTML = '@' + s.nickName;
spanNode1.contentEditable = false;
spanNode2.innerHTML = ' ';
var frag = document.createDocumentFragment(),
node,
lastNode;
frag.appendChild(spanNode1);
while ((node = spanNode2.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
selection.extend(lastNode, 1);
selection.collapseToEnd();
}
- 删除, 监听键盘的输入如果输入是 backSpace 则进行删除的操作
// 输入监听
function onKeyDown(e) {
let atInput = document.getElementById('atInput');
if (e.code == 'Backspace') {
var range = selection.getRangeAt(0);
var removeNode = null;
if (range.startOffset <= 1 && range.startContainer.parentElement.className != "atFont")
removeNode = range.startContainer.previousElementSibling;
if (range.startContainer.parentElement.className == "atFont")
removeNode = range.startContainer.parentElement;
if (removeNode)
atInput.removeChild(removeNode);
}
}
这样,我们的 @ 功能就基本做好了