替换contenteditable div的html后,如何将插入符号放置在以前的位置? [英] How to place the caret where it previously was after replacing the html of a contenteditable div?

查看:116
本文介绍了替换contenteditable div的html后,如何将插入符号放置在以前的位置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a contenteditable div where I replace hashtags with clickable links when the user clicks on space or on enter. The user writes down:

我爱,但家里没有鱼|

I love but there is no fish at home|

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

He then realizes he made a mistake and then decides to go back and write

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

I love #sushi | but there is no fish at home

#sushi 

被替换为:

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

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

Notice that the | shows the position of where I want the caret to be when the user presses spacebar. My current "placeCaretAtEnd" function places the caret at the end of the div and NOT behind the link that I just replaced sushi with. Is there a way to alter my current function to place the caret behind the link I just replaced in the text on the position shown above, so that the user can continue typing carelessly? So in raw html:

我爱< a>#sushi< /a> |但家里没有鱼

I love < a> #sushi< /a> | but there is no fish at home

/**
* 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>

推荐答案

rangy.js具有

rangy.js has a text range module that lets you save selection as indices of the characters that are selected, and then restore it. Since your modifications do not alter innerText, this looks like a perfect fit:

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

手动实现此功能需要仔细遍历contenteditable内部的DOM并进行带有索引的仔细算术;只需几行代码就无法做到这一点.

Implementing this manually requires careful traversal of the DOM inside contenteditable and careful arithmetics with the indices; this can't be done with a few lines of code.

您的placeCaretAtEnd不可能将插入号放在插入的链接之后,因为它不知道"它是(可能是多个)链接中的哪个.您必须事先保存此信息.

Your placeCaretAtEnd can't possibly place the caret after the link you've inserted, since it doesn't "know" which (of the possibly multiple) link it is. You have to save this information beforehand.

/**
* 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>

这篇关于替换contenteditable div的html后,如何将插入符号放置在以前的位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆