本文將介紹筆者在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,往裏面加入內聯樣式便可。