My HTML:
<div id="text" contenteditable="true">abcd<img src="icon.gif"/>efgh</div>
My caret = 5;
so I want to set the caret poisition to be immediately after the image as the image is treated as 1 character.
So I wrote thise code:
var node = document.querySelector("div");
node.focus();
var textNode = node.firstChild;
var caret = 5;
var range = document.createRange();
range.setStart(textNode, caret);
range.setEnd(textNode, caret);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
But I get this error:
Uncaught DOMException: Failed to execute 'setStart' on 'Range': The offset 5 is larger than or equal to the node's length (4).
Please suggest how I might achieve this ? I might have several images before the caret position I desire and each image would be treated as 1 character.
Your textNode
has 3 children (1 text, 1 element, 1 text) and therefore you can't just use firstChild
.
You need to iterate over the childNodes
of the <div>
and track the character count where the nodeType
of the childNode
equals Node.TEXT_NODE
(see here on MDN). Where the character count is less than the value of caret
you can deduct that from caret
and move onto the next text node.
Per your condition that:
I desire and each image would be treated as 1 character
The code will deduct 1 from caret
where nodeType == 1
i.e. Node.ELEMENT_NODE
Here is a code example with multiple icons:
var node = document.querySelector("div");
node.focus();
var caret = 24;
var child;
var childNodeIndex = 0;
for(var i=0; i<node.childNodes.length; i++) {
child = node.childNodes[i];
// Node.ELEMENT_NODE == 1
// Node.TEXT_NODE == 3
if(child.nodeType == Node.TEXT_NODE) {
// keep track of caret across text childNodes
if(child.length <= caret) {
caret -= child.length;
} else {
break;
}
} else if (child.nodeType == Node.ELEMENT_NODE) {
// condition that 'each image would be treated as 1 character'
if(caret > 0) {
caret -= 1;
} else {
break;
}
};
childNodeIndex += 1;
}
var textNode = node.childNodes[childNodeIndex];
// your original code continues here...
var range = document.createRange();
range.setStart(textNode, caret);
range.setEnd(textNode, caret);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
<div id="text" contenteditable="true">a<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/><img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/><img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/><img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/>b<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/>cdefghijkl<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/>mnopq<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/>rst<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/>uvw<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/>xyz<img src="https://www.splitbrain.org/_static/ico/circular/ico/add.png"/></div>