前端聊天怎么实现艾特@功能?高亮@信息,仿微信、qq艾特功能
创始人
2024-11-12 16:38:04

以上篇文章的代码为基准,继续扩展实现艾特@功能,没看过的可以点击链接查看前端实现输入框表情输入及发送,仿b站、qq小表情输入

此demo使用vue3

实现效果图
显示的艾特@输入框
艾特@消息发送效果

声明:这只是个demo,不涉及与后端交互,不过会在该交互的地方标记,如需实际应用于项目,请根据实际情况进行改造完善!

艾特列表组件定义

     

父组件主逻辑处理

首先修改消息输入框,增加输入事件,用以判断显示输入框。且在上篇中发送消息采用了@keydown.enter.prevent绑定事件的形式发送,直接改成@keydown,用判断的方式发送,因为显示艾特输入框时,按回车是要选择群员的

       

定义相关变量,群员列表、列表组件定位坐标、是否显示艾特组件

// 群员列表 const userGroupData = reactive([   { name: "用户A", id: 1, avatar: require("@/assets/logo.png") },   { name: "用户B", id: 2, avatar: require("@/assets/logo.png") },   { name: "用户C", id: 3, avatar: require("@/assets/logo.png") }, ]); // 用户列表定位 const userGroupPosition = reactive({   x: "0px",   y: "0px", }); let showAtSelect = ref(false); 

定义输入事件,当输入@时,获取输入框光标相对页面坐标,并显示输入框组件。(失焦时,在失焦事件getAfterBlurIndex里,使用 setTimeout(() => {
showAtSelect.value = false;}, 100);隐藏艾特组件)

// 输入框输入事件 function msgInputFun(e) {   if (e.data === "@") {     setTimeout(() => {       const { x, y } = getCursorPosition();       showAtSelect.value = true;       userGroupPosition.x = x + "px";       userGroupPosition.y = y + "px";     }, 100);   } else {     showAtSelect.value = false;   } } // 获取输入框中的光标相对页面的坐标 function getCursorPosition() {   let sel = window.getSelection();   let range = document.createRange();   range.selectNode(sel.focusNode);   range.setStart(sel.focusNode, sel.focusOffset);   const { x, y } = range.getBoundingClientRect();   return { x, y }; } 

这个时候可以显示出来群员选择列表了,再来定义键盘事件,在列表组件显示时阻止上下按钮的默认行为。如果不显示且按下回车时发送消息

// 显示艾特选择组件时,阻止输入框默认上下按键及回车行为 function msgInputKeyDown(e) {   if (showAtSelect.value && ["ArrowUp", "ArrowDown", "Enter"].includes(e.key)) {     return e.preventDefault();   }   if (e.key === "Enter") {     e.preventDefault();     sendMsg();   } } 

然后编写选择群员事件,先获取并设置输入框选区,创建一个span标签节点,把被艾特人相关信息填入后插入到输入框子节点里。注意:创建的span标签里写了一个@,所以需要chatInputOffset.deleteContents()删除原@字符

function selectAtUser(user) {   showAtSelect.value = false;   msgInput.value.blur();   getInputSelection();   chatInputOffset.setStart(focusNode, focusOffset.value - 1);   chatInputOffset.setEnd(focusNode, focusOffset.value);   chatInputOffset.deleteContents();   const atElement = `user.id}' userName='${user.name}' contentEditable="false" class="at-msg" style="color:#2E77E5">@${user.name} `;   chatInputOffset.collapse(false);   const node = chatInputOffset.createContextualFragment(atElement);   let c = node.lastChild;   chatInputOffset.insertNode(node);   if (c) {     chatInputOffset.setEndAfter(c);     chatInputOffset.setStartAfter(c);   }   let j = window.getSelection();   j.removeAllRanges();   j.addRange(chatInputOffset); } 

这个时候就可以把@消息发送出去了

let msgType = ref("text"); // 发送的消息类型  function sendMsg() {   console.log("msgInput:", msgInput);   chatMsgRecord.value = ""; // 先清空一下旧消息   msgType.value = "text"; // 初始化消息类型   msgInput.value.childNodes.forEach((element) => {     // 如果是emoji表情图片的话,则转义     if (element.nodeName === "IMG" && element.className === "emoji") {       chatMsgRecord.value += element.alt;     } else if (element.className === "at-msg") {       chatMsgRecord.value += element.innerText + " ";       msgType.value = "groupChatAt"; // 根据实际约束类型修改     } else {       chatMsgRecord.value += element.data;     }   });   // 清空输入框中的内容   msgInput.value.innerHTML = "";   msgInput.value.innerText = "";   // 在这里使用websocket把数据发送给后端   // socketSend({text:chatMsgRecord,type:msgType}) } 

如有更好的意见欢迎在评论区指出!

相关内容

热门资讯

裸辞做“一人公司”,我后悔了 去年这个时候,一位以色列程序员正在东南亚旅行。他顺手把一个在脑子里转了很久的想法做成了产品,一个让任...
南京建成国内首个Pre-6G试... 4月21日,2026全球6G技术与产业生态大会在南京开幕。全息互动技术展台前,一名远在北京的工作人员...
超梵求职受邀参加“2025抖音... 超梵求职受邀参加“2025抖音巨量引擎成人教育行业生态大会”,探讨分享优质内容传播,服务万千学员。 ...
摩托罗拉Razr 2026(R... IT之家 4 月 22 日消息,摩托罗拉宣布新一代 Razr 折叠手机将于 4 月 29 日在美国发...
库克卸任,特纳斯领航:苹果新纪... 苹果首席执行官蒂姆·库克将卸任,硬件工程主管约翰·特纳斯将接任,苹果公司今天宣布此事。 库克将在夏季...