繁体   English   中英

为 contentEditable div 保存和恢复插入符号 position

[英]Saving and Restoring caret position for contentEditable div

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

回到 2016 年 :)
在我在这里遇到解决方案后,它们不适合我,因为每次输入后我的 DOM 都被完全替换了。 我做了更多的研究,并提供了一个简单的解决方案,可以按角色的位置保存光标,这对我来说是完美的。

这个想法很简单。

  1. 找到插入符号前的字符长度并保存。
  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){ const NODE_TYPE = NodeFilter.SHOW_TEXT; var treeWalker = document.createTreeWalker(root, NODE_TYPE, function next(elem) { if(index > elem.textContent.length){ index -= elem.textContent.length; return NodeFilter.FILTER_REJECT } return NodeFilter.FILTER_ACCEPT; }); var c = treeWalker.nextNode(); return { node: c? c: root, position: index }; }
 <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>

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

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}

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

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

原答案

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM