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可拖动弹窗效果实例代码
Nov 09 Javascript
textarea焦点的用法实现获取焦点清空失去焦点提示效果
May 19 Javascript
js带前后翻页的图片切换效果代码分享
Sep 08 Javascript
微信小程序 POST请求(网络请求)详解及实例代码
Nov 16 Javascript
JS实现图片点击后出现模态框效果
May 03 Javascript
JavaScript通过filereader接口读取文件
May 10 Javascript
Vue2实时监听表单变化的示例讲解
Aug 30 Javascript
vue根据值给予不同class的实例
Sep 29 Javascript
详解关于Vuex的action传入多个参数的问题
Feb 22 Javascript
如何为你的JS项目添加智能提示与类型检查详解
Mar 12 Javascript
vue实现路由懒加载及组件懒加载的方式
Jun 11 Javascript
详解JavaScript修改注册表的方法
Jan 05 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世纪万年历
2006/12/06 PHP
php防注入,表单提交值转义的实现详解
2013/06/10 PHP
php解析base64数据生成图片的方法
2016/12/06 PHP
JS 获取span标签中的值的代码 支持ie与firefox
2009/08/24 Javascript
20个非常棒的Jquery实用工具 国外文章
2010/01/01 Javascript
常用一些Javascript判断函数
2012/08/14 Javascript
cument.execCommand()用法深入理解
2012/12/04 Javascript
js渐变显示渐变消失示例代码
2013/08/01 Javascript
JavaScript伸缩的菜单简单示例
2013/12/03 Javascript
javascript:FF/Chrome与IE动态加载元素的区别说明
2014/01/26 Javascript
javascript控制台详解
2015/06/25 Javascript
快速学习jQuery插件 Form表单插件使用方法
2015/12/01 Javascript
在百度搜索结果中去除掉一些网站的资料(通过js控制不让显示)
2017/05/02 Javascript
Easyui ueditor 整合解决不能编辑的问题(推荐)
2017/06/25 Javascript
webpack打包nodejs项目的方法
2018/09/26 NodeJs
jQuery 获取除某指定对象外的其他对象 ( :not() 与.not())
2018/10/10 jQuery
基于小程序请求接口wx.request封装的类axios请求
2020/07/02 Javascript
Angular利用HTTP POST下载流文件的步骤记录
2020/07/26 Javascript
Python字符转换
2008/09/06 Python
python中as用法实例分析
2015/04/30 Python
python实现将英文单词表示的数字转换成阿拉伯数字的方法
2015/07/02 Python
Python3操作SQL Server数据库(实例讲解)
2017/10/21 Python
详解Python3.6安装psutil模块和功能简介
2018/05/30 Python
使用Python为中秋节绘制一块美味的月饼
2019/09/11 Python
Python爬虫爬取煎蛋网图片代码实例
2019/12/16 Python
Python利用FFT进行简单滤波的实现
2020/02/26 Python
python对指定字符串逆序的6种方法(小结)
2020/04/02 Python
享誉全球的多元化时尚精品购物平台:Farfetch发发奇(支持中文)
2017/08/08 全球购物
特色冷饮店创业计划书
2014/01/28 职场文书
2014年五四青年节演讲比赛方案
2014/04/22 职场文书
消防标语大全
2014/06/07 职场文书
个人职业及收入证明
2014/10/13 职场文书
优秀工作者事迹材料
2014/12/26 职场文书
工会经费申请报告
2015/05/15 职场文书
解决Python保存文件名太长OSError: [Errno 36] File name too long
2022/05/11 Python
TypeScript 内置高级类型编程示例
2022/09/23 Javascript