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

Following this SO post, I am able to place the caret inside a span element, which is inside a div conte

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>

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>

Share Improve this question edited Oct 15, 2020 at 11:13 MikeMichaels asked Oct 15, 2020 at 10:58 MikeMichaelsMikeMichaels 4942 gold badges6 silver badges27 bronze badges 4
  • On Chrome, when I click the "place caret" button on the one that says it's working and start typing, what I type doesn't go in the span, it goes before the span (e.g., it's not bold). On Firefox what I type is within the span. So I hate to say it, but the code is already problematic even when the span has content. – T.J. Crowder Commented Oct 15, 2020 at 11:04
  • Firefox here. Still haven't tested it in other browsers. Here, it works as intended. I click on the button, it moves the caret inside the span, and it stays there as I type. – MikeMichaels Commented Oct 15, 2020 at 11:07
  • See also stackoverflow./questions/53747581/… – John M Commented Oct 19, 2020 at 9:16
  • 2 You can correct the first issue by checking if there's a child node and adding an empty text element if not: if (node.childNodes.length === 0) { var t = document.createTextNode(''); node.appendChild(t); } Can't help with the Chrome issue though :-( – John M Commented Oct 19, 2020 at 9:18
Add a ment  | 

3 Answers 3

Reset to default 9 +100

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>

try with OR operator, this will return the node if node.childNodes[0] is undefined:

 range.setStart(node.childNodes[0] || node, 0);

The problem occurs in the second case because span2 has no content and node.childNodes[0] bees undefined. A small workaround is used in the below snippet. The only line changed in your code is this:

range.setStart(node.childNodes[0], 0);

and became

range.setStart((typeof node.childNodes[0] !== 'undefined' ? node.childNodes[0] : node), 0);

You can check the result below.

function setCaret(x, y) {
   let element = document.getElementById(x);   
   let range = document.createRange();  
   let node = document.getElementById(y);  
   range.setStart((typeof node.childNodes[0] !== 'undefined' ? node.childNodes[0] : node), 0);
   let sel = window.getSelection();
   range.collapse(true);
   sel.removeAllRanges();
   sel.addRange(range);
   element.focus();    
}
body {
  padding: 10px;
  font-family: sans-serif;
}

input {
  margin-bottom: 10px;
  padding: 5px;
}

input:last-of-type {
  margin-top: 50px;
}

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 working also.</div>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744311546a4567972.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信