本文将介绍笔者在React的项目中使用百度的富文本编辑器Ueditor的过程。注意本文不提供一条龙式的使用方法,只是将使用过程当中的一些实现思路进行总结,供以参考。react项目中导入ueditor,会存在各类不正交的问题,须要注意。javascript
首先在ueditor官网下载最新安装包,而后在项目入口的html中导入(导入方式不一,能够采用import的方式,须要自行度娘。可是不管哪一种引入方式,只要想自定义功能,不正交问题就难以免QAQ)。无论三七二十一先跑起来再说。。css
<!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要注意html
/** * 封装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修改。具体的将一一介绍,最后实现效果以下:java
autoHeightEnabled: false
initialFrameWidth:this.props.width
initialFrameHeight:this.props.height
复制代码
autoHeightEnabled
能够阻止自动增高,而后再自定义容器宽度和高度。react
解决方法:ueditor.all.js的第6800多行的render
方法,在其中能够自定义全局样式。webpack
解决方法:在每次ueditor实例化以前,先删除对应的idweb
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的状况,如图所示:
解决方法:在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事件注释了。。。
实现思路:将themes/default/css/ueditor.css中加入:
.edui-default .edui-editor-toolbarbox {
position: absolute;
······
top: -36px;
}
复制代码
首先实现头部偏移,而后经过控制toolbar对应dom元素的display来隐藏工具条。实现效果以下:
下面解释一下为何编辑器失焦的时候不恢复预置文案:
从4中的代码能够看出,咱们是经过触发focus和blur事件分别清空和填充编辑器的内容。可是当咱们点击工具条时,编辑器就会触发blur事件!!因而就会出现各类bug。以百度官网的ueditor为例,控制台输入:
为该编辑器注册点击事件,当点击加粗按钮时,控制台输出:
为了不点击工具条时触发blur事件,笔者将自定义的blur事件所有注释了。
首先,在ueditor.config.js中找到toolbars数组,增长一个diyimg字符串,而后在zh-cn.js找到labelMap数组,在末尾加上'diyimg': '插入图片'
。最后,在ueditor.all.js中找到btnCmds数组,加入diyimg字符串。初始化时使用这个字符串,工具条上就会显示一个按钮,可是咱们发现他显示的是这样的:
这是由于ueditor默认使用加粗的icon做为自定义按钮的默认icon,因此为了使用默认的插入图片的图标,咱们须要到themes/default/css/ueditor.css中,在最后一行加入:
/*自定义图片上传按钮 */
.edui-default .edui-toolbar .edui-for-diyimg .edui-icon {
background-position: -380px 0px;//这个位置是“插入图片”的icon,其余图标可自行调整
}
复制代码
添加后,显示效果以下:
图标正常显示后,须要为该图标添加相应的点击事件,在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'
});
复制代码
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,往里面加入内联样式便可。