Search code examples
javascripthtmldomckeditor

How to avoid CKEditor instance death after swap node?


I've a HTML table with one CKEditor instance on each row, all works fine until i swap positions between 2 rows. After the swap, values aren't displayed and any use of method setData ends in an JS error.

Basic structure is:

<tr id=1>
  <td>...</td>
  <td>ckeditor1</td>
</tr>
.
.
<tr id=n>
  <td>...</td>
  <td>ckeditorn</td>
</tr>

Method used to swap nodes is

Node.prototype.swapNode = function (node) {
        var nextSibling = this.nextSibling;
        var parentNode = this.parentNode;
        node.parentNode.replaceChild(this, node);
        parentNode.insertBefore(node, nextSibling);  
    };

So, i call Node1.swapNode(Node2), CKEditor loses it's value, then tried to force the values again on CKEditor instance via CKEditor.instances[1].setData(data,{});

That call ends in this stack trace:

   TypeError: this.document.getWindow(...).$ is undefined  ckeditor.js:427:29
        CKEDITOR.dom.selection.prototype.getNative http://localhost/js/packages/ckeditor/ckeditor.js:427:29
        CKEDITOR.dom.selection http://localhost/js/packages/ckeditor/ckeditor.js:425:54
        CKEDITOR.editor.prototype.getSelection http://localhost/js/packages/ckeditor/ckeditor.js:422:319
        CKEDITOR.plugins.undo.Image http://localhost/js/packages/ckeditor/ckeditor.js:1077:358
        b.prototype.save http://localhost/js/packages/ckeditor/ckeditor.js:1072:24
        .init/< http://localhost/js/packages/ckeditor/ckeditor.js:1068:269
        h http://localhost/js/packages/ckeditor/ckeditor.js:10:68
        CKEDITOR.event.prototype</<.fire</< http://localhost/js/packages/ckeditor/ckeditor.js:11:428
        CKEDITOR.editor.prototype.fire http://localhost/js/packages/ckeditor/ckeditor.js:13:67
        .setData http://localhost/js/packages/ckeditor/ckeditor.js:261:79

Tested on FF 52, Opera 52, Chrome 61 @OpenSuse 42.3

Is it any other way to achieve the swap without losing the values? Or at least not ending in that error?

Regards

Snippet:

Node.prototype.swapNode = function (node) {
            var nextSibling = this.nextSibling;
            var parentNode = this.parentNode;
            node.parentNode.replaceChild(this, node);
            parentNode.insertBefore(node, nextSibling);  
};

var element1 = new     CKEDITOR.dom.element(document.getElementById('doc_content1'));
CKEDITOR.replace(element1);

var element2 = new CKEDITOR.dom.element(document.getElementById('doc_content2'));
CKEDITOR.replace(element2);
<html>
<head>
 <script src="https://cdn.ckeditor.com/4.9.2/full/ckeditor.js"></script>
</head>
<body> 
<form>
<table>
<tbody>
<tr id='1'>
    <td><input name="doc_title1" type="text"></td>
    <td><textarea id="doc_content1" name="doc_content1" ></textarea></td>
    <td><input type="button" onclick="javascript: this.parentNode.parentNode.swapNode(document.getElementById('2'));" value='change to 2'></td>
</tr>
<tr id='2'>
    <td><input name="doc_title2" type="text"></td>
    <td><textarea id="doc_content2"  name="doc_content2"></textarea></td>
    <td><input type="button" onclick="javascript: this.parentNode.parentNode.swapNode(document.getElementById('1'));" value='change to 1'>
    </td>
</tr>
</tbody>
</form>
</body>
</html>


Solution

  • You have to destroy the instances first, swap the rows and then recreate the instances. When destroying the instances, their textareas are updated with their data, so when recreating them, the data will not be lost.

    Node.prototype.swapNode = function (node) {
        var firstInstance = 'doc_content' + node.id;
        var secondInstance = 'doc_content' + this.id;
        CKEDITOR.instances[firstInstance].destroy();
        CKEDITOR.instances[secondInstance].destroy();
        var nextSibling = this.nextSibling;
        var parentNode = this.parentNode;
        node.parentNode.replaceChild(this, node);
        parentNode.insertBefore(node, nextSibling);  
        CKEDITOR.replace(document.getElementById(firstInstance));
        CKEDITOR.replace(document.getElementById(secondInstance));
    };
    
    CKEDITOR.replace(document.getElementById('doc_content1'));
    CKEDITOR.replace(document.getElementById('doc_content2'));