Get Contenteditable Caret Position
Solution 1:
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 topre
If you need a more general approach that will work content with nested elements, try this answer:
https://stackoverflow.com/a/4812022/96100
Code:
functiongetCaretPosition(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;
}
}
} elseif (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;
}
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><divid="contentbox"contenteditable="true">Click me and move cursor with keys or mouse</div><divid="caretposition">0</div><script>var update = function() {
$('#caretposition').html(getCaretPosition(this));
};
$('#contentbox').on("mousedown mouseup keydown keyup", update);
</script>
Solution 2:
A few wrinkles that I don't see being addressed in other answers:
- the element can contain multiple levels of child nodes (e.g. child nodes that have child nodes that have child nodes...)
- a selection can consist of different start and end positions (e.g. multiple chars are selected)
- the node containing a Caret start/end may not be either the element or its direct children
Here's a way to get start and end positions as offsets to the element's textContent value:
// node_walk: walk the element tree, stop when func(node) returns falsefunctionnode_walk(node, func) {
var result = func(node);
for(node = node.firstChild; result !== false && node; node = node.nextSibling)
result = node_walk(node, func);
return result;
};
// getCaretPosition: return [start, end] as offsets to elem.textContent that// correspond to the selected portion of text// (if start == end, caret is at given position and no text is selected)functiongetCaretPosition(elem) {
var sel = window.getSelection();
var cum_length = [0, 0];
if(sel.anchorNode == elem)
cum_length = [sel.anchorOffset, sel.extentOffset];
else {
var nodes_to_find = [sel.anchorNode, sel.extentNode];
if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
returnundefined;
else {
var found = [0,0];
var i;
node_walk(elem, function(node) {
for(i = 0; i < 2; i++) {
if(node == nodes_to_find[i]) {
found[i] = true;
if(found[i == 0 ? 1 : 0])
returnfalse; // all done
}
}
if(node.textContent && !node.firstChild) {
for(i = 0; i < 2; i++) {
if(!found[i])
cum_length[i] += node.textContent.length;
}
}
});
cum_length[0] += sel.anchorOffset;
cum_length[1] += sel.extentOffset;
}
}
if(cum_length[0] <= cum_length[1])
return cum_length;
return [cum_length[1], cum_length[0]];
}
Solution 3:
$("#editable").on('keydown keyup mousedown mouseup',function(e){
if($(window.getSelection().anchorNode).is($(this))){
$('#position').html('0')
}else{
$('#position').html(window.getSelection().anchorOffset);
}
});
body{
padding:40px;
}
#editable{
height:50px;
width:400px;
border:1px solid #000;
}
#editablep{
margin:0;
padding:0;
}
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script><divcontenteditable="true"id="editable">move the cursor to see position</div><div>
position : <spanid="position"></span></div>
Solution 4:
Kinda late to the party, but in case anyone else is struggling. None of the Google searches I've found for the past two days have come up with anything that works, but I came up with a concise and elegant solution that will always work no matter how many nested tags you have:
functioncursor_position() {
var sel = document.getSelection();
sel.modify("extend", "backward", "paragraphboundary");
var pos = sel.toString().length;
if(sel.anchorNode != undefined) sel.collapseToEnd();
return pos;
}
// Demo:var elm = document.querySelector('[contenteditable]');
elm.addEventListener('click', printCaretPosition)
elm.addEventListener('keydown', printCaretPosition)
functionprintCaretPosition(){
console.log( cursor_position(), 'length:', this.textContent.trim().length )
}
<divcontenteditable>some text here <i>italic text here</i> some other text here <b>bold text here</b> end of text</div>
It selects all the way back to the beginning of the paragraph and then counts the length of the string to get the current position and then undoes the selection to return the cursor to the current position. If you want to do this for an entire document (more than one paragraph), then change paragraphboundary
to documentboundary
or whatever granularity for your case. Check out the API for more details. Cheers! :)
Post a Comment for "Get Contenteditable Caret Position"