iframe html 编辑器,在iframe中使用富文本编辑器wangEditor

自己做的邮件项目里面需要使用到富文本编辑器,邮件内容说白了就是HTML代码。前任使用的是wangEditor,部分定制化需求就是直接改的源码。

最近发现有的用户的邮件内容加进去的很多css信息,比如

为了做样式的隔离,所以我们查看邮件内容时使用了iframe,这样邮件内容的样式和我们邮件系统的样式不会发生相互影响。但是前任在新建、编辑邮件时没有使用iframe,当时就很纳闷,为什么没有使用呢?编辑邮件肯定也要进行样式隔离的啊,之前就有时会碰到个别邮件,在点击回复时,整个邮件系统的部分样式都受到了影响,我当时第一时间看了下wangEdtor的官网,没有发现这样的配置,可能需要修改源码,比较麻烦,再加上抽不出时间、出现频率极低,就扔在一边没有管了。最近有时间准备解决下这个问题,再次看了wangEdtor的官网,还是没有发现这样的配置项,估计是没有人有这样的需求,或者作者不知道有这样的需求吧,看来只能自己动手了改了。

由于之前有做翻译的经验,深知多了个iframe会有小麻烦,要处理好window、document,因为这个是分iframe的。

首先想到的就是直接将内容区域的节点从div改成iframe,再改动_initDom,在里面进行一些是否使用iframe的判断,

const textContainerDom = $(textSelector)[0];

const isIframe = Object.prototype.toString.call(textContainerDom) === '[object HTMLIFrameElement]';

if (isIframe) {

$textContainerElem = $(textContainerDom.contentDocument.body);

this.win = textContainerDom.contentWindow;

} else {

$textContainerElem = $(textSelector);

this.win = window;

}

上的this.win就是用来记录window的,如果使用了iframe的话,window自然应该是该iframe对应的contentWindow,相应的document的也需要变更。

于是我搜了下window和document,还是有点多了,这样即使做成功了,肯定觉得很low,并且维护性不好。

wangEditor源码改动

改动点一

为什么不直接改全局的window和document

先定义同名变量window和document分别等于默认的值,如果有iframe的话,直接使用contentWindow和contentDocument替换即可,想想都觉得很好,不是吗。

在最上面加入了一个方法fixContext。

(function (global, factory) {

typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :

typeof define === 'function' && define.amd ? define(factory) :

(global.wangEditor = factory());

}(this, (function () { 'use strict';

let window = global;

let document = window.document;

function fixContext() {

if (global.$old) {

window = global.$old;

document = global.document;

} else {

global.$old = global

}

const iframe = document.querySelector('#editorIframe');

if (iframe) {

window = iframe.contentWindow;

document = iframe.contentDocument;

}

}

...

})));

改动点二

在_initDom的第一时间运行fixContext方法。

_initDom: function _initDom() {

var _this = this;

fixContext();

var toolbarSelector = this.toolbarSelector;

var $toolbarSelector = $(toolbarSelector);

...

}

自己感觉这样对源码的侵入性更小。

但是很快出现了这样的问题:

/> 1.Maximum call stack size exceeded。

2.TypeError: elem.appendChild is not a function

我很是纳闷,怎么可能呢,又没用写死循环。自己刚开始想难道是自己替换window和document的时机不对,或许个别地方的window和document不需要替换成iframe的呢。

决定自己一步一步打断点吧,发现问题出在initSelection

initSelection: function initSelection(newLine) {

var $textElem = this.$textElem;

var $children = $textElem.children();

if (!$children.length) {

// 如果编辑器区域无内容,添加一个空行,重新设置选区

$textElem.append($('

this.initSelection();

return;

}

var $last = $children.last();

if (newLine) {

// 新增一个空行

var html = $last.html().toLowerCase();

var nodeName = $last.getNodeName();

if (html !== '
' && html !== '
' || nodeName !== 'P') {

// 最后一个元素不是

$textElem.append($('

this.initSelection();

return;

}

}

this.selection.createRangeByElem($last, false, true);

this.selection.restoreSelection();

},

上面的!$children.length始终是null,导致了无限调用initSelection方法。

但是其实$children已经有新添加进去的子元素了,但是它的length却是空。经过打断点发现原来是源码的里面的querySelectorAll返回的“有误”导致。

function querySelectorAll(selector) {

var result = document.querySelectorAll(selector);

if (isDOMList(result)) {

return result;

} else {

return [result];

}

}

很显然,这里的document已经被我们替换iframe的contentDocument了,但是怎么还会有问题呢,其实返回的result没有问题,问题就出在isDOMList方法的判断上面。

function isDOMList(selector) {

if (!selector) {

return false;

}

if (selector instanceof HTMLCollection || selector instanceof NodeList) {

return true;

}

return false;

}

仔细排除发现,上面的document.querySelectorAll返回的明明是个NodeList,但是它并不是上面的NodeList的实例,所以上面的isDOMList还是会返回false。

1a1c9571aec81f54d80a265bef5a3e57.png

就这样最终结果错误的返回了[result]而非result,也就是多了一层数组,而它并没有length,所以才引发了上面的死循环问题。

改动点三

可以简单粗暴的改成

if (selector instanceof HTMLCollection || selector instanceof NodeList || Object.prototype.toString.call(selector) === '[object HTMLCollection]' || Object.prototype.toString.call(selector) === '[object NodeList]') {

return true;

}

wangEditor的改动就此三处即可。

使用页面HTML结构变更

使用wangEdtior的页面建议进行如下改动

这样可以了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值