У меня есть contentEditable
div, innerHTML
которого можно обновить через AJAX во время редактирования. Проблема в том, что при изменении содержимого div он перемещает курсор в конец div (или теряет фокус в зависимости от браузера). Какое хорошее кросс-браузерное решение для хранения позиции каретки перед изменением innerHTML
, а затем для его восстановления?
Сохранение и восстановление позиции каретки для contentEditable div
Ответ 1
вернуться к 2016 году :)
После того, как я наткнулся на это решение, оно мне не подошло, потому что мой DOM заменялся полностью после каждой печати. Я провел больше исследований и пришел с простым решением, которое сохраняет курсор за положением символа, которое идеально подходит для меня.
Идея очень проста.
- найдите длину символов перед кареткой и сохраните ее.
- изменить DOM.
- использование
TreeWalker
дляTreeWalker
толькоtext nodes
context node
и подсчета символов до тех пор, пока мы не получим правильныйtext node
и положение внутри него
Два крайних случая:
-
содержимое полностью удалено, поэтому
text node
:
итак: переместить курсор в начало узла контекста -
там меньше контента, чем указано в
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 }</code>
</pre>
<script >
var code = document.getElementsByTagName('code')[0];
code.addEventListener('input',function () {
var restore = saveCaretPosition(this);
Prism.highlightElement(this);
restore();
})
</script>
Ответ 2
Я знаю, что это древняя тема, но я подумал, что я бы предложил альтернативное не библиотечное решение
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}
Ответ 3
Вы можете использовать Rangy, мою кросс-браузерную область и библиотеку выбора. Он имеет модуль сохранения и восстановления выбора, который кажется вам подходящим.
Подход не является сложным: он вставляет элементы маркера в начале и конце каждого выбранного диапазона и использует эти элементы маркера для восстановления границ диапазона позже, что может быть реализовано без Rangy в не очень большом количестве кода (и вы могли бы даже Rangy собственный код). Основным преимуществом Rangy является поддержка IE <= 8.