Reputation: 439
I have this function which moves caret correctly inside the first line of a contenteditable although it only looks at childNodes[0] so it doesn't move caret inside the second line.
function setcaret(item, pos) {
var range = document.createRange();
var sel = window.getSelection();
range.setStart(item.childNodes[0], pos);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
item.focus();
}
setcaret(divid, 4); //move caret four places in current line
I need to modify this to get the current node so it will work for multiple divs inside the contenteditable like if the caret is inside the second node
<div contenteditable="true"><div>first child</div><div>second |child</div>
Any ideas?
Upvotes: 1
Views: 1048
Reputation: 136707
That is unfortunately not that easy...
To do this, we have to determine ourself which node the character at given index belongs to. And to do this, we need to walk over the childNodes of our container.
This operation can be greatly simplified with a TreeWalker, set up to only iterate over TextNodes:
// this method will allow us to get the TextNode at character index
function getNodeAtCharPos(parent, pos) {
var walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT);
var chars = 0;
var nodeLength = 0;
while (chars < pos && walker.nextNode()) {
nodeLength = walker.currentNode.textContent.length;
chars += nodeLength;
}
return {
node: walker.currentNode,
index: Math.min(pos - (chars - nodeLength), nodeLength)
};
}
function setcaret(item, pos) {
if (pos < 0) return;
var nodeAtPos = getNodeAtCharPos(item, pos);
var range = document.createRange();
var sel = window.getSelection();
range.setStart(nodeAtPos.node, nodeAtPos.index);
sel.removeAllRanges();
sel.addRange(range);
}
var divid = document.getElementById('divid');
btn.onclick = e => setcaret(divid, inp.value);
<div id="divid" contenteditable="true"><div>first child</div><div>second child</div></div>
<br><label>Offset: <input id="inp" type="number" value="4" min="0"><button id="btn">set caret</button></label>
Upvotes: 3