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 随机展示头像实现代码
Dec 06 Javascript
js 页面元素的几个用法总结
Nov 18 Javascript
showModelDialog弹出文件下载窗口的使用示例
Nov 19 Javascript
js实现图片点击左右轮播
Jul 08 Javascript
浅析如何利用JavaScript进行语音识别
Oct 27 Javascript
使用JavaScriptCore实现OC和JS交互详解
Mar 28 Javascript
使用async-validator编写Form组件的方法
Jan 10 Javascript
JavaScript实现JSON合并操作示例【递归深度合并】
Sep 07 Javascript
JS学习笔记之闭包小案例分析
May 29 Javascript
扫微信小程序码实现网站登陆实现解析
Aug 20 Javascript
小程序两种滚动公告栏的实现方法
Sep 17 Javascript
Node.js操作MongoDB数据库实例分析
Jan 19 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对表单提交特殊字符的过滤和处理方法汇总
2014/02/18 PHP
基于php实现的验证码小程序
2016/12/13 PHP
PHP中的日期时间处理利器实例(Carbon)
2017/06/09 PHP
[原创]PHP获取数组表示的路径方法分析【数组转字符串】
2017/09/01 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
2019/12/20 PHP
jquery中使用ajax获取远程页面信息
2011/11/13 Javascript
JavaScript对象的property属性详解
2014/04/01 Javascript
JQuery each()嵌套使用小结
2014/04/18 Javascript
js/jquery判断浏览器的方法小结
2014/09/02 Javascript
JavaScript获取键盘按键的键码(参照表)
2017/01/10 Javascript
vue综合组件间的通信详解
2017/11/06 Javascript
浅谈AngularJS中$http服务的简单用法
2018/05/15 Javascript
原生js生成图片验证码
2020/10/11 Javascript
使用Vant完成Dialog弹框案例
2020/11/11 Javascript
Python Socket编程入门教程
2014/07/11 Python
Python生成验证码实例
2014/08/21 Python
对numpy 数组和矩阵的乘法的进一步理解
2018/04/04 Python
python发送多人邮件没有展示收件人问题的解决方法
2019/06/21 Python
python输出决策树图形的例子
2019/08/09 Python
Python帮你微信头像任意添加装饰别再@微信官方了
2019/09/25 Python
python多线程实现代码(模拟银行服务操作流程)
2020/01/13 Python
Python爬虫定时计划任务的几种常见方法(推荐)
2021/01/15 Python
英国袜子店:Sock Shop
2017/01/11 全球购物
JD Sports法国:英国篮球和运动时尚的领导者
2017/09/28 全球购物
西安启天科技有限公司网络工程师面试题笔试题
2016/06/12 面试题
大学生毕业自我鉴定范文
2013/11/03 职场文书
办理护照介绍信
2014/01/16 职场文书
《大海那边》教学反思
2014/04/09 职场文书
软件项目开发计划书
2014/05/01 职场文书
电视节目策划方案
2014/05/16 职场文书
安全生产标语
2014/06/06 职场文书
2014年大学生党员评议表自我评价
2014/09/20 职场文书
公司领导九九重阳节发言稿2014
2014/09/25 职场文书
党员批评与自我批评总结
2014/10/15 职场文书
selenium.webdriver中add_argument方法常用参数表
2021/04/08 Python
Java 关于String字符串原理上的问题
2022/04/07 Java/Android