CKEDITOR - DOM修改后无法恢复光标位置

时间:2015-04-22 18:17:52

标签: ckeditor

我已经将excellent answer读到了几乎相同的问题。但是,我尝试了@Reinmar推荐的所有技术,但似乎都没有。

情况是我从编辑器中获取当前的HTML并将某些部分包装在 span 标记中。然后我将现在修改的HTML设置回来并尝试恢复用户的光标位置。没有技术可行。

以下是重现此问题的一个非常简单的示例:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="//cdn.ckeditor.com/4.4.7/standard/ckeditor.js"></script>

</head>
<body>
    <textarea id="cktest"><p>Sometimes Lorem. Sometime Ipsum. Always dolor.</p></textarea>

    <script type="text/javascript">

        (function () {
            var checkTimeout;
            var bookmark;

            var storeCursorLocation = function(editor) {
                bookmark = editor.getSelection().createBookmarks();
            };

            var restoreCursorLocation = function(editor) {
                editor.getSelection().selectBookmarks(bookmark);
            };

            var validateText = function(editor) {
                storeCursorLocation(editor);
                var data = editor.document.getBody().getHtml();
                data = data.replace("Lorem", "<span class='err-item'>Lorem</span>");
                editor.document.getBody().setHtml(data);
                restoreCursorLocation(editor);
            };


            CKEDITOR.replace('cktest', {
                on: {
                    'instanceReady': function(evt) {

                    },
                    'key' : function(evt) {
                        clearTimeout(checkTimeout);
                        checkTimeout = setTimeout(function () {
                            validateText(evt.editor);
                        }, 1000);
                    }
                }
            });
        })();

    </script>
</body>
</html>

当用户按下某个键时,此代码启动一个计时器,然后在他们停止按键进行检查后等待1秒钟。

将其复制到新的.html文件并在您喜欢的浏览器中运行(我使用的是Chrome)。

当CKEditor加载时,使用鼠标将光标放在文本中间的某个位置。然后按CTRL键并等待1秒钟。您将看到光标跳回文本的开头。

此代码示例使用

editor.getSelection().createBookmarks();

创建书签。但我也尝试过:

editor.getSelection().createBookmarks(true);

editor.getSelection().createBookmarks2();

我还尝试使用

保存范围
var ranges = editor.getSelection().getRanges();

editor.getSelection().selectRanges(ranges);
在restoreCursorLocation函数中。

2 个答案:

答案 0 :(得分:5)

        (function () {
            var checkTimeout;
            var bookmark;

            var storeCursorLocation = function( editor ) {
               bookmark = editor.getSelection().createBookmarks( true );
            };

            var restoreCursorLocation = function( editor ) {
                //editor.focus();
                editor.getSelection().selectBookmarks( bookmark );
            };

            var validateText = function( editor ) {
                storeCursorLocation( editor );

                var data = editor.document.getBody().getHtml();
                data = data.replace( "spaceflight", "<span class='err-item'>spaceflight</span>" );
                editor.document.getBody().setHtml( data );
                restoreCursorLocation( editor );
                //fire this event after DOM changes if working with widgets
                //editor.fire( 'contentDomInvalidated' ); 
            };


           var editor = CKEDITOR.replace( 'editor1', {
                extraAllowedContent : 'span(err-item)',             
                on: {
                    "pluginsLoaded" : function( event ){
                        editor.on( 'contentDom', function() {
                            var editable = editor.editable();                   
                            editable.attachListener( editable, 'keyup', function( e ) { 
                                clearTimeout( checkTimeout );
                                checkTimeout = setTimeout(function () {
                                    validateText( editor );
                                }, 100 );
                            });
                        });
                    }
                }
            });
        })();

我检查了你的代码,做了一些修正,上面似乎工作正常。我知道你说过你已经尝试了但是对我来说createBookmarks(true)已经做到了。

说明和注释:

  1. 您需要使用createBookmarks(true)将唯一范围插入HTML。此类书签不受您在DOM中所做更改的影响(当然有限制,例如您的自定义更改会删除书签)。
  2. 使用getBody().getHtml()getBody().setHTML()非常聪明。如果您使用过editor.getData(),则会删除代表书签的空跨度。 但请注意,此类方法可能会破坏小部件,因此需要在发生此类更改后触发contentDomInvalidated事件。
  3. 在恢复选择之前,我还关注编辑器,但这是“以防万一”的解决方案,因为我注意到编辑器选择没有它的书签。但是,如果出于某种原因,您正在失去选择,那么这将是另一回事。
  4. 这里有一个工作示例:http://jsfiddle.net/j_swiderski/nwbsywnn/1/

答案 1 :(得分:1)

https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML

中设置innerHtml时检查默认行为
  

删除所有元素的子元素,解析内容字符串并将结果节点指定为元素的子元素

CKEDITOR中的书签是隐藏的span元素,设置innerHtml将删除所有这些元素。

无论如何,解决方案非常简单。

将storeCursorLocation函数更改为此

var storeCursorLocation = function(editor) {
    bookmark = editor.getSelection().createBookmarks(true);
};

当您将true作为参数传递时,它将使用id作为引用而不是存储DOM元素,以便您可以在innerHtml更改后恢复。

<强> {编辑}

@Reinmar阅读解决方案2 他说

  

如果你可以避免不受控制的innerHTML更改,而是追加/删除/移动一些节点,那么请记住你必须保留这些元素,这个方法将完美地工作。您还可以移动书签&#39;元素,如果你的修改也应该改变选择。

如果你不能替换元素innerHtml的内容,你就是这样做的。

此解决方案效率较低,但可能在某些情况下有效

将validateText函数更改为此。

var validateText = function(editor) {
    storeCursorLocation(editor);
    var parent = editor.document.getBody().$.firstChild,
        nodes = parent.childNodes,
        nodeText,
        words,
        index = 0,
        current,
        newElement;
    while (index < nodes.length) {
        current = nodes[index];
        nodeText = current.nodeValue;
        if (current.nodeType === Node.TEXT_NODE && nodeText.indexOf('Lorem') !== -1) {
            words = nodeText.split('Lorem');
            newElement = document.createTextNode(words[0]);
            parent.insertBefore(newElement, current);
            newElement = document.createTextNode(words[1]);
            parent.insertBefore(newElement, current.nextSibling);
            newElement = document.createElement('span')
            newElement.className = 'err-item';
            newElement.innerHTML = 'Lorem';
            parent.replaceChild(newElement, current);
            break;
        }
        index++;

    }
    restoreCursorLocation(editor);
};

基本上我遍历chkeditor主体中第一个p的节点,只替换包含Lorem的文本类型的节点,并将剩余的文本作为文本元素添加到前后。如果你像你一样替换整个文本,它将从DOM中删除书签,所以当你试图恢复它们时,它们不存在。

相关问题