Following this SO post, I am able to place the caret inside a span
element, which is inside a div contenteditable="true"
.
I am able to target whichever span
I desire, via its id
, while also being able to decide which character should the caret be placed after.
But how can I place the caret inside a span that has no text inside?
Simply using the function, as is, gets me this error: TypeError: Range.setStart: Argument 1 is not an object.
Also, for some reason, when the span
has content, it works fine, in Firefox. But not in Chrome, where the caret is placed outside the span
. Any way to also solve this problem?
I am open to jQuery, if it makes things easier.
Here is my code:
JSFiddle
function setCaret(x, y) {
var element = document.getElementById(x);
var range = document.createRange();
var node;
node = document.getElementById(y);
range.setStart(node.childNodes[0], 0);
var sel = window.getSelection();
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
element.focus();
}
body {
font-family: sans-serif;
}
input {
margin-bottom: 5px;
padding: 3px;
}
input:last-of-type {
margin-top: 30px;
}
div {
width: 300px;
padding: 5px;
border: solid 1px #000;
}
span {
font-weight: bold;
}
<input type="button" value="place caret" onclick="setCaret('editableDiv1', 'span1');">
<div id="editableDiv1" contenteditable="true" spellcheck="false">This one <span id="span1">is</span> working.</div>
<input type="button" value="place caret" onclick="setCaret('editableDiv2', 'span2');">
<div id="editableDiv2" contenteditable="true" spellcheck="false">This one <span id="span2"></span> is not.</div>
There's a nice trick with zero-width space that you may consider (look at at the code below) and CSS property white-space: pre
that allows spaces to be "visible" when focused.
function makeTextNode() {
return document.createTextNode('') // <-- there a zero-width space between quotes
}
function placeCaretInSpan() {
const range = document.createRange()
const editable = document.getElementById("editable")
const span = editable.querySelector("span")
if (span.childNodes.length === 0) {
span.appendChild(makeTextNode()) // <-- you have to have something in span in order to place caret inside
}
range.setStart(span.childNodes[0], 1) // <-- offset by 1 to be inside SPAN element and not before it
let selection = window.getSelection()
range.collapse(true)
selection.removeAllRanges()
selection.addRange(range)
editable.focus()
}
span {
font-weight: bold;
background: yellow;
}
#editable:focus {
white-space: pre;
}
<div contenteditable="true" id="editable">This should be <span></span> editable.</div>
<button onclick="placeCaretInSpan()">place caret</button>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With