iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >vue实现At人文本输入框示例详解
  • 540
分享到

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

2024-04-02 19:04:59 540人浏览 独家记忆
摘要

目录知识前置需求分析实现创建能够输入文本的文本框添加at功能后记知识前置 基于Vue手把手教你实现一个拥有@人功能的文本编辑器(其实就是微信群聊的输入框) Selection&nbs

知识前置

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

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

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

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

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

需求分析

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

实现

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

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

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

<!--main.vue!-->
<template>
  <div>
    <Editor v-model="value"/>
  </div>
</template>
<!--editor.vue!-->
<template>
  <div>
    <div
      class="editor"
      contenteditable="true"
      @input="input"
    />
  </div>
</template>
<script>
export default {
  computed: {
    editor() {
      return this.$refs.editor || {}
    }
  },
  methods: {
    input(e) {
      this.$emit('input', this.getEditorhtml())
    },
    getEditorHtml() {
      return this.editor.innerHTML || ''
    }
  }
}
</script>
<style lang="less" scoped>
.editor{
  overflow-y: auto;
  background: #F4F6FB;
  border-radius: 4px;
  border: 1px solid transparent;
  min-height: 40px;
  max-height:200px;
  padding: 14px 9px;
  line-height: 20px;
  &:empty{
    &::before{
      content:'输入你想对他/她说的话,然后@她!';
      color: #999;
    }
  }
  &:focus{
    outline: none;
    border-color: #3656C6;
    border-radius: 4px;
  }
}
</style>

效果如下图所示

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

添加at功能

这里的需求主要分四步走

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

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

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

input(e) {
  if (e.data === '@') {
    // 弹出用户选择框
    this.$refs.UserList.show()
    // 失去焦点,退出手机的软体键盘
    this.editor.blur()
  }
  this.$emit('input', this.getEditorHtml())
},

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

userItemClick(item) {
  const dom = this.createAtDom(item)
  this.$refs.editor.innerHTML = this.$refs.editor.innerHTML + dom.outerHTML
  this.$refs.UserList.close()
},
createAtDom(item) {
  const dom = document.createElement('span')
  dom.classList.add('active-text')
  // 这里的contenteditable属性设置为false,删除时可以整块删除
  dom.setAttribute('contenteditable', 'false')
  // 将id存储在dom元素的标签上,便于后续数据处理
  dom.setAttribute('data-id', item.id)
  dom.innerHTML = `&nbsp@${item.name}&nbsp`
  return dom
},

效果入下图所示

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

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

Selection 对象

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

//改造input函数
input(e) { 
  if (e.data === '@') {
    // 保存焦点位置
    this.saveIndex()
    // 弹出用户选择框 
    this.$refs.UserList.show() 
    // 失去焦点,退出手机的软体键盘
    this.editor.blur() 
  }
  this.$emit('input', this.getEditorHtml()) 
},
// 添加saveIndex方法
async saveIndex() {
  // 获取selection对象
  const selection = getSelection()
  // 保存当前焦点的位置
  this.selectionIndex = selection.anchorOffset
},
// 改造userItemClick函数
userItemClick(item) {
  const dom = this.createAtDom(item)
  this.aDDData(item)
  this.$refs.UserList.close()
},
// 添加dom节点到指定位置
addData(item){
  const html = this.editor.innerHTML
  const leftInnerHtml = html.substring(0, this.selectionIndex - 1)
  const dom = this.createAtDom(item)
  const rightInnerHtml = html.substring(this.selectionIndex, html.length)
  this.editor.innerHTML = leftInnerHtml + dom.outerHTML + rightInnerHtml
}

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

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

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

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

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

也就是说

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

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

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

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

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

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

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

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

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

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

// 改造一下saveIndex
async saveIndex() {
  const selection = getSelection()
  this.selectionIndex = selection.anchorOffset
  const nodeList = this.editor.childNodes
  // 保存当前编辑的dom节点
  for (const [index, value] of nodeList.entries()) {
    // 这里第二个参数要配置成true,没配置有其他的一些小bug,这里不展开讲,详细可以看文档
    if (selection.containsNode(value, true)) {
      this.dom = value
      this.domIndex = index
    }
  }
},

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

// 改造一下addData方法
addData(item) {
  const html = this.dom.textContent
  const leftText = html.substring(0, this.selectionIndex - 1)
  const dom = this.createAtDom(item)
  const rightText = html.substring(this.selectionIndex, html.length)
  this.dom.textContent = leftText + dom.outerHTML + rightText
},

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

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

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

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

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

addData(item) {
  const text = document.createDocumentFragment()
  const span = document.createElement('span')
  const html = this.dom.textContent
  // 左边的节点
  const textLeft = document.createTextNode(html.substring(0, this.selectionIndex - 1) + '')
  // 这里如果textLeft是个空的文本节点,会导致@用户无法删除,这里添加一个判断,如果是空,则插入一个空的span节点
  text.appendChild(textLeft.textContent ? textLeft : span)
  // 加入@人的节点
  text.appendChild(this.createAtDom(item))
  // 右边的节点
  const textRight = document.createTextNode(html.substring(this.selectionIndex, html.length))
  textRight.textContent && text.appendChild(textRight)
  this.editor.insertBefore(text, this.dom)
  this.editor.removeChild(this.dom)
},

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

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

后记

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

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

以上就是vue实现At人文本输入框示例详解的详细内容,更多关于vue At人文本输入框的资料请关注编程网其它相关文章!

--结束END--

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

本文链接: https://www.lsjlt.com/news/167683.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • vue实现At人文本输入框示例详解
    目录知识前置需求分析实现创建能够输入文本的文本框添加at功能后记知识前置 基于vue手把手教你实现一个拥有@人功能的文本编辑器(其实就是微信群聊的输入框) Selection&nbs...
    99+
    2024-04-02
  • js实现验证码输入框示例详解
    目录前言思路遇到的问题HTMLCSSJS前言 验证码输入框应该是很常见的需求吧,不知道各位小伙伴在在遇到的时候是选择找一找插件还是自己动手撸一个呢?我花了点时间撸了一个出来,实际体验...
    99+
    2024-04-02
  • pygame实现中文输入框的示例
    目录1.效果2.源码3.用法注意: pygame2.0已经支持输入法了,本文的方法仅供参考 为了解决pygame不能输入中文的问题,就写了一个中文输入框 1.效果 2.源码 以下...
    99+
    2024-04-02
  • CSS实现Google Material Design文本输入框风格的示例
    小编给大家分享一下CSS实现Google Material Design文本输入框风格的示例,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!虽然今天我们有很多框架可以帮我们实现这些风格,不过通过学习在底层是如何使用纯CSS实...
    99+
    2023-06-08
  • Vue实现输入框@功能的示例代码
    目录前言成员列表创建使用输入框获取光标的坐标保存光标插入文本运行结果总结前言 前几篇文章中分别介绍了如何实现聊天输入框的双向绑定、回车键发送、粘贴文本图片等功能,本着完善输入框的目的...
    99+
    2024-04-02
  • C++中简单的文本文件输入/输出示例详解
    为了便于理解,我们把cout 用于控制台输出时的一些情况和写入到文本文件的情况进行类比: cout 控制台输出 包含头文件 iostream 头文件 iostream 定义了一个 o...
    99+
    2024-04-02
  • Bootstrap实现基本的输入框组实例
    这篇文章主要讲解了“Bootstrap实现基本的输入框组实例”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Bootstrap实现基本的输入框组实例”吧!通过...
    99+
    2024-04-02
  • Qt如何实现输入框@联系人的@检测的示例
    目录前言解决思路方法一:按键事件方法二:光标位置变化信号前言 最近在用qt做通讯类的客户端软件,其中有一项功能类似与微信和QQ聊天时的@全体人员和@指定联系人。下面就来探究一下如何实...
    99+
    2022-11-13
    Qt 输入框@检测 Qt 输入框@
  • vue electron实现无边框窗口示例详解
    目录一、前言二、实现方案1.创建无边框窗口2.创建windows窗口控件组件三、后记一、前言 无边框窗口是不带外壳(包括窗口边框、工具栏等),只含有网页内容的窗口。对于一个产品来讲,...
    99+
    2024-04-02
  • jQuery如何实现TEXT文本框输入时的提示信息
    这篇文章主要介绍了jQuery如何实现TEXT文本框输入时的提示信息,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在搜索框中,输入之前框内有...
    99+
    2024-04-02
  • useState 解决文本框无法输入的问题详解
    目录正文什么是 useState设置值和 onChange 属性总结正文 有时,会遇到无法在 React 输入文本字段中键入内容的问题。在本文中,将研究如何解决无法在 React ...
    99+
    2023-03-15
    useState 文本框无法输入 useState 文本框输入
  • vue实现input文本框只能输入0-99的正整数问题
    目录vue input文本框只能输入0-99的正整数通过自定义指令实现文本框只能输入正整数vue input文本框只能输入0-99的正整数 利用vue里面自带的watch监听器就可以...
    99+
    2022-11-13
    vue文本框输入正整数 vue input输入正整数 vue input只能输入正整数
  • python中Tkinter 窗口之输入框和文本框的实现
    在制作登录页面时学习了TK弹窗的输入文本框的使用,下面让我们一起来学习一下TK弹窗中的输入框及文本框的使用方法吧! 输入框 要想制作一个输入弹窗其实比较简单,只需要几行代码就可以了...
    99+
    2024-04-02
  • vue实现列表展示示例详解
    目录Vue 的CSS之deep语法::v-deepclassPrefix 前缀给元素绑定class总结Object.freeze关于Vue和ts的配合问题ISO8601和dayjs库...
    99+
    2024-04-02
  • Vue实现输入框回车发送和粘贴文本与图片功能
    目录实现回车发送粘贴文本粘贴图片总结上一篇中,我们初步新建了一个可“双向绑定”的聊天输入框,结合实际使用的场景,如果仅仅只是实现了“双向绑定&rd...
    99+
    2024-04-02
  • html5配合css3实现带提示文字的输入框
    这篇文章主要介绍“html5配合css3实现带提示文字的输入框”,在日常操作中,相信很多人在html5配合css3实现带提示文字的输入框问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大...
    99+
    2024-04-02
  • 人工智能TextGeneration文本生成原理示例详解
    目录承上启下原理选取预测的下一个字符的三种方式训练总结案例承上启下 上一篇文章我们介绍了 RNN 相关的基础知识,现在我们介绍文本生成的基本原理,主要是为了能够灵活运用 RNN 的...
    99+
    2023-01-05
    人工智能Text Generation Text Generation文本生成
  • Android实现IP地址输入框的方法示例代码
    前言本文主要给大家介绍了关于Android实现IP地址格式输入框的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。实现效果图:解决方案:      1.添加4个Ed...
    99+
    2023-05-30
    android ip地址 输入框
  • Vue+Element一步步实现动态添加Input_输入框案例
    目录输入式动态添加单选式动态添加组合式动态添加组合式动态添加(回传名称)单选、多选组合式数据回显完整示例总结单选切换多选(补充)下拉框渲染卡顿问题(补充)双循环遍历优化输入式动态添加...
    99+
    2024-04-02
  • Go语言实现彩色输出示例详解
    目录简介说明支持Linux彩色输出支持Windows彩色输出Golang IDE输出是不支持的使用CODE DEMO小结简介 在逛github时发现一个好玩的Go项目,彩色输出文本 ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作