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 相关文章推荐
JavaScript入门教程(11) js事件处理
Jan 31 Javascript
jquery必须知道的一些常用特效方法及使用示例(整理)
Jun 24 Javascript
JavaScript面向对象程序设计教程
Mar 29 Javascript
WEB前端开发框架Bootstrap3 VS Foundation5
May 16 Javascript
javascript事件模型介绍
May 31 Javascript
深入理解jQuery()方法的构建原理
Dec 05 Javascript
javascript中的深复制详解及实例分析
Dec 29 Javascript
node+vue实现用户注册和头像上传的实例代码
Jul 20 Javascript
详解react内联样式使用webpack将px转rem
Sep 13 Javascript
微信小程序基于canvas渐变实现的彩虹效果示例
May 03 Javascript
Flutter实现仿微信底部菜单栏功能
Sep 18 Javascript
three.js 如何制作魔方
Jul 31 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 SPL标准库之数据结构堆(SplHeap)简单使用实例
2015/05/12 PHP
PHP的new static和new self的区别与使用
2019/11/27 PHP
php实现微信和支付宝支付的示例代码
2020/08/11 PHP
JavaScript与C# Windows应用程序交互方法
2007/06/29 Javascript
出现“不能执行已释放的Script代码”错误的原因及解决办法
2007/08/29 Javascript
Mootools 1.2教程 设置和获取样式表属性
2009/09/15 Javascript
js实现二级菜单渐隐显示
2015/11/03 Javascript
分享Javascript实用方法二
2015/12/13 Javascript
浏览器复制插件zeroclipboard使用指南
2016/03/26 Javascript
javascript基础语法——全面理解变量和标识符
2016/06/02 Javascript
Vue.js双向绑定操作技巧(初级入门)
2016/12/27 Javascript
jQuery实现IE输入框完成placeholder标签功能的方法
2017/09/20 jQuery
JS设计模式之策略模式概念与用法分析
2018/02/05 Javascript
Vue进度条progressbar组件功能
2018/04/17 Javascript
在HTML文档中嵌入JavaScript的四种方法
2018/05/07 Javascript
微信小程序实现之手势锁功能实例代码
2018/07/19 Javascript
详解js常用分割取字符串的方法
2019/05/15 Javascript
JS中的算法与数据结构之链表(Linked-list)实例详解
2019/08/20 Javascript
微信小程序仿今日头条导航栏滚动解析
2019/08/20 Javascript
详解用js代码触发dom事件的实现方案
2020/06/10 Javascript
[35:34]Liquid vs Winstrike 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python查找指定具有相同内容文件的方法
2015/06/28 Python
Python分支语句与循环语句应用实例分析
2019/05/07 Python
如何用Python 实现全连接神经网络(Multi-layer Perceptron)
2020/10/15 Python
Python基于tkinter canvas实现图片裁剪功能
2020/11/05 Python
html5 canvas绘制放射性渐变色效果
2018/01/04 HTML / CSS
学年自我鉴定范文
2013/10/01 职场文书
小学生红领巾广播稿
2014/01/21 职场文书
我爱我的祖国演讲稿
2014/05/04 职场文书
工程项目经理任命书
2014/06/05 职场文书
老公给老婆的检讨书(精华篇)
2014/10/18 职场文书
2015年安全生产责任书
2015/01/30 职场文书
入党介绍人意见2015
2015/06/01 职场文书
助学金申请书该怎么写?
2019/07/16 职场文书
python 利用 PIL 将数组值转成图片的实现
2021/04/12 Python
Python中X[:,0]和X[:,1]的用法
2021/05/10 Python