前端 @功能的实现,在react、vue中通用

废话不多说,先上需求:
在输入框中输入@后,弹出选人的浮框,然后选择人(可多选),选完后,关闭浮框,然后在输入框中加入@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 = '&nbsp;';
      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]);
    }
  }

结束。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值