contenteditable联合v-html实现数据双向绑定的vue组件
全手打原创,转载请标明出处:https://www.cnblogs.com/dreamsqin/p/11466197.html
先看最终实现的demo效果图:


(1)上面看似文本域的大框是通过给div添加contenteditable=true属性实现的Vue组件DivEditable.vue;
(2)下面的输入框是父组件中与DivEditable绑定相同变量的输入框,用于展示数据的双向绑定效果;
(3)按钮实现绑定变量的赋值操作;
(4)DivEditable的blur事件可触发文本过滤或样式的变更等操作(专门留的组件接口);
可以看到,DivEditable中值的改变会影响输入框中的值,同样的,输入框中值改变也会影响DivEditable中的值,通过按钮给绑定变量赋值同时触发了输入框及DivEditable中值的改变。
1、contenteditable属性
用于设置或返回元素的内容是否可编辑。:
疑问:这时你可以能会想,这么麻烦,怎么不直接使用可编辑元素?比如我们最常见的有input、textarea。
解答:但如果你想要在输入的内容中加入html代码,并且还要正常渲染,就要与v-html结合使用,所以我们只能采用不可编辑元素并为其添加contenteditable为true的属性。
2、怎么实现DivEditable数据的双向绑定
犯傻1:一开始我天真的以为v-html与v-model一样,变量赋值后自带双向绑定,=.=事实证明还是太嫩;
犯傻2:于是我想那我再加一个v-model不就完事儿了,结果证明还是太嫩,浏览器直接报错'v-model' directives aren't supported on <div> elements;
最终只能自己上了:
(1)首先可以通过@input事件监听到输入值的变化,此时就可以获取到变化后的值并将其传递给父组件;
(2)虽然div不能添加v-model,但是在父组件中我调用DivEditable时却可以为其添加v-model;
(3)v-model中传入的值可以在子组件prop中获取的到;
(4)这时你再监听获取到的prop值,并将该值赋值给子组件中的v-html参数,双向绑定就搞定啦。
这里引入Vue官方描述:
自定义组件的v-model:一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,v-model的值将会传入子组件中的prop。
DivEditable.vue组件源码(可以结合我上面的步骤描述看会更容易理解):
<!-- Created by dreamsqin on 2019/9/5 -->
<template>
<div
class="div-editable"
contenteditable="true"
v-html="innerText"
@input="changeText"
@focus="isChange = false"
@blur="blurFunc"></div>
</template> <script>
export default {
name: 'DivEditable',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
innerText: this.value,
isChange: true
}
},
watch: {
value() {
if (this.isChange) {
this.innerText = this.value
}
}
},
methods: {
changeText() {
this.$emit('input', this.$el.innerHTML)
},
blurFunc() {
this.isChange = true
this.$emit('blurFunc')
}
}
}
</script> <style lang="scss">
.div-editable{
width: 100%;
height: 100%;
overflow-y: auto;
word-break: break-all;
outline: none;
user-select: text;
white-space: pre-wrap;
text-align: left;
&[contenteditable=true]{
user-modify: read-write-plaintext-only;
&:empty:before {
content: attr(placeholder);
display: block;
color: #ccc;
}
}
}
</style>
重点说明一下isChange参数的作用:
你可以先尝试拿掉它以及相关逻辑,看看最终会出现什么效果(输入一个字母光标就跑到前面去了,并且输入不了中文);
分析一下原因:
(1)通过打断点可以看到,当你输入的时候触发input事件,提交值给父组件中的v-model;
(2)但因为在子组件中又监听了v-model的值,所以整体形成了闭环;
(3)还需要重点说明的是光标问题,contenteditable与v-html所在的元素值的改变如果不是通过输入而是通过赋值实现,光标就会跑到最前面;
所以以输入中文为例,你刚打了一个字母,立马就触发了监听与变动,光标移到最前面,自然无法完成整个正常的输入。
解决办法:
只有当blur的时候再做赋值操作(isChange为true),focus状态下不做赋值(isChange为false);
至于初始为true的原因是在父组件中直接给绑定的变量赋值时子组件中还是需要触发赋值的(isChange为true);
除此之外,我还为组件提供了一个blur事件的函数接口,你可以做一些数据的过滤或者样式的变更,例如我demo中要高亮标签。
3、父组件调用
直接上源码:
<template>
<div class="test-page">
<div class="contain">
<div-editable
v-model="testContent"
@blurFunc="blurHighLight"></div-editable>
<el-input
class="input-style"
v-model="testContent"></el-input>
<el-button
class="button-style"
@click="changeText">改变值</el-button>
</div>
</div>
</template> <script>
import DivEditable from '@/components/DivEditable'
export default {
name: 'TestPage',
data() {
return {
testContent: 'dreamsqin'
}
},
components: {
DivEditable
},
methods: {
blurHighLight() {
// 这里做数据过滤或样式变更操作
},
changeText() {
this.testContent = '【标签1】dreamsqin'
this.blurHighLight()
}
}
}
</script> <style lang="scss">
.test-page{
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.contain{
width: 600px;
height: 250px;
border: 2px solid #000;
.input-style,.button-style{
margin-top: 10px;
}
.text-blue{
color: #2080F7;
}
}
}
</style>
contenteditable联合v-html实现数据双向绑定的vue组件的更多相关文章
- Vue父子组件数据双向绑定,子组件可修改props
		
第一种,子组件通过监听父组件数据,子组件改变数据之后通知给父组件 原文链接:https://blog.csdn.net/m0_37728716/article/details/81776929 父组件 ...
 - 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定
		
前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...
 - Vue的数据双向绑定和Object.defineProperty()
		
Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...
 - 我的angularjs源码学习之旅3——脏检测与数据双向绑定
		
前言 为了后面描述方便,我们将保存模块的对象modules叫做模块缓存.我们跟踪的例子如下 <div ng-app="myApp" ng-controller='myCtrl ...
 - Angular数据双向绑定
		
Angular数据双向绑定 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.Angul ...
 - AngularJS中数据双向绑定(two-way data-binding)
		
1.切换工作目录 git checkout step-4 #切换分支,切换到第4步 npm start #启动项目 2.代码 app/index.html Search: <input ng-m ...
 - vuejs数据双向绑定原理(get & set)
		
前端的数据双向绑定指的是view(视图)和model(数据)两者之间的关系:view层是页面上展示给用户看的信息,model层一般是指通过http请求从后台返回的数据.view到model的绑定都是通 ...
 - 原生js实现数据双向绑定
		
最近接触了vue,在谈到vue等等的mvvm框架之前,先了解什么是数据双向绑定以及如何利用原生JS实现数据双向绑定 单向数据绑定 指先把模板写好,然后把模板和数据(数据可能来自后台)整合到一起形成HT ...
 - vue中数据双向绑定的实现原理
		
vue中最常见的属v-model这个数据双向绑定了,很好奇它是如何实现的呢?尝试着用原生的JS去实现一下. 首先大致学习了解下Object.defineProperty()这个东东吧! * Objec ...
 
随机推荐
- Jira未授权SSRF漏洞复现(CVE-2019-8451)
			
0x00 漏洞背景 Jira的/plugins/servlet/gadgets/makeRequest资源存在SSRF漏洞,原因在于JiraWhitelist这个类的逻辑缺陷,成功利用此漏洞的远程攻击 ...
 - flink SourceFunction SinkFunction timeWindowAll reduce
			
1.实现SourceFunction接口生成数据源 /** * @Description: 产生数据 traceid,userid,timestamp,status,response time */ ...
 - 【Thinkphp】引入第三方类库常见问题
			
TP3.2在添加第三方sdk的时候,文件放在ThinkPHP/Library/Org文件夹下可独立创建文件夹(官方文档有其他思路)需对文件做以下修改. 1.第一应该修改文件的名称(下载的sdk一般是 ...
 - javascript之DOM(二Document对象)
			
javascript通过Document类型来表示文档.在浏览器中document是HTMLDocument对象(继承自Document)的一个实例,表示整个html页面.而且在浏览器中documen ...
 - kernel: nfsd: too many open TCP sockets, consider increasing the number of threads
			
在/var/log/syslog中看到如下报错: kernel: nfsd: too many open TCP sockets, consider increasing the number o ...
 - XGBoost使用教程(纯xgboost方法)一
			
一.导入必要的工具包# 导入必要的工具包import xgboost as xgb # 计算分类正确率from sklearn.metrics import accuracy_score二.数据读取X ...
 - Java精通并发-Condition方法实现分析与讲解
			
在上两次已经对Condition这个类的javadoc进行了完整的解读,接下来则对它里面的方法进行一下纵览,并进行官方的解读,如下: 下面一一来读一下各个方法的说明: await(): 上面这段说明已 ...
 - python测试开发django-69.templates模板过滤器filter
			
前言 templates 模板里面过滤器 filter 的作用是对变量的出来,比如小写转大写,替换一些特殊字符,对列表取值,排序等操作. 内置的过滤器有很多,本篇拿几个常用的过滤器做案例讲解下相关的功 ...
 - Vue 生成PDF并下载
			
实现原理 该功能原理是将页面转化伟canvas在把canvas转化为base64数据 最后将数据通过pdf.js生成下载,故需要和html2canvas一起使用 友情提醒这个pdf下载不能在app里直 ...
 - 网络协议 16 - DNS 协议
			
为什么在地址栏输入域名,就能直接访问到对应服务器?全局负载均衡和内部负载均衡又是什么?这些都和 DNS 解析息息相关,让我们一起来解密 DNS 解析. 其实说起 DNS 解析,应该都知道它很像 ...