I have a contenteditable div, it has some paragraphs in it.
Assuming a situation, when I click the button “set caret position”, the main div will focus, the caret will start at position number 8 of second paragraph, which means the caret will appear after the word “draw”.
How would you do it?
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="text/javascript">
$(function(){
//code
});
</script>
<div id="main" contenteditable="true" style="border:solid 1px black">
<div>Said Hamlet to Ophelia,</div>
<div>I'll draw a sketch of thee,</div>
<div>What kind of pencil shall I use?</div>
<div>2B or not 2B?</div>
</div>
<button>Set caret position</button>
var char = 3, sel; // character at which to place caret
content.focus();
if (document.selection) {
sel = document.selection.createRange();
sel.moveStart('character', char);
sel.select();
}
else {
sel = window.getSelection();
sel.collapse(content.firstChild, char);
}
Took me ages to figure out because I was testing on an anchor - turns out IE doesn’t like doing focus() on an anchor that is contentEditable. Other elements (p, div) seem to be OK!
In this code, the id blinking represents the node where the caret is. the id blinking has index of 2 inside body container.
What I am trying to archive is, when I open a working document, I want the caret to be exact position when I left off.
To do so I need to store the words offset and the node which contain the caret, but the node returned by selection.anchorNode is an object, we can’t store object in database. Therefore, we need to get the index value of the node. so that we can do something like this
sel.collapse(content.childsNode[4], 23);// put the caret at fifth node, offset 23 characters
Back to my question, how do we capture index of the node where the caret is ?
Those codes on Stackoverflow basically get selection, and restore it as long as page is live. They can’t store values for later use.
After five days of struggling, with the help of yours and Tim Down at Stack overflow I’ve got it to work.
var range, selection, star, end, selectedText, startNode, endNode, containerId ="editableDiv";
function getCaretPos(){
range= window.getSelection().getRangeAt(0);
start = range.startOffset;
end = range.endOffset;
startNode = range.startContainer;
endNode = range.endContainer;
selectedText = range.toString();
// alert('start: '+start+'\
\
end: '+ end+'\
\
text: '+selectedText +'\
\
startnode: '+$(startNode.parentNode).index()+'\
\
endnode: '+$(endNode.parentNode).index() );
}
function restoreCaretPos(){
selection = window.getSelection();
if (selection.rangeCount > 0) {
selection.removeAllRanges();
selection.addRange(range);
}
}
function setCaretPos(startNodeIndex, endNodeIndex, start, end){
range = document.createRange();
var editableContainer = document.getElementById(containerId);
range.setStart(editableContainer.childNodes[startNodeIndex].firstChild,start);
range.setEnd(editableContainer.childNodes[endNodeIndex].firstChild,end);
var selection = window.getSelection();
selection.addRange(range);
}
The tricky part is to get the node index, as you can see in function setCaretPos, there is node index utilizing. And I attained it by using jquery index() function, as seen in alert() function above.
For this snippet work correctly, The html inside contenteditable needs to be controlled strictly. Hopefully, I can improve it to work with arbitrary html.