JSFiddle - React, Tailwind, and code Playground
by Mottie
HTML
<label>Start: <input id="start" type="number"/></label>
<label>End: <input id="end" type="number"/></label> (updates on click & keyup)
<button id="set">Set</button>
<p></p>
<!-- inline BR's behave differently from <br> on their own separate line -->
<div id="ce" contenteditable>012345<br><br><br>9012345</div>
<!-- get/set caret needs to work with these examples as well
* <br> at beginning
<div id="ce" contenteditable><br>12345<br><br><br>9012345</div>
* <br>'s wrapped in a <div>
<div id="ce" contenteditable><div><br></div>12345<div><br></div><div><br></div><div><br></div>9012345</div>
-->
CSS
input {
width: 40px;
}
[contenteditable] {
white-space: pre;
}
JavaScript
const $el = $('ce'),
$start = $('start'),
$end = $('end');
function getCaret(el) {
let start, end;
const range = document.getSelection().getRangeAt(0),
preSelectionRange = range.cloneRange(),
postSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(el);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
postSelectionRange.selectNodeContents(el);
postSelectionRange.setEnd(range.endContainer, range.endOffset);
start = preSelectionRange.toString().length;
end = start + range.toString().length;
// count <br>'s and adjust start & end
if (start > 0) {
var node,
i = el.children.length;
while (i--) {
node = el.children[i];
if (node.nodeType === 1 && node.nodeName === 'BR') {
start += preSelectionRange.intersectsNode(el.children[i]) ? 1 : 0;
end += postSelectionRange.intersectsNode(el.children[i]) ? 1 : 0;
}
}
}
return {start, end};
}
function setCaret(el, start, end) {
var node, i, nextCharIndex, sel,
charIndex = 0,
nodeStack = [el],
foundStart = false,
stop = false,
range = document.createRange();
range.setStart(el, 0);
range.collapse(true);
while (!stop && (node = nodeStack.pop())) {
// BR's aren't counted, so we need to increase the index when one
// is encountered
if (node.nodeType === 1 && node.nodeName === 'BR') {
charIndex++;
} else if (node.nodeType === 3) {
nextCharIndex = charIndex + node.length;
if (!foundStart && start >= charIndex && start <= nextCharIndex) {
range.setStart(node, start - charIndex);
foundStart = true;
}
if (foundStart && end >= charIndex && end <= nextCharIndex) {
range.setEnd(node, end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
sel =...