【问题标题】:How do I dynamically append a <span> tag into a specific location of the <p> tag?如何动态地将 <span> 标记附加到 <p> 标记的特定位置?
【发布时间】:2025-11-22 08:40:01
【问题描述】:

本质上,我正在尝试实现一个功能,即在选择时突出显示某些文本。这仅适用于 Google Chrome 浏览器。

例如: 选择前:

<html>
    <body>
        <p>sample text</p>
    </body>
</html>

从“示例文本”中选择“文本”后:

<html>
    <body>
        <p>sample <span class="state-highlighted">text</span> </p>
    </body>
</html>

JavaScript:

document.body.addEventListener("mousedown", (event) => {
  document.body.addEventListener("mouseup", (event) => {

    // Assume we have checked that mousedown position is different from mouseup position.
    // Not sure what to do after this.

  });
});

我可以从一个更简单的问题开始: 如何将 span 元素插入到 paragragh 元素中,比如说点击?

【问题讨论】:

    标签: javascript html dom google-chrome-extension


    【解决方案1】:

    在 mouseup 时,调用 window.getSelection() 以获取 Selection 对象。您可以检查它以在&lt;p&gt; 中找到所选内容的开始和结束文本。然后用&lt;span class="highlighted"&gt;替换&lt;p&gt;的HTML来包围那段文本:

    const p = document.body.querySelector('p');
    const origContent = p.textContent;
    p.addEventListener('mousedown', () => {
      p.textContent = origContent;
    });
    p.addEventListener('mouseup', (e) => {
      const selection = window.getSelection();
      if (!selection) {
        return;
      }
      const range = selection.getRangeAt(0);
      // If user starts highlighting on the right, and drags mouse to the left,
      // endOffset will be smaller than startOffset:
      const startIndex = Math.min(range.startOffset, range.endOffset);
      const { length } = String(selection);
      const endIndex = startIndex + length;
      p.textContent = p.textContent;
      p.innerHTML = (
        p.textContent.slice(0, startIndex) +
        '<span class="highlighted">' +
        selection +
        '</span>' +
        p.textContent.slice(endIndex)
      );
    });
    .highlighted {
      background-color: orange;
    }
    &lt;p&gt;sample text sample text sample text sample text sample text sample text sample text sample text sample text&lt;/p&gt;

    如果用户可能一次选择多个文本部分,并且您希望突出显示两个不连续的文本片段,您可以遍历从 0 到 selection.rangeCount 的范围并将原始上下文切片为相应地创建新的 HTML。

    【讨论】:

    • 谢谢,帮了大忙。
    【解决方案2】:

    这里有一个Range.surroundContents 方法可以派上用场,但是当只选择元素的一部分时它会抛出异常。
    因此,在您的情况下,最好将当前 Range 的 extract the content 附加到您的新节点中,然后 insert Range 所在的新节点:

    document.getElementById('target').addEventListener('mouseup', e => {
      const sel = getSelection();
      const range = sel.getRangeAt(0);
      const highlighter = document.createElement('span');
      highlighter.classList.add('highlight');
      highlighter.append(range.extractContents());
      range.insertNode(highlighter);
    })
    .highlight { color: red; }
    &lt;p id="target"&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.&lt;/p&gt;

    【讨论】:

      最近更新 更多