2015-04-22 65 views
6

我读过这excellent answer几乎相同的问题。然而,我已经尝试了所有推荐的技术,并且它们都不起作用。CKEDITOR - 无法在DOM修改后恢复光标位置

这种情况是,我从编辑器中取出当前的HTML,并将范围内的某些片段打包成标签。然后,我将现在修改的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(); 

我也尝试过保存在restoreCursorLocation功能使用

var ranges = editor.getSelection().getRanges(); 

editor.getSelection().selectRanges(ranges); 

的范围内。

回答

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. 在恢复选择之前,我也是关注编辑器,但这是“以防万一”的解决方案,因为我注意到编辑器选择没有它的书签。但是,如果出于某种原因,你失去了选择,这将是另一回事。

在这里,你已经工作示例:http://jsfiddle.net/j_swiderski/nwbsywnn/1/

+0

只需添加到此答案 - 一个更安全的解决方案将使用['CKEDITOR.style'](http://docs.ckeditor.com/#!/api/CKEDITOR.style),因为它只会触及必须更改的树的这些部分。但是,要使用它,您需要实现一个在DOM中查找文本的函数,这是一件非常复杂的事情。 – Reinmar

+0

感谢Reinmar的出色答案。在真实的代码中,我使用CKEDITOR.htmlParser来浏览文档并进行修改。不只是做一个简单的.replace()。我会看看使用样式,看看它是否更好。不知道为什么createBookmark(true)以前不适合我。但现在是。 –

1

检查,当你在https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML

设置innerHTML的默认行为,删除所有元素的子项,解析内容串和得到的节点分配作为元素的孩子

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

无论如何,解决方案非常简单。当你作为参数,它将使用IDS作为参考,而不是存储的DOM元素,所以你可以在innerHTML的变化之后再恢复传递true

更改storeCursorLocation功能,这

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

{编辑}

阅读解决方案2@Reinmar他说

如果你能避免不受控制innerHTML的变化,而是追加/删除/移动一些节点,然后只记得你必须保留这些元素,这种方法将完美运作。如果你的修改也改变了选择,你也可以移动书签的元素。

这就是你如何做到这一点,如果你不能替换元素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); 
}; 

基本上我横切所述第一p的节点在chkeditor体和更换包含的Lorem与跨度和前和作为文本元素之后加入剩余的文本类型的文本的唯一的节点。如果你像所做的那样替换整个文本,它会从DOM中删除书签,所以当你尝试恢复时,它们不存在。

+0

在实际的代码我使用CKEDITOR.htmlParser做类似你的第二个解决方案的东西。它的确比使用.replace()更好。但是.replace()对于这个例子来说要复杂得多,并且替换发生的方式不是问题。 –

+1

使用第一种解决方案,您可以使用替换。书签使用ID保存并恢复。我添加了第二个解决方案,因为有些情况下使用替换比替换单个文本元素更难。这就是你说的一个例子,我个人主张提供更多的数据来更真实地解决问题。 – devconcept