React中使用UEditor百度富文本的方法


Posted in Javascript onAugust 22, 2018

前言

本文将介绍笔者在React的项目中使用百度的富文本编辑器Ueditor的过程。注意本文不提供一条龙式的使用方法,只是将使用过程中的一些实现思路进行总结,供以参考。react项目中导入ueditor,会存在各种不正交的问题,需要注意。

引入

首先在ueditor官网下载最新安装包,然后在项目入口的html中导入(导入方式不一,可以采用import的方式,需要自行度娘。但是无论哪种引入方式,只要想自定义功能,不正交问题就难以避免QAQ)。不管三七二十一先跑起来再说。。

<!DOCTYPE HTML>
<html lang="en-US">

<head>
  <meta charset="UTF-8">
  <title>ueditor demo</title>
</head>

<body>
  <!-- 配置文件 -->
  <script type="text/javascript" src="path/ueditor.config.js"></script>
  <!-- 编辑器源码文件 -->
  <script type="text/javascript" src="path/ueditor.all.js"></script>
 ······
</body>

</html>

在React项目中使用ueditor要注意

  1. 导入的路径,笔者使用的是项目经webpack打包之后的相对路径。
  2. 导入顺序,配置文件要先于源码。
  3. 笔者这种引入方式存在缓存问题,所以修改ueditor.all.js后需要及时清理缓存,测试新的代码。

封装

/**
 * 封装UEditor
 */
import React from 'react';
import './index.less';

class UEditor extends React.Component {
  constructor(props) {
    super(props);
    this.editor = {};
    this.id = '';
  }
 ······
  componentDidMount() {
    let UE = window.UE;
    let id = this.id;
    if (id) {
      try {
        /* 加载之前先执行删除操作,否则如果存在页面切换,
      再切回带编辑器页面重新加载时不刷新无法渲染出编辑器 */
        UE.delEditor(id);
      } catch (e) {}
      let ueditor = UE.getEditor(id, {
        toolbars: [
          ['bold', 'italic', 'underline', 'kityformula', 'diyimg']
        ],
        initialContent: '',
        autoHeightEnabled: false,
        autoFloatEnabled: false,
        elementPathEnabled: false,
        wordCount: false,
        enableAutoSave: false,
        initialFrameWidth: this.props.width,
        initialFrameHeight: this.props.height
      });
    }
  }
  render() {
    this.id = this.props.id;
    return <div styleName="content" id={this.id} />;
  }
}

export default UEditor;

笔者在项目中使用了加粗,斜体,下划线,插入图片,公式等功能,想要自定义配置均可参照ueditor.config.js修改。具体的将一一介绍,最后实现效果如下:

React中使用UEditor百度富文本的方法

问题总结:

1. 禁止自动增高,改用滚动条

autoHeightEnabled: false
initialFrameWidth:this.props.width
initialFrameHeight:this.props.height

autoHeightEnabled可以阻止自动增高,然后再自定义容器宽度和高度。

2. 自定义全局样式,如容器的padding,p标签的line-height等

解决方法:ueditor.all.js的第6800多行的render方法,在其中可以自定义全局样式。

React中使用UEditor百度富文本的方法

3. 导航条切换后,无法再次渲染

解决方法:在每次ueditor实例化之前,先删除对应的id

UE.delEditor(id);

原因分析:

从实例化和卸载实例的源码来看:

getEditor:

UE.getEditor = function (id, opt) {
  var editor = instances[id];
  if (!editor) {
    editor = instances[id] = new UE.ui.Editor(opt);
    editor.render(id);//渲染编辑器
  }
  return editor;
};

delEditor:

UE.delEditor = function (id) {
  var editor;
  if (editor = instances[id]) {
    editor.key && editor.destroy();
    delete instances[id]
  }
};

UE在全局管理了一个实例池,每次实例化都会根据id检索,然后生成实例。从getEditor的源码中可以看出,ueditor的一个实例在第一次初始化时存在一个editor.render(),这是将此id的实例渲染到对应的id容器上。然而,当用户tab切换编辑器再切回来时,此时由于该实例已在实例池中存在,于是直接执行return editor,所以少了editor.render()这一步,于是不能重新渲染。所以,在Ueditor组件每次实例化之前,先进行delEditor卸载。这里需要注意,从delEditor中可以看出ueditor卸载实例时调用了实例的destroy方法。从destroy的注释来看:销毁编辑器实例,使用textarea代替 ,这解释了为什么在切换编辑器或者卸载编辑器时,会出现编辑器变为textarea的情况,如图所示:

React中使用UEditor百度富文本的方法

4. 模拟placeholder实现预置文案

解决方法:在UE的实例中自定义方法,实现填充文字模拟placeholder的效果,代码如下:

//模拟placeholder和控制toolbar显示隐藏
UE.Editor.prototype.initDiy = function (placeholder) {
  var _editor = this;
  //获取焦点
  _editor.addListener("focus", function () {
    UE.isEditored = true;
    var Text = `<p style="color: #CDCDCD">${placeholder}</p>`
    var localHtml = _editor.getContent();
    if (localHtml === Text) {
      _editor.setContent("");//点击时清空
      _editor.focus(true);
    }
    //使得其他工具条display置为none
    var list = document.querySelectorAll('.edui-editor-toolbarbox');
    list.forEach((ele) => {
      ele.style.display = 'none';
    });
    var toolbar = findKey(_editor.key);
    toolbar.style.display = 'block';
  });
  // 插入图片时存在问题
  // _editor.addListener("blur", function () {
  //   var localHtml = _editor.getContent();
  //   if (localHtml === '') {
  //     _editor.setContent(`<p style="color: #CDCDCD">${placeholder}</p>`);
  //   }
  //   // window.activeEditor = _editor.key;
  // });
  _editor.ready(function () {
    // _editor.fireEvent("blur");
    _editor.setContent(`<p style="color: #CDCDCD">${placeholder}</p>`);//填充预置文案
  }); 
}
//寻找工具条
function findKey(key) {
  let ele = document.querySelector(`#${key}`);
  let toolbar = ele.querySelector('.edui-editor-toolbarbox');
  return toolbar;
}

原来,笔者实现的效果是点击时清空,失焦时还原。但是,在做自定义工具条时产生了bug(在5中我会细说),因此我采用了另一种方案:初始时设置预设文案,当用户聚焦时清空预设,用户失焦后不再恢复该预设文案。也就是将blur事件注释了。。。

5. 工具条显示在编辑器头部,显示为悬浮效果,默认隐藏,聚焦时出现

实现思路:将themes/default/css/ueditor.css中加入:

.edui-default .edui-editor-toolbarbox {
  position: absolute;
  ······
  top: -36px;
}

首先实现头部偏移,然后通过控制toolbar对应dom元素的display来隐藏工具条。实现效果如下:

React中使用UEditor百度富文本的方法

下面解释一下为什么编辑器失焦的时候不恢复预置文案:
从4中的代码可以看出,我们是通过触发focus和blur事件分别清空和填充编辑器的内容。但是当我们点击工具条时,编辑器就会触发blur事件!!于是就会出现各种bug。以百度官网的ueditor为例,控制台输入:

React中使用UEditor百度富文本的方法

为该编辑器注册点击事件,当点击加粗按钮时,控制台输出:

React中使用UEditor百度富文本的方法

为了避免点击工具条时触发blur事件,笔者将自定义的blur事件全部注释了。

6. 自定义按钮和七牛云图片上传

首先,在ueditor.config.js中找到toolbars数组,增加一个diyimg字符串,然后在zh-cn.js找到labelMap数组,在末尾加上'diyimg': '插入图片' 。最后,在ueditor.all.js中找到btnCmds数组,加入diyimg字符串。初始化时使用这个字符串,工具条上就会显示一个按钮,但是我们发现他显示的是这样的:

React中使用UEditor百度富文本的方法

这是因为ueditor默认使用加粗的icon作为自定义按钮的默认icon,所以为了使用默认的插入图片的图标,我们需要到themes/default/css/ueditor.css中,在最后一行加入:

/*自定义图片上传按钮 */
.edui-default .edui-toolbar .edui-for-diyimg .edui-icon {
  background-position: -380px 0px;//这个位置是“插入图片”的icon,其他图标可自行调整
}

添加后,显示效果如下:

React中使用UEditor百度富文本的方法

图标正常显示后,需要为该图标添加相应的点击事件,在ueditor.all.js中加入:

//图片上传
UE.commands['diyimg'] = {
  execCommand : function(){
    const upload = async(e) => {
      ······//完成图片上传的代码
    }
    const fileInput = document.getElementById('diyimg');//获取dom上隐藏的一个input标签
    fileInput.onchange = upload;
    fileInput.click();//触发input标签实现文件上传
    return true; 
  },
  queryCommandState:function(){
  
  }
};

笔者这里不赘述图片上传的代码,度娘上很多,我简单说说实现的思路:

先实现一个插入图片的按钮,然后为该按钮注册相应的事件diyimg,然后在页面中添加一个input file标签并隐藏,diyimg事件会触发该标签的点击事件,弹出文件上传弹窗,此时选择文件点击后会触发onchange事件,执行相应的图片上传代码。上传成功到服务器后,服务器会返回图片对应的url,此时拿到该url填入对应编辑器实例,执行编辑器的插入图片的代码:

this.execCommand('insertimage', {
  src: res.data.downloadUrl,//回调传来的url
  width:'60'
  // height:'45'
});

7. 给在编辑器内部的img等标签添加内联样式

ueditor默认存在xss过滤!!!这里以给img标签添加style=“vertical-top”为例。

首先要找到ueditor.config.js,在其中搜索xss,在第403行左右有代码:

img:  [src', 'alt', 'title', 'width', 'height', 'id', '_src', 'loadingclass', 'class', 'data-latex'],

往数组里加入style字符串,然后在ueditor.all.js中搜索UE.commands['insertimage'] ,在第约11172行找到str,往里面加入内联样式即可。

一些吐槽:

1. 在react项目里使用script形式引入,感觉格格不入

2. 为了实现placeholder,各个事件之间存在不正交的现象。诸如点击按钮,却触发了编辑器的失焦事件

3. 在使用自定义的字数限制功能时,笔者使用ueditor的contentChange去检测内容字数,但是contentChange事件是定时的,所以计算字数会有问题。

实现中的问题及解决方法

1.上传图片时的跨域问题

在源码里 有 header['X_Requested_With'] = 'XMLHttpRequest';

后端需要配置 header('Access-Control-Allow-Headers', 'AccessToken, Content-Type, WebOrigin,X-Requested-With,X_Requested_With');

2. 进入文本编辑界面编辑器没有加载出来

可能原因: 放置编辑器的容器id, 容器下的编辑器已经存在

解决方法如代码所示

this.editor.ready(function (ueditor) {
if (!ueditor) {
// 如果初始化后ueditor不存在删除后重新调用
UE.delEditor(self.props.id);
self.initEditor();
}
})

3.注意这两个参数配置

'UEDITOR_HOME_URL': '/react/dist/ueditor/', // 编辑器实例路径,即ueditor文件包所放置的位置

serverUrl: window.api_host + '/innerMessage/uploadImage', // 后端提供加载图片接口,这是个共同接口接口包括了后端配置的config.json文件 通过url中action

值不同来区分(config|uploaimage 等)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
div层的移动及性能优化
Nov 16 Javascript
使用原生js实现页面蒙灰(mask)效果示例代码
Jun 20 Javascript
Js实现手机发送验证码时按钮延迟操作
Jun 20 Javascript
JavaScript获取图片真实大小代码实例
Sep 24 Javascript
使用jQuery5分钟快速搞定双色表格的简单实例
Aug 08 Javascript
JS基于递归算法实现1,2,3,4,5,6,7,8,9倒序放入数组中的方法
Jan 03 Javascript
angularJs的ng-class切换class
Jun 23 Javascript
详解Vue组件实现tips的总结
Nov 01 Javascript
jQuery NProgress.js加载进度插件的简单使用方法
Jan 31 jQuery
详解Node 定时器
Feb 26 Javascript
JS为什么说async/await是generator的语法糖详解
Jul 11 Javascript
vue 自定指令生成uuid滚动监听达到tab表格吸顶效果的代码
Sep 16 Javascript
VUE 全局变量的几种实现方式
Aug 22 #Javascript
详解使用VUE搭建后台管理系统(vue-cli更新至3.0)
Aug 22 #Javascript
基于vue.js中关于下拉框的值默认及绑定问题
Aug 22 #Javascript
微信小程序有旋转动画效果的音乐组件实例代码
Aug 22 #Javascript
vue2.0 下拉框默认标题设置方法
Aug 22 #Javascript
VUE中v-on:click事件中获取当前dom元素的代码
Aug 22 #Javascript
jquery获取select选中值的文本,并赋值给另一个输入框的方法
Aug 21 #jQuery
You might like
修改Zend引擎实现PHP源码加密的原理及实践
2008/04/14 PHP
CodeIgniter php mvc框架 中国网站
2008/05/26 PHP
phpExcel中文帮助手册之常用功能指南
2014/08/18 PHP
php使用get_class_methods()函数获取分类的方法
2016/07/20 PHP
php通过会话控制实现身份验证实例
2016/10/18 PHP
phpQuery采集网页实现代码实例
2020/04/02 PHP
JavaScript 学习笔记(四)
2009/12/31 Javascript
关于图片按比例自适应缩放的js代码
2011/10/30 Javascript
JS打开新窗口的2种方式
2013/04/18 Javascript
jquery 快速回到页首的方法
2013/12/05 Javascript
浏览器兼容性问题大汇总
2015/12/17 Javascript
JavaScript操作表单实例讲解(上)
2016/06/20 Javascript
nodejs服务搭建教程 nodejs访问本地站点文件
2017/04/07 NodeJs
jQuery模拟实现天猫购物车动画效果实例代码
2017/05/25 jQuery
JS+H5 Canvas实现时钟效果
2018/07/20 Javascript
react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面
2019/11/12 Javascript
vue.js实现简单的计算器功能
2020/02/22 Javascript
JavaScript 如何在浏览器中使用摄像头
2020/12/02 Javascript
[00:48]完美“圣”典2016风云人物:xiao8宣传片
2016/11/30 DOTA
[00:59]PWL开团时刻DAY7——我在赶
2020/11/06 DOTA
[01:15:16]DOTA2-DPC中国联赛 正赛 Elephant vs Aster BO3 第一场 1月26日
2021/03/11 DOTA
Python3实现发送QQ邮件功能(附件)
2020/12/23 Python
python mqtt 客户端的实现代码实例
2019/09/25 Python
python如何通过pyqt5实现进度条
2020/01/20 Python
pytorch梯度剪裁方式
2020/02/04 Python
韩国三大免税店之一:THE GRAND 中文免税店
2016/07/21 全球购物
卡骆驰德国官方网站:Crocs德国
2019/03/29 全球购物
荷兰网上药店:Drogisterij.net
2019/09/03 全球购物
小学数学国培感言
2014/03/10 职场文书
计划生育证明格式及范本
2014/10/09 职场文书
后进生评语大全
2015/01/04 职场文书
廉政承诺书
2015/01/19 职场文书
2015年乡镇财政工作总结
2015/05/19 职场文书
2015大学生暑期实习报告
2015/07/13 职场文书
2016年万圣节家长开放日活动总结
2016/04/05 职场文书
浅谈Python中对象是如何被调用的
2022/04/06 Python