首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在新创建的跨度中搜索并放置一个子HTML文本,尽可能保留HTML结构

在新创建的跨度中搜索并放置一个子HTML文本,尽可能保留HTML结构
EN

Stack Overflow用户
提问于 2022-01-21 14:26:37
回答 1查看 72关注 0票数 -1

考虑下面的HTML示例,我需要突出显示一个搜索文本192.168.1.1。这只是一个例子,文本搜索和HTML结构完全是任意的,因为它将运行在最终用户的网站上和他们的输入。程序还将对插入的span执行额外的操作,不只是突出显示(例如,在悬停时显示工具提示,但只要插入了span,这就不是问题)。

编辑:我刚刚发现一个新的行将导致空间之间的textContent将不再正确。不过,为了更好的视觉效果,我会把它们保存在那里。您可以检查下面的片段以获得正确的HTML代码。

代码语言:javascript
复制
<div>
    <span class="foo-1">The computer address</span>
    <em>is</em>
    <span class="ip-1">192</span>.
    <span class="ip-2">168</span>.
    <span class="ip-3">1</span>.
    <span class="ip-4">1</span>
</div>

使用textContentinnerText,很容易找到与我需要的子文本最接近的元素(示例中的div有完整的192.168.1.1文本)。但是,下一步,我想在不破坏原始结构或其他HTML/CSS class或属性的情况下包装这些部件。理想情况下,我希望最终的HTML是:

代码语言:javascript
复制
<div>
    <span class="foo-1">The computer address</span>
    <em>is</em>
    <span class="my-highlight-span"><span class="ip-1">192</span>.
    <span class="ip-2">168</span>.
    <span class="ip-3">1</span>.
    <span class="ip-4">1</span></span>
</div>

使用深度优先搜索,我可以很容易地到达div,但我不知道如何从那里开始。还有一个棘手的案例:

代码语言:javascript
复制
<div>
    <span class="foo-1">The computer address</span>
    <em>is
    <span class="ip-1">192</span>.</em>
    <span class="ip-2">168</span>.
    <span class="ip-3">1</span>.
    <span class="ip-4">1</span>
</div>

我认为,如果有解决办法,这应该是实现这一目标的最简单方法:

代码语言:javascript
复制
<div>
    <span class="foo-1">The computer address</span>
    <em>is
    <span class="my-highlight-span"><span class="ip-1">192</span>.</span></em><span class="my-highlight-span">
    <span class="ip-2">168</span>.
    <span class="ip-3">1</span>.
    <span class="ip-4">1</span></span>
</div>

如果相关,代码将在Chrome扩展环境中执行,但我认为这应该是一个纯粹的浏览器Javascript问题。

这里有一个片段,您可以尝试一下:

代码语言:javascript
复制
function transform(el, text) {
  // Transform this Element to highlight text
}

const text = "192.168.1.1";
transform(document.querySelector("#case-1"), text);
transform(document.querySelector("#case-2"), text);
transform(document.querySelector("#case-simple"), text);
代码语言:javascript
复制
.my-highlight-span {
  background-color: cornflowerblue;
}
代码语言:javascript
复制
<p>Case 1:</p>
<div id="case-1">
  <span class="foo-1">The computer address</span>
  <em>is</em>
  <span class="ip-1">192</span>.<span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span>
</div>

<p>Case 2:</p>
<div id="case-2">
  <span class="foo-1">The computer address</span>
  <em>is
        <span class="ip-1">192</span>.</em><span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span>
</div>

<p>Simple case:</p>

<div id="case-simple">
  The computer address is 192.168.1.1
</div>

<hr />

<p>Desired Result:</p>

<div id="case-1-result">
  <span class="foo-1">The computer address</span>
  <em>is</em>
  <span class="my-highlight-span"><span class="ip-1">192</span>.<span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span></span>
</div>
<div id="case-2-result">
  <span class="foo-1">The computer address</span>
  <em>is
        <span class="my-highlight-span"><span class="ip-1">192</span>.</span></em><span class="my-highlight-span">
        <span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span></span>
</div>

<div id="case-simple-result">
  The computer address is <span class="my-highlight-span">192.168.1.1</span>
</div>

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-21 18:38:08

我终于得到了使用API接口的答案,这归功于令人敬畏的解释这里Range.surroundContents()的微妙解决方案。

  • 使用scanForText方法,我递归地扫描textContent of TEXT_NODEs,以确定搜索项的开始和结束。
  • 然后,我使用Range API来选择和提取内容,并在span包围它之后将其插入。

显然有一些限制,但这在大多数情况下是可行的。例如,在第2种情况下,我的代码将在<em>中生成一个额外的空<em>

代码语言:javascript
复制
function scanForText(el, expectingStart, expectingEnd, result) {
  if (el.nodeType === Node.TEXT_NODE) {
    const nodeContent = el.textContent;

    result[4] += nodeContent;
    const currText = result[4];

    if (expectingStart < currText.length && !result[0]) {
      result[0] = el;
      result[1] = expectingStart - (currText.length - nodeContent.length);
    }

    if (expectingEnd <= currText.length && !result[2]) {
      result[2] = el;
      result[3] = expectingEnd - (currText.length - nodeContent.length);

      return;
    }
  } else {
    for (let childEl of el.childNodes) {
      scanForText(childEl, expectingStart, expectingEnd, result);

      if (result[2]) { // When already found ending, return
        return;
      }
    }

  }
}

function transform(el, text) {
  const fullText = el.textContent;
  const startIndex = fullText.indexOf(text);
  const endIndex = startIndex + text.length;

  const scanState = [null, -1, null, -1, ""];
  scanForText(el, startIndex, endIndex, scanState);

  const [startNode, startRangeIndex, endNode, endRangeIndex] = scanState;
  if (!startNode || !endNode) {
    console.warn("This should not be happening");
    return;
  }

  const range = new Range();
  range.setStart(startNode, startRangeIndex);
  range.setEnd(endNode, endRangeIndex);

  const surrounding = document.createElement("span");
  surrounding.className = "my-highlight-span";

  // This can't be used when cutting one text boundary.
  // Alternative is offered at https://developer.mozilla.org/en-US/docs/Web/API/Range/surroundContents
  // range.surroundContents(surrounding)

  const extracted = range.extractContents();
  surrounding.appendChild(extracted);
  range.insertNode(surrounding);
}

const text = "192.168.1.1";
transform(document.querySelector("#case-1"), text);
transform(document.querySelector("#case-2"), text);
transform(document.querySelector("#case-simple"), text);
代码语言:javascript
复制
.my-highlight-span {
  background-color: cornflowerblue;
}
代码语言:javascript
复制
<p>Case 1:</p>
<div id="case-1">
  <span class="foo-1">The computer address</span>
  <em>is</em>
  <span class="ip-1">192</span>.<span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span>
</div>

<p>Case 2:</p>
<div id="case-2">
  <span class="foo-1">The computer address</span>
  <em>is
        <span class="ip-1">192</span>.</em><span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span>
</div>

<p>Simple case:</p>

<div id="case-simple">
  The computer address is 192.168.1.1
</div>

<hr />

<p>Desired Result:</p>

<div id="case-1-result">
  <span class="foo-1">The computer address</span>
  <em>is</em>
  <span class="my-highlight-span"><span class="ip-1">192</span>.<span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span></span>
</div>
<div id="case-2-result">
  <span class="foo-1">The computer address</span>
  <em>is
        <span class="my-highlight-span"><span class="ip-1">192</span>.</span></em><span class="my-highlight-span">
        <span class="ip-2">168</span>.<span class="ip-3">1</span>.<span class="ip-4">1</span></span>
</div>

<div id="case-simple-result">
  The computer address is <span class="my-highlight-span">192.168.1.1</span>
</div>

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70802881

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档