CKEditor 实例已经存在

Posted

技术标签:

【中文标题】CKEditor 实例已经存在【英文标题】:CKEditor instance already exists 【发布时间】:2010-12-20 03:15:25 【问题描述】:

我正在使用 jquery 对话框来呈现表单(通过 AJAX 获取)。在某些表单上,我将 CKEditor 用于 textareas。编辑器在第一次加载时显示良好。

当用户取消对话框时,我将删除内容,以便在以后的请求中重新加载它们。问题是,一旦重新加载对话框,CKEditor 就会声称该编辑器已经存在。

uncaught exception: [CKEDITOR.editor] The instance "textarea_name" already exists.

API 包含一种销毁现有编辑器的方法,我看到有人声称这是一种解决方案:

if (CKEDITOR.instances['textarea_name']) 
CKEDITOR.instances['textarea_name'].destroy();

CKEDITOR.replace('textarea_name');

这对我不起作用,因为我收到了一个新错误:

TypeError: Result of expression 'i.contentWindow' [null] is not an object.

这个错误似乎发生在“destroy()”而不是“replace()”上。有没有人遇到过这种情况并找到了不同的解决方案?

是否可以“重新渲染”现有编辑器,而不是销毁和替换它?

更新 Here is another question处理同样的问题,但他提供了downloadable test case。

【问题讨论】:

【参考方案1】:

如果您知道编辑器的名称,那么执行此操作非常简单..

例如,如果编辑器的名称是editor1 (this is the id of div or textarea),那么您只需像这样检查

if(CKEDITOR.instances['editor1'])
   // Instance present
else
   // Instance not present 
 

如果您想获取已初始化编辑器的数量,请执行以下操作

for(let inst of CKEDITOR.instances)
   // inst is an Obj of editor instance
   // inst.name will give the name of the editor instance

【讨论】:

【参考方案2】:

我也遇到过这个问题,不过我用更简单的方法解决了...

我在我的 jQuery 脚本中使用类“ckeditor”作为我希望用于 CKEditor 的文本区域的选择器。默认的 ckeditor JS 脚本也使用这个类来识别用于 CKEditor 的文本区域。

这意味着我的 jQuery 脚本和默认的 ckeditor 脚本之间存在冲突。

我只是将 textarea 的类和我的 jQuery 脚本更改为“do_ckeditor”(您可以使用除“ckeditor”之外的任何内容)并且它起作用了。

【讨论】:

谢谢,是的,“自动”ckeditorizing 也困扰着我。另见***.com/a/3631391/923817【参考方案3】:

试试这个:

for (name in CKEDITOR.instances)

    CKEDITOR.instances[name].destroy(true);

【讨论】:

【参考方案4】:

CKeditor 4.2.1

这里有很多答案,但对我来说,我需要更多的东西(也有点脏,所以如果有人可以改进,请做)。对我来说,我的问题是 MODAL。

我正在使用 Foundation 以模式渲染 CKEditor。理想情况下,我会在关闭时毁掉编辑器,但我不想惹基金会。

我调用了 delete,我尝试了 remove 和另一种方法,但这就是我最终解决的方法。

我使用 textarea 填充而不是 DIV。

我的解决方案

//hard code the DIV removal (due to duplication of CKeditors on page however they didn't work)

    $("#cke_myckeditorname").remove();


     if (CKEDITOR.instances['myckeditorname']) 
                delete CKEDITOR.instances['myckeditorname'];
                CKEDITOR.replace('myckeditorname', GetCKEditorSettings());
             else 
                CKEDITOR.replace('myckeditorname', GetCKEditorSettings());
            

这是我返回特定格式的方法,您可能不想要。

    function GetCKEditorSettings()
    
       return 
                    linkShowAdvancedTab: false,
                    linkShowTargetTab: false,
                    removePlugins: 'elementspath,magicline',
                    extraAllowedContent: 'hr blockquote div',
                    fontSize_sizes: 'small/8px;normal/12px;large/16px;larger/24px;huge/36px;',
                    toolbar: [
                        ['FontSize'],
                        ['Bold', 'Italic', 'Underline', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink'],
                        ['Smiley']
                    ]
                ;
    

【讨论】:

【参考方案5】:

此功能在 CKEditor 版本 4.4.5 中适用于我,它没有任何内存泄漏

 function CKEditor_Render(CkEditor_id) 
        var instance = CKEDITOR.instances[CkEditor_id];
        if (CKEDITOR.instances.instance) 
            CKEDITOR.instances.instance.destroy();
        
        CKEDITOR.replace(CkEditor_id);
    

// 如下调用该函数

var id = 'ckeditor'; // Id of your textarea

CKEditor_Render(id);

【讨论】:

【参考方案6】:

你不需要销毁对象CKeditor,你需要remove():

改变这个:

CKEDITOR.instances['textarea_name'].destroy();

为此:

CKEDITOR.remove(CKEDITOR.instances['textarea_name']);

【讨论】:

【参考方案7】:

去掉class="ckeditor",它可能触发了ckeditor初始化

【讨论】:

【参考方案8】:

这对我有用:

for(name in CKEDITOR.instances)

  CKEDITOR.instances[name].destroy()

【讨论】:

确保你的“取消”代码调用了类似的东西!我的问题是我的取消没有调用销毁:)【参考方案9】:

您可以通过 ckeditor 的 remove 方法删除任何 ckeditor 实例。实例将是文本区域的 id 或名称。

if (CKEDITOR.instances[instance_name]) 
    CKEDITOR.remove(CKEDITOR.instances[instance_name]);

【讨论】:

【参考方案10】:

它非常简单。就我而言,我运行了下面的 jquery 方法,该方法将在页面加载期间破坏 ckeditor 实例。这成功了并解决了问题 -

JQuery 方法-

function resetCkEditorsOnLoad()

    for(var i in CKEDITOR.instances) 
        editor = CKEDITOR.instances[i];
            editor.destroy();
            editor = null;
    


$(function() 

            $(".form-button").button();
    $(".button").button();

    resetCkEditorsOnLoad(); // CALLING THE METHOD DURING THE PAGE LOAD

            .... blah.. blah.. blah.... // REST OF YOUR BUSINESS LOGIC GOES HERE

       );

就是这样。希望对你有帮助。

干杯, 西里什。

【讨论】:

【参考方案11】:

这是 jquery .load() api 和 ckeditor 的完整工作代码,在我的例子中,我将带有 ckeditor 的页面加载到具有一些 jquery 效果的 div 中。我希望它会帮助你。

 $(function() 
            runEffect = function(fileload,lessonid,act) 
            var selectedEffect = 'drop';
            var options = ;
            $( "#effect" ).effect( selectedEffect, options, 200, callback(fileload,lessonid,act) );
        ;
        function callback(fileload,lessonid,act) 
            setTimeout(function() //load the page in effect div
                $( "#effect" ).load(fileload,lessonid:lessonid,act:act);
                 $("#effect").show( "drop", 
                          direction: "right", 200 );
                $("#effect").ajaxComplete(function(event, XMLHttpRequest, ajaxOptions) 
                    loadCKeditor(); //call the function after loading page
                );
            , 100 );   
        ;

        function loadCKeditor()
        //you need to destroy  the instance if already exist
            if (CKEDITOR.instances['introduction']) 
            
                CKEDITOR.instances['introduction'].destroy();
            
            CKEDITOR.replace('introduction').getSelection().getSelectedText();
        
    );

===================== button for call the function ================================

<input type="button" name="button" id="button" onclick="runEffect('lesson.php','','add')" >

【讨论】:

【参考方案12】:

我对实例有同样的问题,我到处寻找,最后这个实现对我有用:



    //set my instance id on a variable

    myinstance = CKEDITOR.instances['info'];

    //check if my instance already exist

    if (myinstance)  
        CKEDITOR.remove(info)
    

    //call ckeditor again

    $('#info').ckeditor(
        toolbar: 'Basic',
        entities: false,
        basicEntities: false
    );

【讨论】:

【参考方案13】:

对于那些使用 jquery“适配器”并且遇到麻烦的人(就像我一样),因为超级 hackish 但有效的解决方案是做这样的事情:

// content editor plugin
(function($)
    $.fn.contentEditor = function( params ) 
        var xParams = $.extend(, $.fn.contentEditor.defaultParams, params);
        return this.each( function()    
            var $editor = $(this);
            var $params = $.extend(, xParams, $editor.data());

            // if identifier is set, detect type based on identifier in $editor
            if( $params.identifier.type ) 
                $params.type = $editor.find($params.identifier.type).val();
            

            $editor.data('type', $params.type);

            // edit functionality
            editButton = $('<button>Edit Content</button>').on('click',function()
                // content container
                var $cc = $('#' + $editor.data('type'));

                // editor window
                var $ew = $('<form class="editorWindow" />');
                $ew.appendTo('body');

                // editor content
                $ec = $('<textarea name="editorContent" />').val($cc.html());
                $ec.appendTo($ew);
                $ec.ckeditor();


                //$ec.ckeditorGet().setMode('source');


                $ew.dialog(
                    "autoOpen": true,
                    "modal": true,
                    "draggable": false,
                    "resizable": false,
                    "width": 850,
                    "height": 'auto',
                    "title": "Content Editor",
                    "buttons":  
                        'Save': function()                             
                            $cc.html( $ec.val() );
                            $ec.ckeditorGet().destroy(); 
                            $ew.remove();
                        ,
                        'Cancel / Close': function()  

                            $ec.ckeditorGet().destroy();                        
                            $ew.remove();
                        
                    ,
                    'close': function() 
                        $ec.ckeditorGet().destroy(); 
                    ,
                    'open': function() 
                        $ew.find('a.cke_button_source').click();

                        setTimeout(function()
                            $ew.find('a.cke_button_source.cke_on').click();
                        , 500);
                    
                );

                return false;
            );

            editButton.appendTo( $editor );

        );
    

    // set default option values
    $.fn.contentEditor.defaultParams = 
        'identifier': 
            'type': 'input[name="type"]'
        
    ;   

)(jQuery);   

$(function()

    $('form.contentEditor').contentEditor();

);

重点是这部分:

                'open': function() 
                    $ew.find('a.cke_button_source').click();

                    setTimeout(function()
                        $ew.find('a.cke_button_source.cke_on').click();
                    , 500);
                

这解决了下次打开对话框时编辑器文本不可见的问题。我意识到这是非常骇人听闻的,但考虑到其中大多数将用于管理工具,我认为这不像通常那样是一个大问题......而且这很有效,所以希望它可以为某人节省一些时间;)

【讨论】:

【参考方案14】:

我已经根据以上所有代码准备了自己的解决方案。

      $("textarea.ckeditor")
      .each(function () 

                var editorId = $(this).attr("id");

                try     
                    var instance = CKEDITOR.instances[editorId];
                    if (instance)  instance.destroy(true); 

                
                catch(e) 
                finally 
                    CKEDITOR.replace(editorId);
                
            );

它非常适合我。

有时在 AJAX 请求后出现错误的 DOM 结构。 例如:

<div id="result">
   <div id="result>
   //CONTENT
   </div>
</div>

这也会导致问题,并且 ckEditor 将无法工作。所以请确保你有正确的 DOM 结构。

【讨论】:

【参考方案15】:

为此,您需要在销毁实例时传递布尔参数 true

    var editor = CKEDITOR.instances[name];
    if (editor)  editor.destroy(true); 
    CKEDITOR.replace(name);

【讨论】:

“真实”选项让一切变得不同。还值得一提的是,下面给出的“CKEDITOR.remove(instance)”答案不是一个好的解决方案,因为它是一个内部 API,也会产生错误,最好使用 instance.destroy(true) 谢谢你,这对我很有帮助。 加入感谢合唱团!我喜欢 Stack Overflow 总是通过一次搜索就能解决我所有的问题。 这个答案太有用了,我很惊讶它被埋在这里这么深。 应该有一个选项可以按他们收到的票数对答案进行排序。所以最好的答案会排在首位。【参考方案16】:
var e= CKEDITOR.instances['sample'];
e.destroy();
e= null;

【讨论】:

【参考方案17】:

也许这会对你有所帮助——我使用 jquery 做了类似的事情,除了我正在加载未知数量的 ckeditor 对象。我花了一段时间才偶然发现这一点 - 文档中并不清楚。

function loadEditors() 
    var $editors = $("textarea.ckeditor");
    if ($editors.length) 
        $editors.each(function() 
            var editorID = $(this).attr("id");
            var instance = CKEDITOR.instances[editorID];
            if (instance)  instance.destroy(true); 
            CKEDITOR.replace(editorID);
        );
    

这是我从编辑那里获取内容的方法:

    var $editors = $("textarea.ckeditor");
    if ($editors.length) 
        $editors.each(function() 
            var instance = CKEDITOR.instances[$(this).attr("id")];
            if (instance)  $(this).val(instance.getData()); 
        );
    

更新:我已更改答案以使用正确的方法 - 即 .destroy()。 .remove() 是内部的,并且在某一时刻被错误地记录。

【讨论】:

这似乎已经消除了错误,但在第二次渲染时,textarea 现在包含“null”,我无法再与工具栏或 textarea 交互。如果我确实单击工具栏按钮,我会收到如下错误:“TypeError:表达式'this.$.focus'[undefined] 的结果不是函数”-或-“TypeError:表达式'this.document.getWindow()的结果” .$' [undefined] 不是对象”。您在哪里/何时调用该函数?我的脚本与 jquery 加载的内容内联。 (父 html 头部的ckeditor.js) 在所有文本区域都添加到页面并填充内容后,我调用它。【参考方案18】:

我处于必须控制生成对话框的情况,每个对话框都需要在这些对话框中嵌入一个 ckeditor。碰巧文本区域共享相同的ID。 (通常这是非常糟糕的做法,但我有 2 个 jqGrid,一个已分配项,另一个未分配项。)它们共享几乎相同的配置。因此,我使用通用代码来配置两者。

所以,当我从 jqGrid 加载一个对话框时,添加行或编辑它们;我必须删除所有文本区域中的所有 CKEDITOR 实例。

$('textarea').each(function()

    try 
    
        if(CKEDITOR.instances[$(this)[0].id] != null)
        
            CKEDITOR.instances[$(this)[0].id].destroy();
        
    
    catch(e)
    

    
);

这将遍历所有文本区域,如果有 CKEDITOR 实例,则销毁它。

或者,如果你使用纯 jQuery:

$('textarea').each(function()

    try 
    
        $(this).ckeditorGet().destroy();
    
    catch(e)
    

    
);

【讨论】:

【参考方案19】:

我遇到了同样的事情,问题是 wordcount 插件的初始化时间太长。 30 多秒。用户将单击显示 ckeditor 的视图,然后取消,从而将新页面 ajax 加载到 dom 中。该插件抱怨,因为 iframe 或任何 contentWindow 指向的内容在它准备好将自己添加到 contentWindow 时不再可见。您可以通过单击视图然后等待字数出现在编辑器的右下角来验证这一点。如果你现在取消,你不会有问题。如果你不等待它,你会得到 i.contentWindow is null 错误。要修复它,只需废弃插件即可:

if (CKEDITOR.instances['textarea_name']) 

   CKEDITOR.instances['textarea_name'].destroy();

CKEDITOR.replace('textarea_name',  removePlugins: "wordcount"  );

如果您需要字数计数器,请在编辑器上注册粘贴和键位事件,并使用计算字数的功能。

【讨论】:

【参考方案20】:

我遇到了与jackboberg 完全相同的问题。我正在使用动态表单加载到 jquery 对话框中,然后附加各种小部件(日期选择器、ckeditors 等......)。 我尝试了上面提到的所有解决方案,但没有一个对我有用。

由于某种原因,ckeditor 仅在我第一次加载表单时附加,第二次我收到了与 jackboberg 完全相同的错误消息。

我分析了我的代码,发现如果您在“半空中”附加 ckeditor,即表单内容仍未放入对话框中,ckeditor 将无法正确附加其绑定。那是因为 ckeditor 附加在“半空中”,第二次将其附加在“半空中”......噗......由于第一个实例未从 DOM 中正确删除,因此引发错误。

这是我导致错误的代码:

var $content = $(r.content); // jQuery can create DOM nodes from html text gotten from <xhr response> - so called "mid-air" DOM creation
$('.rte-field',$content).ckeditor(function());
$content.dialog();

这是有效的修复:

var $content = $(r.content).dialog(); // first create dialog
$('.rte-field',$content).ckeditor(function()); // then attach ckeditor widget

【讨论】:

【参考方案21】:

i.contentWindow is null 错误似乎发生在对绑定到不再在 DOM 中的文本区域的编辑器实例上调用销毁时。

CKEDITORY.destroy 接受参数noUpdate

APIdoc 指出:

如果实例正在替换 DOM 元素,该参数指示是否使用实例内容更新元素。

因此,为避免该错误,请在从 DOM 中删除 textarea 元素之前调用 destroy,或调用 destory(true) 以避免尝试更新不存在的 DOM 元素。

if (CKEDITOR.instances['textarea_name']) 
   CKEDITOR.instances['textarea_name'].destroy(true);

(使用带有 jQ​​uery 适配器的 3.6.2 版本)

【讨论】:

【参考方案22】:
CKEDITOR.instances = new Array();

我在调用创建实例(每页加载一个实例)之前使用它。不知道这会如何影响内存处理以及什么不会。这只有在您想替换页面上的所有实例时才有效。

【讨论】:

我尝试了此页面上的所有解决方案...对于我的环境,这是唯一有效的解决方案(但在我的 ie9 only FF 中无法正常工作)...不知道为什么其他人没有,但经过 1 小时的反复试验,这是唯一对我有用的解决方案。感谢您发布此安德鲁。【参考方案23】:

为了支持动态 (Ajax) 加载表单(不刷新页面),其中包含具有相同(再次调用相同表单)或不同 ID(以前卸载的表单)的文本区域并将它们转换为 CKEditor 元素,我做了以下(使用 JQuery 适配器):

在页面完成每个传递要转换的文本区域的 Ajax 调用后,我调用以下函数:

setupCKeditor()

这看起来像这样(它假设要转换为 RTE 的文本区域具有 class="yourCKClass"):

    /* Turns textAreas into TinyMCE Rich Text Editors where
 * class: tinymce applied to textarea.
 */
function setupCKeditor()

    // define editor configuration      
    var config = skin : 'kama';

    // Remove and recreate any existing CKEditor instances
    var count = 0;
    if (CKEDITOR.instances !== 'undefined') 
        for(var i in CKEDITOR.instances) 

            var oEditor   = CKEDITOR.instances[i];
            var editorName = oEditor.name;

             // Get the editor data.
            var data = $('#'+editorName).val();

            // Check if current instance in loop is the same as the textarea on current page
            if ($('textarea.yourCKClass').attr('id') == editorName) 
                if(CKEDITOR.instances[editorName]) 

                    // delete and recreate the editor
                    delete CKEDITOR.instances[editorName];
                    $('#'+editorName).ckeditor(function()  ,config);
                    count++;

                
               


        
    

    // If no editor's exist in the DOM, create any that are needed.             
    if (count == 0)

        $('textarea.yourCKClass').each( function(index) 

                var editorName = $(this).attr('id');
                $('#'+editorName).ckeditor(function()  $('#'+editorName).val(data); ,config);

            );

    



我应该提到这一行:

$('#'+editorName).ckeditor(function()  $('#'+editorName).val(data); ,config);

可以(并且应该)很简单:

$('#'+editorName).ckeditor(function()  ,config);

但是我发现编辑器通常会在加载后显示正确的内容一秒钟,然后它们会清空所需内容的编辑器。因此,带有回调代码的那一行会强制 CKEditor 内容与原始 textarea 内容相同。使用时会导致闪烁。如果您可以避免使用它,请这样做..

【讨论】:

【参考方案24】:

对于ajax请求,

 for(k in CKEDITOR.instances)
    var instance = CKEDITOR.instances[k];
    instance.destroy()
 
  CKEDITOR.replaceAll();

此片段会从文档中删除所有实例。 然后创建新实例。

【讨论】:

【参考方案25】:

我选择重命名所有实例而不是销毁/替换 - 因为有时加载的 AJAX 实例并没有真正替换页面核心上的实例...在 RAM 中保留更多,但这样冲突更少。

    if (CKEDITOR && CKEDITOR.instances) 
        for (var oldName in CKEDITOR.instances) 
            var newName = "ajax"+oldName;
            CKEDITOR.instances[newName] = CKEDITOR.instances[oldName];
            CKEDITOR.instances[newName].name = newName;
            delete CKEDITOR.instances[oldName];
        
    

【讨论】:

【参考方案26】:

我在 jQuery 对话框中遇到了同样的问题。

如果您只想删除以前的数据,为什么要销毁实例?

function clearEditor(id)

  var instance = CKEDITOR.instances[id];
  if(instance)
  
    instance.setData( '' );
  

【讨论】:

【参考方案27】:

我知道了

删除 CKEDITOR.instances[editorName];

本身,实际上删除了实例。我读过和看到的所有其他方法,包括在 *** 上从用户那里找到的方法,都对我不起作用。

在我的情况下,我使用 ajax 调用来提取包裹在 and 周围的内容的副本。问题恰好是因为我正在使用 jQuery .live 事件来绑定“编辑此文档”链接,然后在 ajax 加载成功后应用 ckeditor 实例。这意味着,当我单击另一个链接与另一个 .live 事件的链接时,我必须使用删除 CKEDITOR.instances[editorName] 作为我清除内容窗口(持有表单)任务的一部分,然后重新获取持有的内容在数据库或其他资源中。

【讨论】:

【参考方案28】:

我遇到过类似的问题,我们为通过 ajax 加载的内容创建了多个 CKeditor 实例。

CKEDITOR.remove()

将 DOM 保留在内存中,并没有删除所有绑定。

CKEDITOR.instance[instance_id].destroy()

每当我使用来自 ajax 的新数据创建新实例时,都会出现错误 i.contentWindow 错误。但这只是直到我发现在清除 DOM 后我正在销毁实例。

当实例和它的 DOM 出现在页面上时使用 destroy(),然后它就可以正常工作了。

【讨论】:

【参考方案29】:

我遇到了同样的问题,我收到了一个空引用异常,并且“空”这个词将显示在编辑器中。我尝试了一些解决方案,包括将编辑器升级到 3.4.1 均无济于事。

我最终不得不编辑源代码。在 _source\plugins\wysiwygarea\plugin.js 的第 416 到 426 行,有一个像这样的 sn-p:

iframe = CKEDITOR.dom.element.createFromHtml( '&lt;iframe' + ... + '></iframe>' );

至少在 FF 中,iframe 在需要时还没有完全实例化。我在该行之后用 setTimeout 函数包围了函数的其余部分:

iframe = CKEDITOR.dom.element.createFromHtml( '<iframe' + ... + '></iframe>' );
setTimeout(function()
 
    // Running inside of Firefox chrome the load event doesn't bubble like in a normal page (#5689)
    ...
, 1000);

;

// The script that launches the bootstrap logic on 'domReady', so the document
...

文本现在在模式对话框中呈现一致。

【讨论】:

【参考方案30】:

确实,从代码中删除“.ckeditor”类可以解决问题。我们大多数人都遵循 ckeditor 文档中的 jQuery 集成示例:

$('.jquery_ckeditor')
.ckeditor( function()  /* callback code */ ,  skin : 'office2003'  );

并想“......也许我可以摆脱'.jquery_'部分”。

我一直在浪费时间调整回调函数(因为 skin:'office2003' 确实有效),而问题来自其他地方。

我认为文档应该提到不建议使用“ckeditor”作为类名,因为它是保留关键字。

干杯。

【讨论】:

以上是关于CKEditor 实例已经存在的主要内容,如果未能解决你的问题,请参考以下文章

CKEditor的第二个实例没有保存到db | Ruby on Rails

如何从 CKEditor 5 实例中获取数据

如何正确销毁 CKEditor 实例?

如何使用ng-repeat获取ckeditor的实例?

无法获取 CKEditor 的实例

CKEDITOR:从Javascript中的多个实例名称中获取数据