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

时间:2011-01-02 01:40:07

标签: javascript jquery contenteditable

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

3 个答案:

答案 0 :(得分:23)

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

想法很简单。

  1. 在插入符号之前找到charachters的长度并保存。
  2. 更改DOM。
  3. 使用TreeWalker在} text nodes context node上行走并计算字符数,直到我们找到正确的text node及其中的位置
  4. 两个边缘案例:

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

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

    3. 
      
      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
          };
      }
      
      <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js"></script>
      <link href="https://rawgit.com/PrismJS/prism/gh-pages/themes/prism.css" rel="stylesheet"/>
      <style>
        *{
          outline:none
          }
      </style>  
      <h3>Edit the CSS Snippet </H3>
      <pre>
          <code class="language-css" contenteditable=true >p { color: red }</code>
      </pre>
      
      <script >
        var code = document.getElementsByTagName('code')[0];
        
        code.addEventListener('input',function () {
              var restore = saveCaretPosition(this);
              Prism.highlightElement(this);
              restore();
          })
      </script>
      &#13;
      &#13;
      &#13;

答案 1 :(得分:11)

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

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

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

<强> HTML

<div id=bE contenteditable=true></div>

<强> 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}

答案 2 :(得分:8)

更新:我已将Rangy的代码移植到独立的Gist:

https://gist.github.com/timdown/244ae2ea7302e26ba932a43cb0ca3908

原始回答

您可以使用Rangy,我的跨浏览器范围和选择库。它有selection save and restore module,似乎非常适合您的需求。

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