替换contenteditable div中选定的文本

66

我曾经搜寻过很多答案,但都失败了。

是否有一种跨浏览器的解决方案来替换contenteditable div中选择的文本?
我只想让用户可以突出显示一些文本,并用xxxxx替换所突出显示的文本。

3个回答

116

以下代码适用于所有主流浏览器:

function replaceSelectedText(replacementText) {
    var sel, range;
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
            range.insertNode(document.createTextNode(replacementText));
        }
    } else if (document.selection && document.selection.createRange) {
        range = document.selection.createRange();
        range.text = replacementText;
    }
}

3
这段话的意思是:在Chrome浏览器中,这种方法可以用于contenteditable元素,但不适用于输入框或文本区域。在非IE浏览器中,此代码仅适用于常规内容内的选择。 - Tim Down
1
@TimDown 我正在使用类似的 rangy 代码(替换iframe上的文本节点)。使用 sel.setSingleRange(range) 不会使 replacementText 被选中,这是正常的吗? - Sebastien Lorber
1
虽然这不是 OP 所要求的,但提醒其他人时,当使用表单字段时,getSelection 在 Firefox 中无法捕获所选文本:https://bugzilla.mozilla.org/show_bug.cgi?id=85686 - AXO
3
这个解决方案存在一个漏洞:尝试撤消已粘贴的文本。 - Alex Zinkevych
5
@TimDown 是的,在使用范围方法修改撤销堆栈时,它不会被处理。因此最好添加一些检查,如下所示:if (document.queryCommandSupported('insertText')) { document.execCommand( 'insertText', false, replacementText ); } else { range.deleteContents(); range.insertNode(document.createTextNode(replacementText)); } - Alex Zinkevych
显示剩余5条评论

3

我在Stack Overflow上发布了一个工作示例,演示如何在contentEditable div中添加表情符号。你可以使用这个方法来替换content editable div中需要的文本。

  function pasteHtmlAtCaret(html) {
        let sel, range;
        if (window.getSelection) {
          // IE9 and non-IE
          sel = window.getSelection();
          if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();

            // Range.createContextualFragment() would be useful here but is
            // non-standard and not supported in all browsers (IE9, for one)
            const el = document.createElement("div");
            el.innerHTML = html;
            let frag = document.createDocumentFragment(),
              node,
              lastNode;
            while ((node = el.firstChild)) {
              lastNode = frag.appendChild(node);
            }
            range.insertNode(frag);

            // Preserve the selection
            if (lastNode) {
              range = range.cloneRange();
              range.setStartAfter(lastNode);
              range.collapse(true);
              sel.removeAllRanges();
              sel.addRange(range);
            }
          }
        } else if (document.selection && document.selection.type != "Control") {
          // IE < 9
          document.selection.createRange().pasteHTML(html);
        }
      }

      function addToDiv(event) {
        const emoji = event.target.value;
        const chatBox = document.getElementById("chatbox");
        chatBox.focus();
        pasteHtmlAtCaret(`<b>${emoji}</b>`);
      }
      function generateEmojiIcon(emoji) {
        const input = document.createElement("input");
        input.type = "button";
        input.value = emoji;
        input.innerText = emoji;
        input.addEventListener("click", addToDiv);
        return input;
      }
      const emojis = [
        {
          emoji: "",
        },
        {
          emoji: "❤️",
        },
      ];
      emojis.forEach((emoji) => {
        document
          .getElementById("emojis")
          .appendChild(generateEmojiIcon(emoji.emoji));
      });
      #emojis span {
        cursor: pointer;
      }
      #chatbox {
        border: 1px solid;
      }
 <button
  type="button"
  onclick="document.getElementById('chatbox').focus(); 
  pasteHtmlAtCaret('<b>INSERTED</b>'); "
>
  Paste HTML
</button>
    <div id="emojis"></div>
    <div id="chatbox" contenteditable></div>


为什么点击粘贴HTML后所有文本都变成了粗体?如何让粘贴的文本只是粗体而其他文本保持正常? - SAF
@SAF,您可以在js中将<b>INSERTED</b><b>${emoji}</b>标签替换为任何您喜欢的内容。您可以使用innerText代替它,在片段代码中稍作更改即可... - SeyyedKhandon
关于后面的问题,我应该再深入探讨一下... - SeyyedKhandon

1
我需要替换文本节点,并在粘贴回来时保留HTML实体。 这是我解决问题的方法,不确定textnodes中的data是否更好使用textContent或其他内容。
let selection = window.getSelection()
  , range = selection.getRangeAt(0)
  , fragment = range.extractContents();

fragment.childNodes.forEach( e => e.data && (e.data = doSomething(e.data)) );
range.insertNode(fragment);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接