废话不多说,先上需求:
在输入框中输入@后,弹出选人的浮框,然后选择人(可多选),选完后,关闭浮框,然后在输入框中加入@XXX,然后焦点定位刚开始输入@的位置。删除的时候,@XXX要一块删除。
分析:
1.因为要在输入框中把@xxx高亮显示,所以需要用到可编辑元素。
<div contentEditable></div>
2.用到selection和range对象
开撸:
<div
className={styles.talkInput}
id="talkInput"
contentEditable
onKeyDown={onTalkKeyDown}
onInput={changeTalkContent}>
</div>
function changeTalkContent(e) {
setTalkContent(e.target.innerText);
if (e.nativeEvent.data === '@') { //监听输入@
openSelect();
}
}
function openSelect() {
//打开选人浮层
dispatch({ type: 'share/update', payload: { visible: true } });
let selection = window.getSelection();//创建selection,用法可以直接去MDN 去查看 https://developer.mozilla.org/zh-CN/docs/Web/API/Selection
setFocusNode(selection.focusNode); // 缓存光标所在节点
setFocusOffset(selection.focusOffset); // 缓存光标所在节点位置
let inputId = document.getElementById('talkInput');
inputId.blur();
}
选完人后
function onSelectSubmit(val) {
//val 选择的人的信息
let userList = val.sharedUserIds;
setDirectUsers([...directUsers, ...userList]);
let inputId = document.getElementById('talkInput');
let selection = window.getSelection();
let range = window.getSelection().getRangeAt(0);
//选中输入的@符号
range.setStart(focusNode, focusOffset - 1);
range.setEnd(focusNode, focusOffset);
//删除输入的@符号
range.deleteContents();
userList.forEach((s) => {
var spanNode1 = document.createElement('span');
var spanNode2 = document.createElement('span');
spanNode1.className = styles.atFont;
spanNode1.innerHTML = '@' + s.nickName;
spanNode1.contentEditable = false;//设置为不可编辑,是为了整个@xxx一起删掉
spanNode1.setAttribute('data-userId', s.userId);
spanNode2.innerHTML = ' ';
var frag = document.createDocumentFragment(),
node,
lastNode;
frag.appendChild(spanNode1);//在 Range 的起点处插入一个节点。
while ((node = spanNode2.firstChild)) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
selection.extend(lastNode, 1);
selection.collapseToEnd();//将当前的选区折叠到最末尾的一个点。
});
}
删除@xxx的时候,要把@xxx一起删除
function onTalkKeyDown(e) {
let inputId = document.getElementById('talkInput');
let delUserId = null,
canDel = true;
if (e.key == 'Backspace') {//按下删除键时
let range = window.getSelection().getRangeAt(0);
let removeNode = null;
//焦点在@xxx后面的时候
if (range.startOffset <= 1 && range.startContainer.parentElement.className.indexOf('atFont') < 0) {
removeNode = range.startContainer.previousElementSibling;
}
// 点一下@xxx的时候,直接删除@xxx的时候,此时range.startContainer是@xxx,range.startContainer.parentElement是<span>@xxx</span>
if (range.startContainer.parentElement.className.indexOf('atFont') >= 0) {
removeNode = range.startContainer.parentElement;
}
if (removeNode) {
delUserId = removeNode.getAttribute('data-userid');
inputId.removeChild(removeNode);
}
let userList = [...directUsers],
newUserList = [];
userList.forEach((s) => {
if (canDel && s.userId === delUserId) {
canDel = false;
} else {
newUserList = [...newUserList, { ...s }];
}
});
setDirectUsers([...newUserList]);
}
}
结束。