Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I position the caret inside a span element that has no text inside of it yet?

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>
like image 525
MikeMichaels Avatar asked Oct 15 '20 10:10

MikeMichaels


1 Answers

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>
like image 179
siaznik Avatar answered Sep 19 '22 17:09

siaznik