VueJS 集成 Medium Editor的示例代码 (自定义编辑器按钮)


Posted in Javascript onAugust 24, 2017

0x00 前言

VueJS 社区里面关于富文本编辑器的集成也不少了,但是之前小调研了一下,基本上就是 quill,medium-editor,因为之前用 AngularJS 用过 medium-editor,并且需要自定义某些按钮,而且最好还是选中弹出式的,所以就决定用 medium-editor。

社区里面 star 较多的就是这个了:vue-medium-editor,但是打开它官网,看了看文档,越看越别扭,来看看它用法:

<!-- index.html -->
<medium-editor :text='myText' :options='options' custom-tag='h2' v-on:edit='applyTextEdit'>

gosh,传这么多参数,我只想要一个简单的 editor 啊

打开源码一看,就 62 行,所以决定自己动手来一个简单点的

0x01 最简版

最简版,其实就在 vue 组件中实例化一下 medium-editor 就可以了

<template>
 <div class="textEditor" @input="handleInput">
 </div>
</template>
<script>
/* eslint-disable no-new */
import MediumEditor from 'medium-editor'
export default {
 props: {
  value: String,
  options: {
   type: Object,
   default: () => ({})
  }
 },
 data () {
  return {
   editor: null // 用来存放 editor 
  }
 },
 watch: {
  // refer: https://github.com/FranzSkuffka/vue-medium-editor/blob/master/index.js
  value (newVal, oldVal) {
   if (newVal !== this.$el.innerHTML) { // 用 $el.innerHTML 来解决 v-html 的光标跳到行首的问题
    this.$el.innerHTML = newVal || ''
   }
  }
 },
 methods: {
  handleInput (e) {
   this.$emit('input', e.target.innerHTML)
  }
 },
 mounted () {
  // 处理初始值的情况
  this.$el.innerHTML = this.value
  // 这里当然可以自定义 options 啦
  this.editor = new MediumEditor(this.$el, Object.assign({}, this.options))
  // medium-editor 的 api,监听内容改变化
  this.editor.subscribe('editableInput', this.handleInput)
 },
 beforeDestroy () {
  this.editor.unsubscribe('editableInput', this.handleInput)
  this.editor.destroy()
 }
}
</script>

完成~,是不是很简单~~哈哈,最简版是一个 v-html 控制的,但是会有自动跳转到首行的问题,所以这里是最终版,细节问题看注释啦

0x02 用法

咋用呢?很简单,在其他组件中这样:

<text-editor v-model="vm.richText"></text-editor>

当然 你首先得安装 medium-editor的 js 和 css了

0x03 自定义 button

下面是我项目中用到的自定义 button 的相关代码,是一个 buttonBuilder:

import MediumEditor from 'medium-editor'
import rangy from 'rangy/lib/rangy-core.js'
import 'rangy/lib/rangy-classapplier'
import 'rangy/lib/rangy-highlighter'
import 'rangy/lib/rangy-selectionsaverestore'
import 'rangy/lib/rangy-textrange'
import 'rangy/lib/rangy-serializer'
const pHash = {
 p1: { name: 'p1', class: 'fs-36' },
 p2: { name: 'p2', class: 'fs-30' },
 p3: { name: 'p3', class: 'fs-24' },
 p4: { name: 'p4', class: 'fs-18' },
 p5: { name: 'p5', class: 'fs-14' },
 p6: { name: 'p6', class: 'fs-12' }
}
function pButtonCreator (p) {
 return MediumEditor.Extension.extend({
  name: p.name,
  init: function () {
   this.classApplier = rangy.createClassApplier(p.class, {
    elementTagName: 'span',
    normalize: false
   })
   this.button = this.document.createElement('button')
   this.button.classList.add('medium-editor-action')
   this.button.innerHTML = p.name
   this.button.title = p.class
   this.on(this.button, 'click', this.handleClick.bind(this))
  },
  getButton: function () {
   return this.button
  },
  clearFontSize: function () {
   MediumEditor.selection.getSelectedElements(this.document).forEach(function (el) {
    if (el.nodeName.toLowerCase() === 'span' && el.hasAttribute('class')) {
     el.removeAttribute('class')
    }
   })
  },
  handleClick: function (event) {
   this.clearFontSize()
   this.classApplier.toggleSelection()
   // Ensure the editor knows about an html change so watchers are notified
   // ie: <textarea> elements depend on the editableInput event to stay synchronized
   this.base.checkContentChanged()
  }
 })
}
export default {
 P1: pButtonCreator(pHash['p1']),
 P2: pButtonCreator(pHash['p2']),
 P3: pButtonCreator(pHash['p3']),
 P4: pButtonCreator(pHash['p4']),
 P5: pButtonCreator(pHash['p5']),
 P6: pButtonCreator(pHash['p6'])
}

简单来说就是给选中的文字加一些 class (上面是 fs-xx 之类的),其中需要引一个鼠标选中的库 rangy,挺烦人的也是,然后在 text-editor 中这样用:

先实例化

import ButtonBuilder from './buttonBuilder'
var editorOptions = {
 toolbar: {
  buttons: ['bold', 'italic', 'underline', 'removeFormat', 'p3', 'p4', 'p5', 'p6']
 },
 buttonLabels: 'fontawesome', // use font-awesome icons for other buttons
 extensions: {
  p3: new ButtonBuilder.P3(),
  p4: new ButtonBuilder.P4(),
  p5: new ButtonBuilder.P5(),
  p6: new ButtonBuilder.P6()
 },
 placeholder: false
}

再放到 editor 上

this.editor = new MediumEditor(this.$el, Object.assign({}, editorOptions, this.options))

当然上面实例化的步骤不一定要写到这个组件里面,配置 options 也可以从组件外传入

0x04 细节和坑

1、这里用到了 v-model 的自定义实现,详见官方文档:v-model

简单来说呢就是 props: value ,和 this.$emit('input', model) 就可以实现在组件中模拟 v-model 啦

2、多个 editor 使用的自定义button 实例的问题。由于我自己应用的时候有两个挨着的 <text-editor>,用的上面的代码会导致两个 editor 实例用的是同一个 button 实例,这会导致一个很严重的问题:即编辑下面编辑器的内容,可能会修改的上面的编辑器!!

要解决这个也很简单,修改这一行:

this.editor = new MediumEditor(this.$el, Object.assign({}, _.cloneDeep(editorOptions), this.options))

将自定义的 options 深复制一下,这里借助了 lodash 的函数。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
不用AJAX和IFRAME,说说真正意义上的ASP+JS无刷新技术
Sep 25 Javascript
javascript脚本调试方法小结
Nov 24 Javascript
再谈javascript图片预加载技术(详细演示)
Mar 12 Javascript
js实现简单锁屏功能实例
May 27 Javascript
JavaScript检查数字是否为整数或浮点数的方法
Jun 09 Javascript
JavaScript生成SQL查询表单的方法
Aug 13 Javascript
Bootstrap幻灯片轮播图支持触屏左右手势滑动的实现方法
Oct 13 Javascript
JavaScript实现经典排序算法之冒泡排序
Dec 28 Javascript
BootStrap daterangepicker 双日历控件
Jun 02 Javascript
深入理解vue.js中的v-if和v-show
Jun 22 Javascript
解决vue bus.$emit触发第一次$on监听不到问题
Jul 28 Javascript
解决vue自定义指令导致的内存泄漏问题
Aug 04 Javascript
JS实现电商放大镜效果
Aug 24 #Javascript
weui框架实现上传、预览和删除图片功能代码
Aug 24 #Javascript
jQuery Datatable 多个查询条件自定义提交事件(推荐)
Aug 24 #jQuery
详解webpack2+node+react+babel实现热加载(hmr)
Aug 24 #Javascript
Vue网页html转换PDF(最低兼容ie10)的思路详解
Aug 24 #Javascript
JS对象序列化成json数据和json数据转化为JS对象的代码
Aug 23 #Javascript
Angular 2 利用Router事件和Title实现动态页面标题的方法
Aug 23 #Javascript
You might like
打造计数器DIY三步曲(上)
2006/10/09 PHP
php生成SessionID和图片校验码的思路和实现代码
2009/03/10 PHP
连接到txt文本的超链接,不直接打开而是点击后下载的处理方法
2009/07/01 PHP
PHP中的MYSQL常用函数(php下操作数据库必备)
2010/09/12 PHP
PHP 文件系统详解
2012/09/13 PHP
PHP递归返回值时出现的问题解决办法
2013/02/19 PHP
php递归函数中使用return的注意事项
2014/01/17 PHP
PHP中的多行字符串传递给JavaScript的两种方法
2014/06/19 PHP
总结对比php中的多种序列化
2016/08/28 PHP
用PHP将Unicode 转化为UTF-8的实现方法(推荐)
2017/02/08 PHP
详解php 使用Callable Closure强制指定回调类型
2017/10/26 PHP
用jscript实现新建和保存一个word文档
2007/06/15 Javascript
jQuery事件绑定.on()简要概述及应用
2013/02/07 Javascript
JavaScript正则表达式中的ignoreCase属性使用详解
2015/06/16 Javascript
js+flash实现的5图变换效果广告代码(附演示与demo源码下载)
2016/04/01 Javascript
js HTML5多图片上传及预览实例解析(不含前端的文件分割)
2016/08/26 Javascript
jquery基于layui实现二级联动下拉选择(省份城市选择)
2017/06/20 jQuery
angular指令笔记ng-options的使用方法
2017/09/18 Javascript
Node.js dgram模块实现UDP通信示例代码
2017/09/26 Javascript
ES6基础之数组和对象的拓展实例详解
2019/08/22 Javascript
JavaScript中数组去重的5种方法
2020/07/04 Javascript
Openlayers学习之地图比例尺控件
2020/09/28 Javascript
原生js实现下拉框选择组件
2021/01/20 Javascript
特征脸(Eigenface)理论基础之PCA主成分分析法
2018/03/13 Python
Window10+Python3.5安装opencv的教程推荐
2018/04/02 Python
python高级特性和高阶函数及使用详解
2018/10/17 Python
Python 动态导入对象,importlib.import_module()的使用方法
2019/08/28 Python
Django 再谈一谈json序列化
2020/03/16 Python
python中get和post有什么区别
2020/06/19 Python
Agoda西班牙:全球特价酒店预订
2017/06/03 全球购物
英国现代家具和照明购物网站:Heal’s
2019/10/30 全球购物
《美丽的南沙群岛》教学反思
2014/04/27 职场文书
2014年资料员工作总结
2014/11/18 职场文书
颐和园的导游词
2015/01/30 职场文书
2016应届毕业生就业指导课心得体会
2016/01/15 职场文书
Tomcat项目启动失败的原因和解决办法
2022/04/20 Servers