ContentEditable div - 更新内部 html 后设置光标位置

Posted

技术标签:

【中文标题】ContentEditable div - 更新内部 html 后设置光标位置【英文标题】:ContentEditable div - set cursor position after updating inner html 【发布时间】:2017-09-17 22:34:20 【问题描述】:

我有一个拼写检查解决方案,它使用内容可编辑的div 并在拼写错误的单词周围插入span 标记。每次更新div的内部html时,光标都会移动到div的开头。

我知道如果用户在句子末尾添加新单词,我可以将光标移动到 div 的末尾(代码如下)。

旧文本:这是一个拼写检查器|

新文本:这是一个拼写检查解决方案|

var range = document.createRange();
range.selectNodeContents(element[0]);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);

但是,如果用户在句子中间添加单词,我无法保留光标位置。

旧文本:这是一个拼写检查器

新文本:这是一个新的拼写检查器|

在上述情况下,光标移动到div 的末尾,而它应该在“new”之后。

如何保留光标位置?由于我正在更新 html 并添加节点,因此在更新之前保存范围并将其添加到选择对象中不起作用。

提前致谢。

【问题讨论】:

请检查this topic 是否回答了您的问题。谢谢。 谢谢,@Pyromonk - 不幸的是,这种方法对我不起作用,因为我需要更新 div 的 innerHtml,因此,任何标记节点都会丢失。 真可惜。我会努力寻找可能有帮助的东西。我想在更新 div 的 innerHTML 时使用单独的元素仅用于输入是不可能的? 不幸的是,是的。我尝试更新内部 html,同时仍保留标记节点,然后选择它,但我不断收到“给定范围不在文档中”错误。 @HAL9256 您可以分享任何代码 sn-p 或小提琴链接以获得更多说明吗?谢谢 【参考方案1】:

据我所知,改变div的内容总会有问题。

所以这是我提供的解决方案。请输入错误词,例如hello,dudeee

这也应该适用于 textarea。

解决方案详情:

使用具有相同文本内容的幽灵 div 为幽灵 div 使用透明颜色 为幽灵 div span 文本使用border-bottom 更改 zIndex 使其不会出现在前面

// some mock logic to identify spelling error
const errorWords = ["helloo", "dudeee"];

// Find words from string like '   Helloo   world .. '
// Perhaps you could find a better library you that does this logic.
const getWords = (data) =>
  console.log("Input: ", data);
  const allWords = data.split(/\b/);
  console.log("Output: ", allWords)
  return allWords;


// Simple mock logic to identify errors. Now works only for 
// two words [ 'helloo', 'dudeee']
const containsSpellingError = word => 
  const found = errorWords.indexOf(word) !== -1;
  console.log("spell check:", word, found);
  return found;


const processSpellCheck = text => 
  const allWords = getWords(text);
  console.log("Words in the string: ", allWords);
  const newContent = allWords.map((word, index) => 
    var text = word;
    if(containsSpellingError(word.toLowerCase())) 
      console.log("Error word found", word);
      text = $("<span />")
        .addClass("spell-error")
        .text(word);
    
    return text;
  );
  return newContent;


function initalizeSpellcheck(editorRef) 
  var editorSize = editorRef.getBoundingClientRect();
  
  var spellcheckContainer = $("<div />", )
    .addClass("spell-check")
    .prop("spellcheck", "false");
 
  var spellcheckSpan = $("<span />")
    .addClass("spell-check-text-content")
    .css(
      width: editorSize.width,
      height: editorSize.height,
      position: "absolute",
      zIndex: -1
    );
    
  var text = $(editorRef).text();
  
  var newContent = processSpellCheck(text);
  spellcheckSpan.append(newContent);
  
  spellcheckContainer.append(spellcheckSpan);
  spellcheckContainer.insertBefore(editorRef);
  
  $(editorRef).on("input.spellcheck", function(event) 
      var newText = $(event.target).text();
      var newContent = processSpellCheck(newText); 
      $(".spell-check .spell-check-text-content").text("");
      $(".spell-check .spell-check-text-content").append(newContent);
  );



$(document).ready(function() 
  var editor = document.querySelector("#editor");
  initalizeSpellcheck(editor);
);
#editor 
  border: 1px solid black;
  height: 200px;


.spell-check 
  color: transparent;


.spell-error 
  border-bottom: 3px solid orange;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<div id="editor" contenteditable="true" spellcheck="false">
dudeee
</div>

【讨论】:

查找词是基于空格字符的。【参考方案2】:

此答案可能适用于 SitePoint:

存储选择x,y:

cursorPos=document.selection.createRange().duplicate();
clickx = cursorPos.getBoundingClientRect().left;
clicky = cursorPos.getBoundingClientRect().top;

恢复选择:

cursorPos = document.body.createTextRange();
cursorPos.moveToPoint(clickx, clicky);
cursorPos.select();

SitePoint 文章:Saving/restoring caret position in a contentEditable div

25.10.2019 更新:

上面提到的解决方案不再起作用,因为使用了已弃用的函数。 Does chrome supports document.selection?

【讨论】:

以上是关于ContentEditable div - 更新内部 html 后设置光标位置的主要内容,如果未能解决你的问题,请参考以下文章

React:编辑contentEditable div时如何保持插入位置?

将插入符号位置设置为零在 chrome 中的 contenteditable 元素的节点内

contenteditable div 中的 HTML5 可拖动元素 - 第一次放置后停止工作 - 为啥?

如何在contenteditable div中删除非contenteditable div

需要在CKEditor中的p标签中添加contentEditable = false

为 contenteditable div 中的 div 启用调整大小