Сохранение и восстановление позиции каретки для contentEditable div - PullRequest
13 голосов
/ 02 января 2011

У меня есть contentEditable div, значение innerHTML которого можно обновить через AJAX во время редактирования. Проблема в том, что при изменении содержимого div он перемещает курсор в конец div (или теряет фокус в зависимости от браузера). Что такое хорошее кросс-браузерное решение для сохранения позиции каретки перед изменением innerHTML, а затем для ее восстановления?

Ответы [ 3 ]

24 голосов
/ 20 июля 2016

назад в 2016:)
После того, как я наткнулся на это решение, оно меня не устраивало, потому что мой DOM заменялся полностью после каждой печати.Я провел больше исследований и пришел к простому решению, которое сохраняет курсор по позиции персонажа, которое мне подходит идеально .

Идея очень проста.найдите длину символов перед кареткой и сохраните ее.

измените DOM. , используя TreeWalker, чтобы пройти только по text nodes из context node и считая символы, пока мы не получим право text node и положение внутри него

Два крайних случая:

  1. содержимое полностью удалено, поэтому text node:
    so : переместить курсор в начало контекстаузел

  2. меньше содержимого, чем указано index:
    так : переместить курсор в конец последнего узла

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 }
11 голосов
/ 21 октября 2014

Я знаю, что это древняя ветка, но я подумал, что смогу предложить альтернативное небиблиотечное решение

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}
8 голосов
/ 07 февраля 2011

Обновление: я перенес код Рэнги в автономный Gist:

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

Оригинальный ответ

Вы можете использовать Rangy , мой кросс-браузерный диапазон и библиотеку выбора.Он имеет модуль сохранения и восстановления выбора , который, кажется, хорошо соответствует вашим потребностям.

Подход не сложен: он вставляет элементы маркера в начало и конец каждого выбранного диапазона и используетэти элементы маркера для восстановления границ диапазона позже, что может быть реализовано без Rangy в небольшом количестве кода (и вы даже можете адаптировать собственный код Rangy ).Основным преимуществом Rangy является поддержка IE <= 8. </p>

...