!
也想出现在这里? 联系我们
广告位
当前位置:首页>技术分享>网站运维>vue实现At人文本输入框示例详解

vue实现At人文本输入框示例详解

基于vue手把手教你实现一个拥有@人功能的文本编辑器(其实就是微信群聊的输入框)

Selection对象,表示用户选择的文本范围或插入符号的当前

developer.mozilla.org/zh-CN/docs/…

contenteditable是一个枚举属性,表示元素是否可被用户编辑。

developer.mozilla.org/zh-CN/docs/…

需求分析

  • 文本框能够输入文本(太简单了)
  • 能够at人

实现

创建能够输入文本的文本框

在这里主要利用 contenteditable属性,让创建的 div 能够编辑

利用input事件监听数据变化,将数据同步出去


效果如下图所示

vue实现At人文本输入框示例详解

这个时候我们就实现了一个能够绑定数据的文本输入框,第一个需求完美实现,接下来实现第二个需求(开始折磨)

添加at功能

这里的需求主要分四步走

  • 当用户输入@字符时,弹出用户选择列表
  • 当用户点击@的人时,收回@列表
  • 将@的人嵌入到文本框中
  • 删除@的人时,要直接整块删除

首先我们先实现一个用户选择的列表,这里主要涉及到的都是界面的编辑和动画的设置,不展开描述,直接上效果图**(完整代码会在文末给出)**

vue实现At人文本输入框示例详解

接着我们要改造input函数,检测当用户输入为@符号时,弹出选择框

当用户点击要@的人时,关闭选择列表,同时将@人的人插入到文本框中

效果入下图所示

vue实现At人文本输入框示例详解

相信有不少朋友已经发现问题了,这种方式只能怪将@的人添加到文本的最末尾,但如果我编辑文本的时候,光标的位置不是在文本的最后,而是在文本之间的某个位置,那此时我们这么添加@的人就会有点反直觉。

所以我们在弹出选择列表的时候,要把当前光标所处的位置标记下来,插入时,就插入到对应的位置上。所以此时就要抛出我们本文最重要的一个对象

Selection对象

我们要利用 Selection对象的 anchorOffset属性去获取当前焦点的位置,此时我们改造input函数,添加 saveIndex 方法,在弹出文本框失焦之前,保存当前焦点的位置。


这个时候我们就可以把@的人添加到我们之前光标的位置了,效果如下如所示

vue实现At人文本输入框示例详解

但在某天,你突发奇想,想同时对很多个女神发出邀请,这个时候你发现,@多人的时候,出现问题了

vue实现At人文本输入框示例详解

我们插入的@人的节点被硬生生拆成了字符串,这很明显跟我们的预期有差别呀,这个时候我们应该分析一下我们编辑时的dom结构,如下图所示

vue实现At人文本输入框示例详解

vue实现At人文本输入框示例详解

为了便于理解我画了个简单的图

vue实现At人文本输入框示例详解

我们在插入dom节点之前,文本框的所有内容都是属于editor节点下唯一一个textNode节点,插入dom节点之后,editor节点新增了一个子节点,而 Selection.anchorOffset 这个属性获取到的焦点位置,实际上是相对于当前所处node节点而言的(←理解这个概念,非常重要)

也就是说

我们第一次插入dom节点,焦点位置是相对于当前节点,也就是editor节点下的唯一一个textNode节点计算

第二次插入dom节点,焦点位置是相对于当前节点,也就是当前textNode节点计算

后续插入的dom节点,焦点位置计算方式同上

所以当我们有如下需求的时候

vue实现At人文本输入框示例详解

Selection.anchorOffset 的返回值是5,而我们的addData方法,实际上是从editor.innerHtml的第一个位置开始算,第五个位置刚好插到了span节点的里面,所以就出现了上文乱码的问题。

所以我们解决的方案,就是在保存焦点位置的时候,同时保存当前编辑的那个textNode节点,那我们怎么找到当前正在编辑的那个textNode节点呢?

Selection 对象提供了一个方法 Selection.containsNode()

mdn文档是这么描述的:判断指定的节点是否包含在 Selection 中 (是否被选中)

在我们这个场景中,通俗点讲就是,我这个节点到底是不是编辑的节点?是你就返回true,不是就false

所以我们可以在弹出用户选择框之前,遍历一下editor节点的子节点,找出我们当前编辑的那个textNode节点

现在当前编辑的节点和编辑的位置都已经保存下来了,剩下的就是把@人的节点插入到我们编辑的那个textNode节点里面就完成了。

然而,当我们再次运行代码调试的时候,出现了我们预期外的结果

vue实现At人文本输入框示例详解

是我们代码有问题吗?说是其实不算是,说不是,其实也算是(废话)

其实是因为我们编辑的是textNode节点,而textNode节点就算包含了dom结构,他也是把结构当成文本输出到页面上,所以在这里

  • 我们应该创建一个新的结构,也就是我们的文档片段DocumentFragment
  • 然后把我们的节点结构插入到DocumentFragment
  • 接着利用Node.insertBefore()方法,把DocumentFragment插入到原来编辑的textNode节点之前,再用Node.removeChild()方法把原来编辑的textNode节点删除
  • 这样就可以实现正常的插入

为了方便理解,可以看一下流程图

vue实现At人文本输入框示例详解

当我们处理到这里时,就可以多次at想要at的人,效果如图

vue实现At人文本输入框示例详解

后续我们要将数据提取出来,可以根据v-model绑定的value进行解析,把插在标签里的数据提取出来,也可以根据自己的业务插入一些数据,这里不是重点,也不展开讲

后记

基本上文本编辑器的核心逻辑到这里就讲完了,但是这个demo在做的过程中,有好几个地方做了优化,特别是针对移动端软体键盘的进入和离开,还有焦点的对焦和失焦,都做了一些处理,但是在文章里头没有展开讲

想要详细了解的大佬们可以到我github仓库下载源码 github.com/adouni1996/…

给TA打赏
共{{data.count}}人
人已打赏
网站运维

如何在vue3中同时使用tsx与setup语法糖

2022-10-11 21:55:48

网站运维

JavaScript变量中var,let和const的区别(javascript let const var)

2022-10-11 21:56:34

声明 本站上的部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。无意侵害您的权益,请发送邮件至 admin@s9h.cn 或点击右侧 私信:少羽 反馈,我们将尽快处理。
2 条回复A文章作者M管理员
  1. 山青

    ?仅仅是沉溺于过去,那么,一个人将没有未来。

  2. qqqqqq

    学习一下,谢谢分享

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索