最近在整改個(gè)人博客的過(guò)程中,想順手將以前使用的一個(gè)富文本編輯器wangEditor也升級(jí)一下,卻碰到了一個(gè)非常操蛋的問(wèn)題,在版本迭代到了3.x的時(shí)候(本文中使用的是3.1.1版本),作者將代碼高亮功能取消了,實(shí)在是讓人頭大。

我去他的GitHub上溜達(dá)了一圈,發(fā)現(xiàn)對(duì)于拿掉代碼高亮這個(gè)功能,困惑的人還不少,作者也做了不少答復(fù),總結(jié)下來(lái)有兩個(gè)原因。
一、個(gè)人精力有限

二、受眾群體主要不是程序員

這種涉及到高亮的issue都是在2017年提出來(lái)的,都3年了,作者也還是一直沒(méi)有抽出精力來(lái)優(yōu)化,而在2018年的時(shí)候,作者回復(fù)了一個(gè)關(guān)于代碼高亮的issue,并給出了具體的實(shí)現(xiàn)思路,瞬間讓我感動(dòng)的淚流滿面,大佬終究還是沒(méi)有放棄我們這一小撮程序猿受眾群體。

如上圖,作者給出了4個(gè)步驟,甚至把需要修改的代碼在多少行都指出來(lái)了,話都說(shuō)到這一步了,跟他自己寫(xiě)出來(lái)也么啥兩樣了,今天就來(lái)教大家如何按照作者的這個(gè)思路來(lái)將highlight.js集成進(jìn)來(lái),實(shí)現(xiàn)代碼高亮(作者說(shuō)的比較抽象,我實(shí)現(xiàn)起來(lái),步驟可能稍微有些差異)。
一、修改代碼塊對(duì)應(yīng)的_createPanel方法,增加編程語(yǔ)言下拉框
代碼位置大概在2024行,源代碼如下:
_createPanel: function _createPanel(value) {
var _this = this;
// value - 要編輯的內(nèi)容
value = value || '';
var type = !value ? 'new' : 'edit';
var textId = getRandom('texxt');
var btnId = getRandom('btn');
var panel = new Panel(this, {
width: 500,
// 一個(gè) Panel 包含多個(gè) tab
tabs: [{
// 標(biāo)題
title: '插入代碼',
// 模板
tpl: '<div>\n <textarea id="' + textId + '" style="height:145px;;">' + value + '</textarea>\n <div class="w-e-button-container">\n <button id="' + btnId + '" class="right">\u63D2\u5165</button>\n </div>\n <div>',
// 事件綁定
events: [
// 插入代碼
{
selector: '#' + btnId,
type: 'click',
fn: function fn() {
var $text = $('#' + textId);
var text = $text.val() || $text.html();
text = replaceHtmlSymbol(text);
if (type === 'new') {
// 新插入
_this._insertCode(text);
} else {
// 編輯更新
_this._updateCode(text);
}
// 返回 true,表示該事件執(zhí)行完之后,panel 要關(guān)閉。否則 panel 不會(huì)關(guān)閉
return true;
}
}]
} // first tab end
] // tabs end
}); // new Panel end
// 顯示 panel
panel.show();
// 記錄屬性
this.panel = panel;
}
從tpl參數(shù)可以看到,代碼塊輸入框基本上就是由一個(gè)textarea組成的,那我們想要的一個(gè)編程語(yǔ)言下拉框,那就在這里給它拼一個(gè)select下拉框放進(jìn)去。
_createPanel: function _createPanel(value) {
var _this = this;
// value - 要編輯的內(nèi)容
value = value || '';
var type = !value ? 'new' : 'edit';
var textId = getRandom('texxt');
var btnId = getRandom('btn');
// 編程語(yǔ)言
var select = "<select class='code-type' style='border:1px solid #666;color:#666;margin-top:4px'><option value=''>編程語(yǔ)言</option>";
jQuery.each(hljs.listLanguages(), function(i, e) {
select += "<option value='" + e + "'>" + e + "</option>";
});
select += "</select>";
var panel = new Panel(this, {
width: 500,
// 一個(gè) Panel 包含多個(gè) tab
tabs: [{
// 標(biāo)題
title: '插入代碼',
// 模板
tpl: '<div><textarea id="' + textId + '" style="resize:vertical;min-height:145px">' + value + '</textarea><div class="w-e-button-container">' + select + '<button id="' + btnId + '" class="right">\u63D2\u5165</button></div><div>',
// 事件綁定
events: [
// 插入代碼
{
selector: '#' + btnId,
type: 'click',
fn: function fn() {
var $text = $('#' + textId);
var text = $text.val() || $text.html();
text = replaceHtmlSymbol(text);
if (type === 'new') {
// 新插入
_this._insertCode(text);
} else {
// 編輯更新
_this._updateCode(text);
}
// 返回 true,表示該事件執(zhí)行完之后,panel 要關(guān)閉。否則 panel 不會(huì)關(guān)閉
return true;
}
}]
} // first tab end
] // tabs end
}); // new Panel end
// 顯示 panel
panel.show();
// 記錄屬性
this.panel = panel;
}
二、修改_insertCode方法,增加hljs高亮樣式,并觸發(fā)高亮
代碼位置大概在2075行,源代碼如下:
// 插入代碼
_insertCode: function _insertCode(value) {
var editor = this.editor;
editor.cmd.do('insertHTML', '<pre><code>' + value + '</code></pre><p><br></p>');
}
可以看到insertHtml的時(shí)候,并沒(méi)有帶上highlight.js高亮需要的樣式,也沒(méi)有調(diào)用highlight.js的api觸發(fā)高亮,我們都在這里給它加上去。
// 插入代碼
_insertCode: function _insertCode(value) {
var editor = this.editor;
// 將語(yǔ)言類型加到css中
var codeType = jQuery('select.code-type').val();
editor.cmd.do('insertHTML', '<pre><code class="hljs '+codeType+'">' + value + '</code></pre><p><br></p>');
// 觸發(fā)高亮
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
}
三、修改onClick方法,在代碼回顯時(shí)保證排版不亂
這一步,也不知道大家理不理解,就是我將光標(biāo)點(diǎn)在代碼塊上,再去點(diǎn)擊上方工具欄中的“插入代碼”圖標(biāo)時(shí),會(huì)自動(dòng)將代碼塊的內(nèi)容回填到那個(gè)textarea中去。
代碼位置大概在1992行,源代碼如下:
onClick: function onClick(e) {
var editor = this.editor;
var $startElem = editor.selection.getSelectionStartElem();
var $endElem = editor.selection.getSelectionEndElem();
var isSeleEmpty = editor.selection.isSelectionEmpty();
var selectionText = editor.selection.getSelectionText();
var $code = void 0;
if (!$startElem.equal($endElem)) {
// 跨元素選擇,不做處理
editor.selection.restoreSelection();
return;
}
if (!isSeleEmpty) {
// 選取不是空,用 <code> 包裹即可
$code = $('<code>' + selectionText + '</code>');
editor.cmd.do('insertElem', $code);
editor.selection.createRangeByElem($code, false);
editor.selection.restoreSelection();
return;
}
// 選取是空,且沒(méi)有夸元素選擇,則插入 <pre><code></code></prev>
if (this._active) {
// 選中狀態(tài),將編輯內(nèi)容
this._createPanel($startElem.html());
} else {
// 未選中狀態(tài),將創(chuàng)建內(nèi)容
this._createPanel();
}
}
大家注意2017行代碼,$startElem.html()獲取的內(nèi)容是帶有了html標(biāo)簽的,回顯到填寫(xiě)代碼塊的那個(gè)textarea中肯定不行,可是我改成$startElem.text()之后,發(fā)現(xiàn)html標(biāo)簽是沒(méi)有了,但是樣式排版也沒(méi)了,比如說(shuō)回車換行和空格符之類的,都被過(guò)濾掉了,所以不能用這幾個(gè)API了,我直接獲取了元素的innerText。
onClick: function onClick(e) {
var editor = this.editor;
var $startElem = editor.selection.getSelectionStartElem();
var $endElem = editor.selection.getSelectionEndElem();
var isSeleEmpty = editor.selection.isSelectionEmpty();
var selectionText = editor.selection.getSelectionText();
var $code = void 0;
if (!$startElem.equal($endElem)) {
// 跨元素選擇,不做處理
editor.selection.restoreSelection();
return;
}
if (!isSeleEmpty) {
// 選取不是空,用 <code> 包裹即可
$code = $('<code>' + selectionText + '</code>');
editor.cmd.do('insertElem', $code);
editor.selection.createRangeByElem($code, false);
editor.selection.restoreSelection();
return;
}
// 選取是空,且沒(méi)有夸元素選擇,則插入 <pre><code></code></prev>
if (this._active) {
// 選中狀態(tài),將編輯內(nèi)容
this._createPanel($startElem[0].innerText);
// 編程語(yǔ)言下拉回顯
var className = $startElem.attr('class');
if (className) {
jQuery('select.code-type').val(className.split(' ')[1]);
}
} else {
// 未選中狀態(tài),將創(chuàng)建內(nèi)容
this._createPanel();
}
}
四、修改_updateCode方法,二次編輯確認(rèn)之后,觸發(fā)hljs高亮
代碼位置大概在2081行,源代碼如下:
// 更新代碼
_updateCode: function _updateCode(value) {
var editor = this.editor;
var $selectionELem = editor.selection.getSelectionContainerElem();
if (!$selectionELem) {
return;
}
$selectionELem.html(value);
editor.selection.restoreSelection();
}
可以看到,這里并沒(méi)有觸發(fā)高亮的動(dòng)作,所以在我們修改了代碼塊之后,會(huì)出現(xiàn)hljs高亮丟失的情況,給它加兩行跟_insertCode一樣的代碼。
// 更新代碼
_updateCode: function _updateCode(value) {
var editor = this.editor;
var $selectionELem = editor.selection.getSelectionContainerElem();
if (!$selectionELem) {
return;
}
// 獲取最新的編程語(yǔ)言
var codeType = jQuery('select.code-type').val();
jQuery($selectionELem).prop('class', '');
jQuery($selectionELem).prop('class', 'hljs ' + codeType);
$selectionELem.html(value);
editor.selection.restoreSelection();
// highlight.js
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
}
一套下來(lái),行云流水,直接上效果圖:

我將我改好的一份js放到了網(wǎng)盤,大家有覺(jué)著麻煩的,或者前端基礎(chǔ)薄弱的,可以直接來(lái)我公眾號(hào)(雨落無(wú)影)回復(fù)“wangEditor3”領(lǐng)取哦?。?!