2015-04-22 48 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