Vue实现contenteditable元素双向绑定的方法详解
作者:诸葛小愚
前言
如何实现一个即时通讯的聊天页面,网上有很多的开源或不开源的成品,可以直接使用,或者简单修改后使用。但在实际项目中,直接使用开源的即时通讯往往不是我们想要的,如何自己开发一个聊天页面呢。本文就着学习的目的,从0开始一步步实现一个聊天框的开发,至于消息的发送和接收,这个就得依靠后端大佬了。
在开发前,首先得明确用什么来实现。用input
输入框和textare
文本输入肯定是不行的,这两个只能输入文本类数据(输入法表情也算),想要在输入框内展示图片、表情包或回复消息等复杂内容时就不行了。灵光一闪,展示图片这些用富文本不就好了,但仔细想想,使用富文本不就引入额外的组件了嘛。想要自己实现一个类似“富文本”输入框,就需要借助contenteditable
来实现。
本项目需要解决的问题:
实现一个可输入文本、图片等复杂消息的输入框
实现数据的“双向绑定”
contenteditable
contenteditable
是所有HTML元素都有的枚举属性,表示元素是否可以被用户编辑。可用于所有元素,但是不是所有元素对该属性都起作用。如果元素支持该属性,并且设置了contenteditable
,则浏览器会修改元素以允许编辑。
关于contenteditable
需要注意:
- true或者空字符串,表示元素可编辑
- false,表示元素不可编辑
- 如果没有设置该属性的值,则其值被视为空字符串
- 如果没有设置该属性或者设置了无效值,则其值默认继承自父元素。如果父元素是可编辑的,那么该子元素也是可编辑的;否则,该子元素不可编辑。
- 虽然该属性可以设置true、false,但是
contenteditable
是一个枚举属性而不是布尔属性。
本项目采用对div
增加contenteditable
属性实现输入框。
基础使用
首先新建一个Vue项目(如果只是实现demo,也可以不创建Vue项目),vue create simple-chat-inputbox
,采用默认的即可(新版vue-cli默认新建的是Vue3项目,本项目采用Vue2)。
然后进入项目,npm run serve
运行起来,不运行怎么能看到效果呢。
在实际开发前,先做好准备工作:
- 初始化本地仓库,并关联远端仓库(可选)
- 取消
App.vue
中的默认样式,隐藏HelloWorld
新建InputBox.vue
,作为输入框组件
... <div class="input-box" contenteditable="true"></div> ... <style scoped> .input-box { width: 400px; height: 250px; border: 1px solid #6e6e6e; outline: none; /* 隐藏聚焦时外边框 */ padding: 10px; } </style>
注册并引入InputBox.vue
,运行后如下图:
此时就可以在输入框中就可以输入你想输入的内容,但是仅仅是输入,怎么获取值,以及怎么实现双向绑定呢。
进阶使用
在自定义事件 — Vue.js (vuejs.org)有一句话:
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的事件,仍然需要在组件的 props
选项里声明 value
这个 prop
根据此特性,我们再来改造一下项目:
虽然div
变成了可编辑,但是并不能像input
一样直接使用v-model
来完成双向绑定,但我们可以另辟蹊径,模拟实现双向绑定。首先我们需要创建两个组件:InputBox.vue
和DivEditable.vue
,分别表示父组件和子组件,对外只提供父组件。
对于父组件来说,只需要使用v-model
传入值就行。
// InputBox.vue <template> <div> <DivEditable v-model="inputContent" /> <input type="text" v-model="inputContent"> <div @click="changeValue">父组件修改子组件的值</div> </div> </template> <script> import DivEditable from '@/components/DivEditable' export default { name: 'inputBox', data() { return { inputContent: '', } }, watch: { inputContent(val) { console.log('父组件接收到的输入框的值', val); } }, components: { DivEditable }, methods: { changeValue() { this.inputContent = this.model1; } } } </script>
子组件处理就相对复杂点,需要接收值并且监听事件:
<template> <div ref="editor" class="input-box" contenteditable="true" @input="inputText" @blur="inputBlur" @focus="inputFocus"></div> </template> <script> export default { name: 'inputBox', props: ['value'], // 父组件v-model绑定的prop data() { return { isBlur: true, // 解决赋值时光标自动定位到起始位置 } }, watch: { value(val) { if (this.isBlur) { this.$refs.editor.innerHTML = val; } } }, methods: { // 监听输入框内容 inputText() { console.log('子组件输入框的输入内容', this.$refs.editor.innerHTML); this.$emit('input', this.$refs.editor.innerHTML); }, inputFocus() { this.isBlur = false; }, inputBlur() { this.isBlur = true; } } } </script> <style scoped> .input-box { width: 400px; height: 250px; border: 1px solid #6e6e6e; outline: none; /* 隐藏聚焦时外边框 */ padding: 10px; } </style>
虽然div
不可以使用v-model
,但是可以监听input
、focus
、blur
等事件。上述代码其实并不复杂,主要就是利用了v-model
的特性。有一点需要说明,由于是“双向绑定”,子组件里面输入的值会通过父组件赋值到子组件,导致子组件的光标始终处于起始位置,因此需要增加一个isBlur
变量,用来解决这个问题。感兴趣的可以试一下去掉isBlur
会是什么效果。
运行结果如下图:
通过修改父组件输入框的值,子组件对应的值也会发生改变;同理,修改子组件也是同样的效果。基本实现了“双向绑定”。
总结
本文基于contenteditable,实现可“双向绑定”的输入框
实现双向绑定,可以动态赋值,或者根据用户的输入实时处理,例如输入@的时候弹出渲染的弹窗,在选择用户后再插入到输入框
项目完整代码可参考 项目地址,接下来将陆续介绍怎么实现粘贴文本、图片,以及对光标的处理。
到此这篇关于Vue实现contenteditable元素双向绑定的方法详解的文章就介绍到这了,更多相关Vue元素双向绑定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!