Имам contentEditable
div, чийто innerHTML
може да се актуализира чрез AJAX, докато редактирате. Проблемът е, че когато промените съдържанието на div, той премества курсора в края на div (или губи фокус в зависимост от браузъра). Какво е добро решение за кръстосани браузъри за съхраняване на позицията на каретката, преди да промените innerHTML
и след това да я възстановите?
Запазване и възстановяване на позицията на каретката за contentEditable div
Отговори (3)
обратно към 2016 г. :)
След като попаднах на решения тук и те не ми паснаха, защото моят DOM се сменяше напълно след всяко писане. Направих повече проучвания и предлагам просто решение, което запазва курсора според позицията на символа, което работи перфектно за мен.
Идеята е много проста.
- намерете дължината на знаците преди каретката и я запазете.
- променете DOM.
- използвайки
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){
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>
range.setStart(pos.node ,pos.position-(lastKeypress == 13 ? 0:1));
, за да предотвратите движението на курсора :-)
- person Tschallacka; 14.09.2017
Prism
. Поправих грешката в цикъла (jsfiddle.net/80ovoxr9/10/]. ( Shift + Enter да ви помогне да слезете по линията)
- person pery mimon; 19.02.2018
index > elem.textContent.length
и position: index
, но не мога да редактирам отговора, защото опашката е пълна.
- person tvanc; 25.04.2020
Знам, че това е древна тема, но реших, че ще осигуря алтернативно решение без библиотека
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}
innerHTML
, което не е гарантирано, че е вярно.
- person Tim Down; 31.03.2015
Актуализация: Пренесох кода на Rangy към самостоятелен Gist:
https://gist.github.com/timdown/244ae2ea7302e26ba932a43cb0ca3908
Оригинален отговор
Бихте могли да използвате Rangy, моята библиотека за диапазон и избор на различни браузъри. Има модул за запазване и възстановяване на селекция, който изглежда добре- подходящи за вашите нужди.
Подходът не е сложен: той вмъква маркерни елементи в началото и края на всеки избран диапазон и използва тези маркерни елементи, за да възстанови отново границите на диапазона по-късно, което може да се приложи без Rangy в малко код (и дори бихте могли да адаптирате Собствен код на Rangy). Основното предимство на Rangy е поддръжката на IE ‹= 8.