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 相关文章推荐
JS等比例缩小图片尺寸的实例
Feb 27 Javascript
js获取dom的高度和宽度(可见区域及部分等等)
Jun 13 Javascript
JavaScript中常见获取元素的方法汇总
Mar 04 Javascript
AngualrJS中每次$http请求时的一个遮罩层Directive
Jan 26 Javascript
使用Promise链式调用解决多个异步回调的问题
Jan 15 Javascript
Angular实现模版驱动表单的自定义校验功能(密码确认为例)
May 17 Javascript
Vue filter格式化时间戳时间成标准日期格式的方法
Sep 16 Javascript
Js 利用正则表达式和replace函数获取string中所有被匹配到的文本(推荐)
Oct 28 Javascript
Webpack4 使用Babel处理ES6语法的方法示例
Mar 07 Javascript
JavaScript实现省份城市的三级联动
Feb 11 Javascript
JS跨浏览器解析XML应用过程详解
Oct 16 Javascript
JavaScript声明变量和数据类型的转换
Apr 12 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
部署PHP项目应该注意的几点事项分享
2013/12/20 PHP
php简单检测404页面的方法示例
2019/08/23 PHP
PhpStorm 如何优雅的调试Hyperf的方法步骤
2019/11/24 PHP
PHP基于array_unique实现二维数组去重
2020/07/14 PHP
js的with语句使用方法
2007/09/21 Javascript
事件绑定之小测试  onclick &amp;&amp; addEventListener
2011/07/31 Javascript
javascript实现鼠标拖动改变层大小的方法
2015/04/30 Javascript
浅谈JavaScript字符串与数组
2015/06/03 Javascript
jQuery实现定时读取分析xml文件的方法
2015/07/16 Javascript
bootstarp modal框居中显示的实现代码
2017/02/18 Javascript
浅谈javascript的url参数parse和build函数
2017/03/04 Javascript
一步步教会你微信小程序的登录鉴权
2018/04/09 Javascript
微信小程序上传图片功能(附后端代码)
2020/06/19 Javascript
在element-ui的el-tree组件中用render函数生成el-button的实例代码
2018/11/05 Javascript
jquery实现上传文件进度条
2020/03/26 jQuery
vue2.x数组劫持原理的实现
2020/04/19 Javascript
Js生成随机数/随机字符串的方法小结【5种方法】
2020/05/27 Javascript
[03:09]2014DOTA2国际邀请赛 赛场上的美丽风景线 中国Coser也爱DOTA2
2014/07/20 DOTA
python条件和循环的使用方法
2013/11/01 Python
详解Python中的Descriptor描述符类
2016/06/14 Python
Python使用asyncio包处理并发详解
2017/09/09 Python
Python ADF 单位根检验 如何查看结果的实现
2020/06/03 Python
Python如何实现大型数组运算(使用NumPy)
2020/07/24 Python
MATCHESFASHION.COM美国官网:英国奢侈品零售商
2018/10/29 全球购物
美国领先的眼镜和太阳镜在线零售商:Glasses.com
2019/08/26 全球购物
Java中有几种方法可以实现一个线程?用什么关键字修饰同步方法?stop()和suspend()方法为何不推荐使用?
2015/08/04 面试题
给水工程专业毕业生自荐信
2014/01/28 职场文书
产品质量承诺范本
2014/03/31 职场文书
总经理岗位职责说明书
2014/07/30 职场文书
扶贫办主任查摆“四风”问题个人对照检查材料思想汇报
2014/10/02 职场文书
整改报告格式
2014/11/06 职场文书
大学生自荐信怎么写
2015/03/26 职场文书
幼儿园开学通知
2015/04/24 职场文书
创业计划书之外语培训班
2019/11/02 职场文书
html form表单基础入门案例讲解
2021/07/15 HTML / CSS
CentOS8.4安装Redis6.2.6的详细过程
2021/11/20 Redis