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 相关文章推荐
jquery 新浪网易的评论块制作
Jul 01 Javascript
使用简洁的jQuery方法实现隔行换色功能
Jan 02 Javascript
JS网页图片按比例自适应缩放实现方法
Jan 15 Javascript
node.js入门教程迷你书、node.js入门web应用开发完全示例
Apr 06 Javascript
动态加载iframe时get请求传递中文参数乱码解决方法
May 07 Javascript
WdatePicker.js时间日期插件的使用方法
Jul 26 Javascript
vue 组件中slot插口的具体用法
Apr 03 Javascript
从0到1构建vueSSR项目之node以及vue-cli3的配置
Mar 07 Javascript
Vue+Express实现登录注销功能的实例代码
May 05 Javascript
JavaScript实现世界各地时间显示
Sep 07 Javascript
vue使用keep-alive实现组件切换时保存原组件数据方法
Oct 30 Javascript
nuxt 路由、过渡特效、中间件的实现代码
Nov 06 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/10/09 PHP
smarty模板引擎中内建函数if、elseif和else的使用方法
2015/01/22 PHP
JS 图片缩放效果代码
2010/06/09 Javascript
学习并汇集javascript匿名函数
2010/11/25 Javascript
js 数据类型转换总结笔记
2011/01/17 Javascript
javascript打印大全(打印页面设置/打印预览代码)
2013/03/29 Javascript
js算法中的排序、数组去重详细概述
2013/10/14 Javascript
Java/JS获取flash高宽的具体方法
2013/12/27 Javascript
使用JavaScript进行进制转换将字符串转换为十进制
2014/09/21 Javascript
webpack学习--webpack经典7分钟入门教程
2017/06/28 Javascript
JS简单实现数组去重的方法分析
2017/10/14 Javascript
Vue2.5 结合 Element UI 之 Table 和 Pagination 组件实现分页功能
2018/01/26 Javascript
webpack配置导致字体图标无法显示的解决方法
2018/03/06 Javascript
基于layui实现高级搜索(筛选)功能
2019/07/26 Javascript
基于Vue 撸一个指令实现拖拽功能
2019/10/09 Javascript
原生javascript制作的拼图游戏实现方法详解
2020/02/23 Javascript
Python中pygame的mouse鼠标事件用法实例
2015/11/11 Python
Django查询数据库的性能优化示例代码
2017/09/24 Python
Python在不同目录下导入模块的实现方法
2017/10/27 Python
Python + selenium + requests实现12306全自动抢票及验证码破解加自动点击功能
2018/11/23 Python
利用Python对文件夹下图片数据进行批量改名的代码实例
2019/02/21 Python
python时间序列按频率生成日期的方法
2019/05/14 Python
Python threading模块condition原理及运行流程详解
2020/10/05 Python
python定时截屏实现
2020/11/02 Python
python Protobuf定义消息类型知识点讲解
2021/03/02 Python
HTML5单选框、复选框、下拉菜单、文本域的实现代码
2020/12/01 HTML / CSS
Roots加拿大官网:加拿大休闲服饰品牌
2016/10/24 全球购物
佛罗里达州印第安河新鲜水果:Hale Groves
2017/02/20 全球购物
施华洛世奇日本官网:SWAROVSKI日本
2018/05/04 全球购物
Pedro官网:新加坡时尚品牌
2019/08/27 全球购物
serialVersionUID具有什么样的特征
2014/02/20 面试题
励志演讲稿范文
2014/04/29 职场文书
土木工程求职信
2014/05/29 职场文书
维护民族团结演讲稿
2014/08/27 职场文书
单位工作证明书格式
2014/10/04 职场文书
vue中的可拖拽宽度div的实现示例
2022/04/08 Vue.js