全手打原创,转载请标明出处: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。

具体的可自行学习 → https://cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model

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组件的更多相关文章

  1. Vue父子组件数据双向绑定,子组件可修改props

    第一种,子组件通过监听父组件数据,子组件改变数据之后通知给父组件 原文链接:https://blog.csdn.net/m0_37728716/article/details/81776929 父组件 ...

  2. 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定

    前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...

  3. Vue的数据双向绑定和Object.defineProperty()

    Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...

  4. 我的angularjs源码学习之旅3——脏检测与数据双向绑定

    前言 为了后面描述方便,我们将保存模块的对象modules叫做模块缓存.我们跟踪的例子如下 <div ng-app="myApp" ng-controller='myCtrl ...

  5. Angular数据双向绑定

    Angular数据双向绑定 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.Angul ...

  6. AngularJS中数据双向绑定(two-way data-binding)

    1.切换工作目录 git checkout step-4 #切换分支,切换到第4步 npm start #启动项目 2.代码 app/index.html Search: <input ng-m ...

  7. vuejs数据双向绑定原理(get & set)

    前端的数据双向绑定指的是view(视图)和model(数据)两者之间的关系:view层是页面上展示给用户看的信息,model层一般是指通过http请求从后台返回的数据.view到model的绑定都是通 ...

  8. 原生js实现数据双向绑定

    最近接触了vue,在谈到vue等等的mvvm框架之前,先了解什么是数据双向绑定以及如何利用原生JS实现数据双向绑定 单向数据绑定 指先把模板写好,然后把模板和数据(数据可能来自后台)整合到一起形成HT ...

  9. vue中数据双向绑定的实现原理

    vue中最常见的属v-model这个数据双向绑定了,很好奇它是如何实现的呢?尝试着用原生的JS去实现一下. 首先大致学习了解下Object.defineProperty()这个东东吧! * Objec ...

随机推荐

  1. android studio学习----通过github的URL怎么导入新的工程

    这一切的前提是你装了git,有了github帐号,之后就很简单,但是导入之后交给android studio 也会发生各种编译错误,这个时候就需要自己去一一解决了,主要还是  引用依赖版本的问题 第一 ...

  2. Qt中QWidget、QDialog和QMainWindow

    QWidget 类是所有用户界面对象的基类.只有一个"页面" QMainWindow 是一个"窗口".含有菜单栏.状态栏.工具栏.停靠窗口.中心窗口 QDial ...

  3. Android 一个TextView中设置多种不同大小的字体,设置超链接

    以前项目中要是遇到这样的UI设计,都是傻不拉唧的分为三个TextView来实现,今天在微信中无意中看了一篇公众号文章,发现原来只要一个TextView就可以搞定啦,人生最悲哀的事情莫过于工作了这么久啦 ...

  4. 第二篇Scrum冲刺博客

    第二篇Scrum冲刺博客 一.站立式会议 提供当天站立式会议照片一张 二.每个人的工作 成员 已完成工作 明天计划完成的工作 遇到的困难 林剑峰 初步学习小程序的编写.博客园的撰写 初步完成用户界面 ...

  5. kubernetes学习Service之headless和statefulSet结合

    一.首先说headless Service和普通Service的区别 headless不分配clusterIP headless service可以通过解析service的DNS,返回所有Pod的地址 ...

  6. 05. redis事务

    目录 Redis 事务 事务 1. 命令有序 2. 始终原子 开启使用事务 Redis事务中出现错误 1. EXEC前的错误 2. EXEC后的错误 为什么出错了不支持roll backs? Redi ...

  7. pyecharts的使用

    折线图1 import pyecharts.options as opts from pyecharts.charts import Line ​ x_data = ["Mon", ...

  8. elasticsearch 索引和mapping导入导出命令

    导mapping:elasticdump \ --input=http://192.168.102.13:9200/search_v1 \ --output=http://192.168.102.69 ...

  9. 在istio中让prometheus跑起来

    使用microk8s安装,默认的prometheus已就位. 可直接弄. 一,映射本地端口(注意,命令行最后的两个端口,前一个为要映射的本地端口,后一个为POD的服务端口,如果本地相同端口被占用,则要 ...

  10. IDEA中常用的一些设置

    一.idea常用设置1.报错级别    idea默认不会像eclipse一样需要ctrl+s进行保存,并且在保存时会进行编译(可以在File>Settings>Build,Executio ...