本文已参与「新人创作礼」活动,一起开启掘金创作之路。
富文本编辑器TinyMCE在vue2中的使用以及动态绑定(解决双向绑定后光标跳到最左侧问题)
引入vue和tinyMCE
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
tinyMCE下载地址:www.tiny.cloud/get-tiny/se…
其中tinyMCE默认为英文,如需中文需要下载语言扩展包,下载地址:www.tiny.cloud/get-tiny/la… 将zh_CN.js文件复制到langs文件夹下
tinyMCE集成了绝大多数功能,但是调整行高的功能并没有,我们需要额外下载该插件。忘记之前在哪里找到的了,直接放上行高插件,下载地址:tinyMCE富文本编辑器line-height行高插件 该插件默认是德语,我们可以直接修改plugin.min.js文件源码改为中文提示,找到return{type开头的内容,修改为return{type:"listbox",text:"行间距",tooltip:"行间距"
初始化tinyMCE
工具栏的配置可以参考这篇文章 (blog.csdn.net/zjiang1994/…)
代码如下: 其中valid_elements、valid_style是将合法标签以及属性加入白名单的,我这里写的是根据策划要求来的,因人而异。官方文档说明:valid_elements,valid_style
tinymce.init({
selector: '#mytextarea',
language: 'zh_CN',
menubar: false,
valid_elements :"p[style],span[style],ul,ol,li,strong/b,em,h1,h2,h3,h4,h5,h6",
valid_style:{
"*":"color,font_size,text-align,line-height"
},
plugins:"code,textcolor,lists,lineheight,fullscreen",
toolbar: [
'undo redo | formatselect | bold italic forecolor fontsizeselect | alignleft aligncenter alignright | bullist numlist lineheightselect | code fullscreen | removeformat '
]
});
之后在html里引入这个textarea就可以了
其中通过tinyMCE.activeEditor.getContent()以及tinyMCE.activeEditor.setContent(val)可以手动获取、设置tinyMCE的值
<div><textarea id="mytextarea" v-model="model.content"></textarea></div>
其中需要注意的小细节:toolbar中的|是用来分割工具栏的,|和工具栏的名称中间必须有空格,否则无法识别该按钮。<textarea>外面一定要用<div>套一层,否则无法正常显示
组件化tinyMCE并设置双向绑定 为了方便使用,我们使用vue component将tinyMCE组件化。同时为了满足实时监控富文本内容的需求,不能采用最后在ajax方法中手动get/set内容的策略了,需要用v-model实时双向绑定。
定义传入的字段props: ['value'],用来接收父组件的值,当编辑器检测到有变化时调用$emit(‘input’, value)将数据写出到父组件
具体代码如下:
Vue.component('tinymce', {
props: ['value'],
watch:{
value(val){
tinyMCE.activeEditor.setContent(val);
}
},
mounted: function(){
var component = this;
tinymce.init({
target: this.$el.children[0],
language: "zh_CN",
menubar: false,
branding: false,
valid_elements: "p[style],span[style],ul,ol,li,strong/b,em,h1,h2,h3,h4,h5,h6",
valid_style: {
"*":"color,font_size,text-align,line-height"
},
plugins: "code,textcolor,lists,lineheight,fullscreen",
toolbar: [
'undo redo | formatselect | bold italic forecolor fontsizeselect | alignleft aligncenter alignright | bullist numlist lineheightselect | code fullscreen | removeformat '
],
setup: function(editor) {
editor.on('input change undo redo execCommand KeyUp', function(e) {
component.$emit('input', editor.getContent());
})
}
});
},
template: `<div><textarea style="height:300px" v-model="value"></textarea></div>`
});
每次修改编辑器的值其实还是会触发watch,从而调用tinyMCE.activeEditor.setContent(val),我只是在前后记录、移动了光标位置,保证输入时光标不会跳到最左侧。但在chrome浏览器下,输入中文时拼音会先填充在文本框里,这样就会触发input从而触发watch里的tinyMCE.activeEditor.setContent(val),导致输入法消失。所以这样来看,我们在编辑器里操作时是就是不应该去触发tinyMCE.activeEditor.setContent(val)!
最终的做法是加入一个flag标志,在编辑器触发input undo redo execCommand事件时将flag置为false,watch的时候只有flag为true时才调用tinyMCE.activeEditor.setContent(val),并且最终都置回true
最后决定不监听change是因为change事件需要失去焦点并且文本变化时触发,这样会导致先触发input再触发change,而由于数据已经在input事件中修改,所以change不会调用watch重新把flag置为true,所以这时候的flag就定格在了false。 这个方法应该也不是太好,但是我不能在这一个功能上再耗太多时间了,就先这样处理了,基本满足要求了。
完整代码
tinyMCE组件js
Vue.component('tinymce', {
props: ['value'],
data(){
return{
flag:true
}
},
watch:{
value(val){
if(this.flag){
tinyMCE.activeEditor.setContent(val);
}
this.flag=true;
}
},
mounted: function(){
var component = this;
tinymce.init({
target: this.$el.children[0],
language: "zh_CN",
menubar: false,
branding: false,
valid_elements: "p[style],span[style],ul,ol,li,strong/b,em,h1,h2,h3,h4,h5,h6",
valid_style: {
"*":"color,font_size,text-align,line-height"
},
plugins: "code,textcolor,lists,lineheight,fullscreen",
toolbar: [
'undo redo | formatselect | bold italic forecolor fontsizeselect | alignleft aligncenter alignright | bullist numlist lineheightselect | code fullscreen | removeformat '
],
setup: function(editor) {
editor.on('input undo redo execCommand', function(e) {
component.flag=false;
component.$emit('input', editor.getContent());
})
}
});
},
template: `<div><textarea style="height:300px" v-model="value"></textarea></div>`
});
需要引入tinyMCE的页面
<body>
<div id="test">
<tinymce v-model="content"></tinymce>
</div>
<!--vue-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--下载的tinyMCE-->
<script src="../plugins/tinymce/tinymce.min.js"></script>
<!--tinyMCE组件js-->
<script src="../js/tinymceTemplate.js"></script>
<script>
new Vue({
el:"#test",
data(){
return{
content:""
}
},
mounted:function(){
//TODO
},
methods:{
//TODO
}
});
</script>
</body>