Get Caret (Cursor) Position in Contenteditable Area Containing HTML Content

Get contentEditable caret position

The following code assumes:

  • There is always a single text node within the editable <div> and no other nodes
  • The editable div does not have the CSS white-space property set to pre

If you need a more general approach that will work content with nested elements, try this answer:

https://stackoverflow.com/a/4812022/96100

Code:

function getCaretPosition(editableDiv) {  var caretPos = 0,    sel, range;  if (window.getSelection) {    sel = window.getSelection();    if (sel.rangeCount) {      range = sel.getRangeAt(0);      if (range.commonAncestorContainer.parentNode == editableDiv) {        caretPos = range.endOffset;      }    }  } else if (document.selection && document.selection.createRange) {    range = document.selection.createRange();    if (range.parentElement() == editableDiv) {      var tempEl = document.createElement("span");      editableDiv.insertBefore(tempEl, editableDiv.firstChild);      var tempRange = range.duplicate();      tempRange.moveToElementText(tempEl);      tempRange.setEndPoint("EndToEnd", range);      caretPos = tempRange.text.length;    }  }  return caretPos;}
#caretposition {  font-weight: bold;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><div id="contentbox" contenteditable="true">Click me and move cursor with keys or mouse</div><div id="caretposition">0</div><script>  var update = function() {    $('#caretposition').html(getCaretPosition(this));  };  $('#contentbox').on("mousedown mouseup keydown keyup", update);</script>

Get caret (cursor) position in contentEditable area containing HTML content

UPDATE

I've written a simpler version of this that also works in IE < 9:

https://stackoverflow.com/a/4812022/96100

Old Answer

This is actually a more useful result than a character offset within the text of the whole document: the startOffset property of a DOM Range (which is what window.getSelection().getRangeAt() returns) is an offset relative to its startContainer property (which isn't necessarily always a text node, by the way). However, if you really want a character offset, here's a function that will do it.

Here's a live example: http://jsfiddle.net/timdown/2YcaX/

Here's the function:

function getCharacterOffsetWithin(range, node) {
var treeWalker = document.createTreeWalker(
node,
NodeFilter.SHOW_TEXT,
function(node) {
var nodeRange = document.createRange();
nodeRange.selectNode(node);
return nodeRange.compareBoundaryPoints(Range.END_TO_END, range) < 1 ?
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
false
);

var charCount = 0;
while (treeWalker.nextNode()) {
charCount += treeWalker.currentNode.length;
}
if (range.startContainer.nodeType == 3) {
charCount += range.startOffset;
}
return charCount;
}

get cursor position in contenteditable div with html-tags

This gives you what you want. It will give you the length of the html also.

https://jsfiddle.net/ko5Lhbeo/

Change this:

$('.msg').on('keyup',function(e){
$('.cursor').html(getCaretPosition(document.getElementById('msg')));
})

to this:

// Added DOMSubtreeModified event here to fire whenever any modifications to the sub tree
$('.msg').on('keyup DOMSubtreeModified',function(e) {
$('.cursor').html($('#msg').html().length);
})

Get and set cursor position with contenteditable div

A good rich-text editor is one of the harder things to do currently, and is pretty much a project by itself (unfriendly API, huge number of corner cases, cross-browser differences, the list goes on). I would strongly advise you to try and find an existing solution.

Some libraries that can be used include:

  • Quill (http://quilljs.com)
  • WYSGIHTML (http://wysihtml.com)
  • CodeMirror library (http://codemirror.net)

How to set the caret (cursor) position in a contenteditable element (div)?

In most browsers, you need the Range and Selection objects. You specify each of the selection boundaries as a node and an offset within that node. For example, to set the caret to the fifth character of the second line of text, you'd do the following:

function setCaret() {
var el = document.getElementById("editable")
var range = document.createRange()
var sel = window.getSelection()

range.setStart(el.childNodes[2], 5)
range.collapse(true)

sel.removeAllRanges()
sel.addRange(range)
}
<div id="editable" contenteditable="true">
text text text<br>text text text<br>text text text<br>
</div>

<button id="button" onclick="setCaret()">focus</button>

Set Caret Position in 'contenteditable' div that has children

So, I was experiencing the same issue and decided to write my own routine quickly, it walks through all the child nodes recursively and set the position.
Note how this takes a DOM node as argument, not a jquery object as your original post does

// Move caret to a specific point in a DOM element
function SetCaretPosition(el, pos){

// Loop through all child nodes
for(var node of el.childNodes){
if(node.nodeType == 3){ // we have a text node
if(node.length >= pos){
// finally add our range
var range = document.createRange(),
sel = window.getSelection();
range.setStart(node,pos);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
return -1; // we are done
}else{
pos -= node.length;
}
}else{
pos = SetCaretPosition(node,pos);
if(pos == -1){
return -1; // no need to finish the for loop
}
}
}
return pos; // needed because of recursion stuff
}

I hope this'll help you!



Related Topics



Leave a reply



Submit