Set caret position in contenteditable div

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>

I am a bit confused. Do you mean, the user clicks the button, and the caret is inserted after the word ‘draw’. And that’s it?

In firefox (and probably webkit and opera too), where “content” is the contentEditable node:

var char = 3; // character at which to place caret  content.focus();
  var sel = window.getSelection();
  sel.collapse(content.firstChild, char);

Here’s a solution that works in IE too:

  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!

Great, it works like charm. I’ve been battling with this for five 4 days now.

Raffles do you know how to get the node’s index where the caret is? I only need solution for compliant browsers.

What do you mean the index? You should know where the caret is, as that’s where you’re placing it!

Index, by my definition is, the numbers indicate the position order of elements within a container.

<!DOCTYPE html>
<html>
<head>
<style>
</style>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
</head>
<body>
  <div></div>
  <div></div>
  <div id="blinking"></div>
  <div></div>
  <div></div>
  <div></div>
<script>
alert($("#blinking").index());
</script>
</body>
</html>

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 ?

You really should have mentioned that you wanted to save the caret position from the outset.

There is a stack overflow question (which was very easy to find via google) addressing your issue. See the [URL=“http://stackoverflow.com/questions/1181700/set-cursor-position-on-contenteditable-div/3323835#3323835”]solution by Nico Burns.

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.