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 相关文章推荐
JS记录用户登录次数实现代码
Jan 15 Javascript
BOOTSTRAP时间控件显示在模态框下面的bug修复
Feb 05 Javascript
Underscore.js常用方法总结
Feb 28 Javascript
jQuery实现form表单元素序列化为json对象的方法
Dec 09 Javascript
Bootstrap文件上传组件之bootstrap fileinput
Nov 25 Javascript
如何使用headjs来管理和异步加载js
Nov 29 Javascript
详解用vue.js和laravel实现微信支付
Jun 23 Javascript
vue生成随机验证码的示例代码
Sep 29 Javascript
浅谈VUE监听窗口变化事件的问题
Feb 24 Javascript
基于VuePress 轻量级静态网站生成器的实现方法
Apr 17 Javascript
详解IOS微信上Vue单页面应用JSSDK签名失败解决方案
Nov 14 Javascript
Js跳出两级循环方法代码实例
Sep 22 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
ThinkPHP实现带验证码的文件上传功能实例
2014/11/01 PHP
PHP中iconv函数知识汇总
2015/07/02 PHP
Yii框架自定义数据库操作组件示例
2019/11/11 PHP
sina的lightbox效果。
2007/01/09 Javascript
JS数组去重与取重的示例代码
2014/01/24 Javascript
JavaScript sup方法入门实例(把字符串显示为上标)
2014/10/20 Javascript
JS判断是否为JSON对象及是否存在某字段的方法(推荐)
2016/11/29 Javascript
Angular2环境搭建具体操作步骤(推荐)
2017/08/04 Javascript
微信小程序中悬浮窗功能的实现代码
2019/08/02 Javascript
Vue路由模块化配置的完整步骤
2019/08/14 Javascript
详解解决小程序中webview页面多层history返回问题
2019/08/20 Javascript
layui 上传插件 带预览 非自动上传功能的实例(非常实用)
2019/09/23 Javascript
原生javascript单例模式的应用实例分析
2020/02/23 Javascript
JS对象属性的检测与获取操作实例分析
2020/03/17 Javascript
JS寄快递地址智能解析的实现代码
2020/07/16 Javascript
[01:16:28]DOTA2-DPC中国联赛 正赛 iG vs Magma BO3 第二场 2月23日
2021/03/11 DOTA
web.py获取上传文件名的正确方法
2014/08/26 Python
详解Django框架中的视图级缓存
2015/07/23 Python
深入解析Python的Tornado框架中内置的模板引擎
2016/07/11 Python
Ubuntu16.04/树莓派Python3+opencv配置教程(分享)
2018/04/02 Python
Python实现的knn算法示例
2018/06/14 Python
python函数与方法的区别总结
2019/06/23 Python
python使用百度文字识别功能方法详解
2019/07/23 Python
python实现飞行棋游戏
2020/02/05 Python
pytorch实现保证每次运行使用的随机数都相同
2020/02/20 Python
导致python中import错误的原因是什么
2020/07/01 Python
使paramiko库执行命令时在给定的时间强制退出功能的实现
2021/03/03 Python
美国最大的骑马用品零售商:HorseLoverZ
2017/01/12 全球购物
NARS化妆品官方商店:美国彩妆品牌
2017/08/26 全球购物
同程旅游英文网站:LY.com
2018/11/13 全球购物
求∏的近似值,直到最后一项的绝对值小于指定的数
2016/02/12 面试题
初中生个人学习的自我评价
2013/12/04 职场文书
英文自荐信
2013/12/15 职场文书
中秋节随笔
2015/08/15 职场文书
大学生各类奖学金申请书
2019/06/24 职场文书
Pytest中skip和skipif的具体使用方法
2021/06/30 Python