Javascript 如何将光标移动到contenteditable实体的末尾

Javascript 如何将光标移动到contenteditable实体的末尾,javascript,contenteditable,cursor-position,Javascript,Contenteditable,Cursor Position,我需要将插入符号移动到Gmail notes小部件上的contenteditable节点的末尾 我阅读了StackOverflow上的线程,但这些解决方案基于使用输入,它们不适用于contenteditable元素。Geowa4的解决方案适用于textarea,但不适用于contenteditable元素 此解决方案用于将插入符号移动到contenteditable元素的末尾。它应该适用于所有支持contenteditable的浏览器 function setEndOfContenteditab

我需要将插入符号移动到Gmail notes小部件上的
contenteditable
节点的末尾


我阅读了StackOverflow上的线程,但这些解决方案基于使用输入,它们不适用于
contenteditable
元素。

Geowa4的解决方案适用于textarea,但不适用于contenteditable元素

此解决方案用于将插入符号移动到contenteditable元素的末尾。它应该适用于所有支持contenteditable的浏览器

function setEndOfContenteditable(contentEditableElement)
{
    var range,selection;
    if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
    {
        range = document.createRange();//Create a range (a range is a like the selection but invisible)
        range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
        range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
        selection = window.getSelection();//get the selection object (allows you to change selection)
        selection.removeAllRanges();//remove any selections already made
        selection.addRange(range);//make the range you have just created the visible selection
    }
    else if(document.selection)//IE 8 and lower
    { 
        range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
        range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
        range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
        range.select();//Select the range (make it the visible selection
    }
}
可由类似以下代码使用:

elem = document.getElementById('txt1');//This is the element that you want to move the caret to the end of
setEndOfContenteditable(elem);

还有一个问题

如果
contenteditable
div不包含其他多行元素,则的解决方案有效

例如,如果一个div包含其他div,而这些其他div中包含其他内容,则可能会出现一些问题

为了解决这些问题,我安排了以下解决方案,这是对的解决方案的改进:

用法:

var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);
这样,光标肯定会定位在最后一个元素的末尾,最终嵌套

<强> >编辑1 < /强>:为了更通用,while语句还应考虑所有不能包含文本的其他标签。这些元素被命名为void元素,在中有一些关于如何测试元素是否为void的方法。因此,假设存在一个名为

cancontentantext
的函数,如果参数不是void元素,则返回
true
,下面的代码行:

contentEditableElement.lastChild.tagName.toLowerCase() != 'br'
应替换为:

canContainText(getLastChildElement(contentEditableElement))

编辑#2:上述代码已完全更新,描述和讨论了每项更改

可以在整个范围内将光标设置到末尾:

setCaretToEnd(target/*: HTMLDivElement*/) {
  const range = document.createRange();
  const sel = window.getSelection();
  range.selectNodeContents(target);
  range.collapse(false);
  sel.removeAllRanges();
  sel.addRange(range);
  target.focus();
  range.detach(); // optimization

  // set scroll to the end if multiline
  target.scrollTop = target.scrollHeight; 
}

我在尝试使元素可编辑时遇到了类似的问题。这在Chrome和FireFox中是可能的,但在FireFox中,插入符号要么出现在输入的开头,要么出现在输入结束后的一个空格。我认为,试图编辑内容的最终用户会感到非常困惑


我尝试了几件事,没有找到解决办法。对我来说唯一有效的方法就是在我的电脑中输入一个简单的旧文本来“绕过这个问题”。现在它起作用了。“内容可编辑”似乎仍然是最前沿的技术,它可能会也可能不会像您希望的那样工作,这取决于上下文

将光标移动到可编辑范围的末尾以响应焦点事件:

  moveCursorToEnd(el){
    if(el.innerText && document.createRange)
    {
      window.setTimeout(() =>
        {
          let selection = document.getSelection();
          let range = document.createRange();

          range.setStart(el.childNodes[0],el.innerText.length);
          range.collapse(true);
          selection.removeAllRanges();
          selection.addRange(range);
        }
      ,1);
    }
  }
并在事件处理程序中调用它(在此处作出反应):


如果你不关心旧的浏览器,这一个为我做了诀窍

// [optional] make sure focus is on the element
yourContentEditableElement.focus();
// select all the content in the element
document.execCommand('selectAll', false, null);
// collapse selection to the end
document.getSelection().collapseToEnd();

contenteditable
的问题在您开始键入时得到解决。一种解决方法是在div元素和该函数上触发焦点事件,清除并重新填充div元素中已有的内容。这样就解决了问题,最后您可以使用范围和选择将光标放置在末尾。为我工作

  moveCursorToEnd(e : any) {
    let placeholderText = e.target.innerText;
    e.target.innerText = '';
    e.target.innerText = placeholderText;

    if(e.target.innerText && document.createRange)
    {
      let range = document.createRange();
      let selection = window.getSelection();
      range.selectNodeContents(e.target);
      range.setStart(e.target.firstChild,e.target.innerText.length);
      range.setEnd(e.target.firstChild,e.target.innerText.length);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  }
在HTML代码中:

<div contentEditable="true" (focus)="moveCursorToEnd($event)"></div>



geowa4的解决方案适用于chrome中的textarea,但不适用于任何浏览器中的contenteditable元素。我的适用于内容可编辑元素,但不适用于文本区域。这是这个问题的正确答案,非常好,谢谢Nico。Nico的
selectNodeContents
部分在Chrome和FF中都给了我错误(没有测试其他浏览器),直到我发现我显然需要添加
。get(0)
添加到我正在为函数提供信息的元素。我想这与我使用jQuery而不是纯JS有关?这是我在@jwarzech中学到的。谢谢大家!是的,函数需要一个DOM元素,而不是jQuery对象
.get(0)
检索jQuery内部存储的dom元素。您还可以附加
[0]
,这相当于此上下文中的
.get(0)
。@Nico Burns:我尝试了您的方法,但在FireFox上不起作用。有趣的是,我希望浏览器能自动处理此情况(这并不奇怪,浏览器似乎从未对contenteditable做过直观的事情)。您是否有一个HTML示例,其中您的解决方案有效,而我的解决方案无效?在我的代码中还有一个错误。我已修复了它。现在,您可以验证我的代码是否有效,而您的代码使用您的函数时没有收到错误,控制台显示
uncaughttypeerror:cannotread属性'nodeType'为null
,这是来自getLastChildElement函数正在被调用。你知道是什么导致了这个问题吗?@VitoGentile这是一个有点旧的答案,但我想注意的是,你的解决方案只处理块元素,如果里面有内联元素,那么光标将定位在内联元素之后(如span,em…)使用一个简单的解决方案是将内联元素作为空标签,并将它们添加到VooNoDobug中,这样它们就会被跳过。使用上面的代码,它可以起到作用。但是,我希望能够在任意内容可编辑的div中移动光标并继续从那个点打字。例如,用户已经识别出一个键入,例如……我可以把上面的代码修改成这样吗?@Zabs这相当简单:不要调用
setcarettoned()
每次-仅在需要时调用它:例如,在复制粘贴后,或在限制消息长度后。这对我很有效。在用户选择标记后,我将contenteditable div中的光标移动到末尾。这是在chrome扩展的背景脚本中唯一对我有效的方法。这很好。在chrome 71上测试.0.3578.98和Android 5.1上的WebView。
document.execCommand
现在已经过时了。2020年,这在chrome版本83.0.4103.116(官方版本)(64位)中仍然有效。这是赢家!
  moveCursorToEnd(e : any) {
    let placeholderText = e.target.innerText;
    e.target.innerText = '';
    e.target.innerText = placeholderText;

    if(e.target.innerText && document.createRange)
    {
      let range = document.createRange();
      let selection = window.getSelection();
      range.selectNodeContents(e.target);
      range.setStart(e.target.firstChild,e.target.innerText.length);
      range.setEnd(e.target.firstChild,e.target.innerText.length);
      selection.removeAllRanges();
      selection.addRange(range);
    }
  }
<div contentEditable="true" (focus)="moveCursorToEnd($event)"></div>