替换contenteditable div的html后,如何将插入符号放置在以前的位置?

时间:2019-08-16 15:23:51

标签: javascript jquery html replace caret

我有一个contenteditable div,当用户单击空格或Enter时,我用可单击的链接替换主题标签。用户写下:

  

我爱,但家里没有鱼|

然后他意识到自己犯了一个错误,然后决定回去写信

  

我爱#寿司|但家里没有鱼

#sushi 

被替换为:

 <a href="https://google.com/sushi>#sushi</a>

请注意|显示当用户按下空格键时我想要插入符号的位置。我当前的“ placeCaretAtEnd”函数将插入符号放置在div的末尾,而不是在我刚刚替换了 sushi 的链接之后。有没有一种方法可以更改我的当前功能,以将插入符号放在我在上面显示的位置上的文本中刚刚替换的链接的后面,以便用户可以继续随意输入?因此,在原始html中:

  

我爱 #sushi |但家里没有鱼

/**
* Trigger when someone releases a key on the field where you can post remarks, posts or reactions
*/
$(document).on("keyup", ".post-input-field", function (event) {

       // if the user has pressed the spacebar (32) or the enter key (13)
       if (event.keyCode === 32 || event.keyCode === 13) {
          let html = $(this).html();
          html = html.replace(/(^|\s)(#\w+)/g, " <a href=#>$2</a>").replace("<br>", ""); 
          $(this).html(html);
          placeCaretAtEnd($(this)[0]);
       }
  });

 /**
 * Place the caret at the end of the textfield
 * @param {object} el - the DOM element where the caret should be placed 
 */
function placeCaretAtEnd(el) {

    el.focus();
    if (typeof window.getSelection != "undefined"
        && typeof document.createRange != "undefined") {
        var range = document.createRange();
        range.selectNodeContents(el);
        range.collapse(false);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (typeof document.body.createTextRange != "undefined"){
        var textRange = document.body.createTextRange();
        textRange.moveToElementText(el);
        textRange.collapse(false);
        textRange.select();
    }
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div contenteditable="true" class="post-input-field">
I love (replace this with #sushi and type space) but there is no fish at home
</div>

1 个答案:

答案 0 :(得分:2)

rangy.js具有text range module,可让您将选择内容另存为所选字符的索引,然后将其还原。由于您所做的修改不会更改innerText,因此看起来很合适:

      var sel = rangy.getSelection();
      var savedSel = sel.saveCharacterRanges(this);
      $(this).html(html);
      sel.restoreCharacterRanges(this, savedSel);

要手动实现此功能,需要仔细遍历contenteditable内部的DOM以及使用索引进行仔细的算术;只需几行代码就无法完成。

您的placeCaretAtEnd可能无法将插入符号放在您插入的链接之后,因为它不“知道”它(可能是多个)链接。您必须事先保存此信息。

/**
* Trigger when someone releases a key on the field where you can post remarks, posts or reactions
*/
$(document).on("keyup", ".post-input-field", function (event) {

       // if the user has pressed the spacebar (32) or the enter key (13)
       if (event.keyCode === 32 || event.keyCode === 13) {
          let html = $(this).html();
          html = html.replace(/(^|\s)(#\w+)/g, " <a href=#>$2</a>").replace("<br>", ""); 
          var sel = rangy.getSelection();
          var savedSel = sel.saveCharacterRanges(this);
          $(this).html(html);
          sel.restoreCharacterRanges(this, savedSel);
       }
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-core.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-textrange.js"></script>
<div contenteditable="true" class="post-input-field">
I love (replace this with #sushi and type space) but there is no fish at home
</div>