保存和恢复contentEditable div的插入位置

我有一个contentEditable div,其innerHTML可以在编辑时通过AJAX更新。 问题是,当您更改div的内容时,它会将光标移动到div的末尾(或根据浏览器失去焦点)。 在更改innerHTML然后恢复它之前,什么是存储插入位置的良好的跨浏览器解决方案?

回到2016年:)
在我遇到这个解决方案后,它并不适合我,因为我的DOM在每次打字后都完全取代了。 我做了更多的reasech,并提供简单的解决方案,保存光标的角色的位置,这对我来说非常合适。

想法很简单。

  1. 在插入之前找到charachters的长度并保存它。
  2. 改变DOM。
  3. 使用TreeWalker只在context node text nodes上行走并计算字符,直到我们得到正确的text node及其中的位置

两个边缘案例:

  1. 内容已完全删除,因此没有text node
    so :将光标移动到上下文节点的开头

  2. index指向的内容较少:
    so :将光标移动到最后一个节点的末尾

 function saveCaretPosition(context){ var selection = window.getSelection(); var range = selection.getRangeAt(0); range.setStart( context, 0 ); var len = range.toString().length; return function restore(){ var pos = getTextNodeAtPosition(context, len); selection.removeAllRanges(); var range = new Range(); range.setStart(pos.node ,pos.position); selection.addRange(range); } } function getTextNodeAtPosition(root, index){ var lastNode = null; var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT,function next(elem) { if(index >= elem.textContent.length){ index -= elem.textContent.length; lastNode = elem; return NodeFilter.FILTER_REJECT } return NodeFilter.FILTER_ACCEPT; }); var c = treeWalker.nextNode(); return { node: c? c: root, position: c? index: 0 }; } 
    

Edit the CSS Snippet

 p { color: red } 

我知道这是一个古老的线索,但我想我会提供一个替代的非库解决方案

http://jsfiddle.net/6jbwet9q/9/

在chrome,FF和IE10 +中测试允许您在保留插入位置/选择的同时更改,删除和恢复html。

HTML

 

JS

 function saveRangePosition() { var range=window.getSelection().getRangeAt(0); var sC=range.startContainer,eC=range.endContainer; A=[];while(sC!==bE){A.push(getNodeIndex(sC));sC=sC.parentNode} B=[];while(eC!==bE){B.push(getNodeIndex(eC));eC=eC.parentNode} return {"sC":A,"sO":range.startOffset,"eC":B,"eO":range.endOffset}; } function restoreRangePosition(rp) { bE.focus(); var sel=window.getSelection(),range=sel.getRangeAt(0); var x,C,sC=bE,eC=bE; C=rp.sC;x=C.length;while(x--)sC=sC.childNodes[C[x]]; C=rp.eC;x=C.length;while(x--)eC=eC.childNodes[C[x]]; range.setStart(sC,rp.sO); range.setEnd(eC,rp.eO); sel.removeAllRanges(); sel.addRange(range) } function getNodeIndex(n){var i=0;while(n=n.previousSibling)i++;return i} 

您可以使用Rangy ,我的跨浏览器范围和选择库。 它有一个选择保存和恢复模块 ,似乎非常适合您的需求。

这种方法并不复杂:它在每个选定范围的开头和结尾插入标记元素,并使用这些标记元素稍后再次恢复范围边界,这可以在没有Rangy的情况下实现很少的代码(你甚至可以适应Rangy自己的代码 )。 Rangy的主要优点是支持IE <= 8。