【问题标题】:How do I position the caret inside a span element that has no text inside of it yet?如何将插入符号放置在其中没有文本的 span 元素中?
【发布时间】:2020-10-15 10:58:12
【问题描述】:

this SO 发布之后,我可以将插入符号放在span 元素内,该元素位于div contenteditable="true" 内。

我可以通过id 定位我想要的任何span,同时还可以决定插入符号应该放在哪个字符之后。

但是如何将插入符号放在没有文本的 span 中?

只需按原样使用该函数,就会得到这个错误:TypeError: Range.setStart: Argument 1 is not an object.

另外,由于某种原因,当 span 有内容时,它在 Firefox 中运行良好。但不是在 Chrome 中,插入符号位于 span 之外。有什么办法也可以解决这个问题?

我对 jQuery 持开放态度,如果它能让事情变得更容易的话。

这是我的代码:

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>

【问题讨论】:

  • 在 Chrome 上,当我单击表示它正在工作并开始输入的那个上的“放置插入符号”按钮时,我输入的内容没有进入 span,它在 之前 跨度(例如,它不是粗体)。在 Firefox 上,我输入的内容在范围内。所以我不想这么说,但是即使span 有内容,代码也已经有问题了。
  • 火狐在这里。还没有在其他浏览器中测试过。在这里,它按预期工作。我单击该按钮,它将插入符号移动到跨度内,并在我键入时停留在那里。
  • 您可以通过检查是否存在子节点并添加空文本元素来纠正第一个问题:if (node.childNodes.length === 0) { var t = document.createTextNode(''); node.appendChild(t); } 虽然无法解决 Chrome 问题 :-(

标签: javascript html jquery contenteditable caret


【解决方案1】:

您可以考虑使用零宽度空间的一个很好的技巧(请看下面的代码)和 CSS 属性 white-space: pre,它允许空间在聚焦时“可见”。

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>

【讨论】:

  • 这在 Chrome 中有效,但零宽度的空间会弄乱箭头键的导航。还有其他选择吗?
【解决方案2】:

尝试使用 OR 运算符,如果 node.childNodes[0] 未定义,这将返回节点:

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

【讨论】:

  • 对不起,我的回答其实是一样的。我还没有看到其余的答案。我点击了小提琴并在那里工作......除此之外,那个问题的情况是,node.childNodes[0]undefined
  • 嗨@PeterDarmis,别担心;),同样的事情发生在我身上很多次了:)。
【解决方案3】:

问题出现在第二种情况,因为span2没有内容,node.childNodes[0]变成undefined。在下面的 sn-p 中使用了一个小的解决方法。您的代码中唯一更改的行是:

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

变成了

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

您可以查看下面的结果。

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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多