道招

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

如果您发现本文排版有问题,可以先点击下面的链接切换至老版进行查看!!!

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

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

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

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

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

<div :class="'skin-mode-' + windowModel" style="display: flex;flex-direction: column;width: 100%;height:100%;">
        <div :id="'toolbar'+unique" style="border-bottom: 1px solid #EBF2FA; font-size: 14px; padding:5px 10px;margin-bottom: 5px;flex: none; ">
        </div>
        <iframe :id="'body'+unique" style="height: auto;word-break: break-all;white-space: normal;flex: auto;display: flex;flex-direction: column;"></iframe>
</div>
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的也需要变更。 于是我搜了下windowdocument,还是有点多了,这样即使做成功了,肯定觉得很low,并且维护性不好。

wangEditor源码改动

改动点一

为什么不直接改全局的windowdocument 先定义同名变量windowdocument分别等于默认的值,如果有iframe的话,直接使用contentWindowcontentDocument替换即可,想想都觉得很好,不是吗。 在最上面加入了一个方法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

我很是纳闷,怎么可能呢,又没用写死循环。自己刚开始想难道是自己替换windowdocument的时机不对,或许个别地方的windowdocument不需要替换成iframe的呢。 决定自己一步一步打断点吧,发现问题出在initSelection

initSelection: function initSelection(newLine) {
        var $textElem = this.$textElem;
        var $children = $textElem.children();
        if (!$children.length) {
            // 如果编辑器区域无内容,添加一个空行,重新设置选区
            $textElem.append($('<p><br></p>'));
            this.initSelection();
            return;
        }

        var $last = $children.last();

        if (newLine) {
            // 新增一个空行
            var html = $last.html().toLowerCase();
            var nodeName = $last.getNodeName();
            if (html !== '<br>' && html !== '<br\/>' || nodeName !== 'P') {
                // 最后一个元素不是 <p><br></p>,添加一个空行,重新设置选区
                $textElem.append($('<p><br></p>'));
                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还是会返回falsefile 就这样最终结果错误的返回了[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的页面建议进行如下改动

<iframe id="editorIframe" style="width: 100%;height: 100%;border:none">
</iframe>
<section id="editorChildren">
    <div :class="'skin-mode-' + windowModel" style="display: flex;flex-direction: column;width: 100%;height:100%;">
        <div :id="'toolbar'+unique" style="border-bottom: 1px solid #EBF2FA; font-size: 14px; padding:5px 10px;margin-bottom: 5px;flex: none; ">
        </div>
        <div :id="'body'+unique" style="height: auto;word-break: break-all;white-space: normal;flex: auto;display: flex;flex-direction: column;"></div>
    </div>
</section>

这样可以了。

更新时间:
上一篇:vue发送请求是应该在mounted还是在created生命周期下一篇:Vue在chrome44偶现点击子元素事件无法冒泡

相关文章

富文本编辑器wangEditor迁移CKEditor前后效果对比

一、背景 富文本编辑器wangEditor的工具栏如图所示 富文本编辑器CKEditor4工具栏如图所示 二、wangEditor编辑器存在问题 1. 字号和字体设置 阅读更多…

wangEditor富文本编辑器改造记录之一——了解wangEditor结构

最近鉴于项目要求,需要对自己使用的富文本编辑器进行改造。 我们首先简单了解下wangEditor的源码结构 第一步:polyfill 当然现在这些基本都用不上了,现代点的浏览器都支持这些 阅读更多…

富文本编辑器wangEditor多语言、工具栏体验改造

支持多语言 首页wangEditro本身是支持使用多语言的,通过源码可以看到里面有个 replaceLang 方法,虽然这种写法有点非主流,但是是有效的,里面部分地方在支持多语言方面有些遗漏。 阅读更多…

wangEditor输入中文后直接粘贴bug来了解compositionstart

昨天有人反馈邮件编辑过程中的一个报障,具体内容就是在编辑器中输入中文然后直接粘贴先前复制好的信息,然后出现了bug,比如之前复制了订单号“1234”,再输入“您的订单号”后直接粘贴,编辑器内显示的结 阅读更多…

2021年的一点工作总结(二)富文本编辑器

邮件项目的核心功能就是编辑邮件了,所以文本的编辑特别容易被用户吐槽了。用户报障的时候一个万能的吐槽点“没有xxx功能,不支持xxx,没有Outlook好用”。 其实作为一个web产品,如果需要更加公 阅读更多…

iframe跨域传输数据

我们一般在A域名的a.html用iframe内嵌B域名的proxy.html,达到将从a.html传输数据值proxy.html并且在proxy.html还可以执行a.html的函数。 a.html 阅读更多…

关注道招网公众帐号
友情链接
消息推送
道招网关注互联网,分享IT资讯,前沿科技、编程技术,是否允许文章更新后推送通知消息。
允许
不用了