首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在不破坏HTML结构的情况下替换Javascript中的重叠字符串

如何在不破坏HTML结构的情况下替换Javascript中的重叠字符串
EN

Stack Overflow用户
提问于 2022-02-10 23:18:15
回答 1查看 85关注 0票数 1

我有一个字符串和一个N项数组:

代码语言:javascript
复制
<div>
sometimes the fox can fly really high
</div>
代码语言:javascript
复制
const arr = ['the fox can', 'fox can fly', 'really high']`

我想找到一种方法,用HTML替换div中的文本,在不破坏HTML的情况下突出显示数组中的那些特定短语。这可能会有问题,因为我不能执行简单的循环和替换,因为其他单词在替换后将不匹配,因为突出显示范围会破坏类似于indexOfinnerHTML上的includes之类的内容,当然,我可以使用innerText读取文本,但它没有提供任何使其生效的内容,因此我可以在不破坏原始HTML高亮显示的情况下添加"next“跨度。理想情况下,我还希望能够根据我使用的单词来定制类名,而不仅仅是一个普通的突出显示类。

结果应该是

代码语言:javascript
复制
<div>
sometimes
<span class="highlight-1">the <span class="highlight-2">fox can</span></span><span class="highlight-2"> fly</span> <span class="highlight-3">really high</span>
</div>

我试过什么?

我真的考虑过这个问题,在网上找不到任何资源来帮助解决这个场景和主要问题,目前,我还需要额外的价值,比如charStartcharEnd,我不喜欢这个解决方案,因为它依赖于使用DOMParser() API,而且它感觉非常笨拙,显然没有性能,我只是得到了一种“氛围”,我不应该这样做,而且肯定会有更好的解决方案,我正在寻求关于如何完成这一挑战的想法。

代码语言:javascript
复制
      let text = `<p id="content">${content}</p>`
      let parser = new DOMParser().parseFromString(text, "text/html")

      for (const str of strings) {
        const content = parser.querySelector("#content")
        let descLength = 0

        for (const node of content.childNodes) {
          const text = node.textContent

          let newTextContent = ""

          for (const letter in text) {
            let newText = text[letter]
            if (descLength === str.charStart) {
              newText = `<em class="highlight ${str.type}" data-id="${str.id}">${text[letter]}`
            } else if (descLength === str.charEnd) {
              newText = `${text[letter]}</em>`
            }

            newTextContent += newText
            descLength++
          }

          node.textContent = newTextContent
        }

        // Replace the &lt; with `<` and replace &gt; with `>` to construct the HTML as text inside lastHtml
        const lastHtml = parser
          .querySelector("#content")
          .outerHTML.split("&lt;")
          .join("<")
          .split("&gt;")
          .join(">")

        // Redefine the parser variable with the updated HTML and let it automatically correct the element structure
        parser = new DOMParser().parseFromString(lastHtml, "text/html")

        /**
         * Replace the placeholder `<em>` element with the span elements to prevent future issues. We need the HTML
         * to be invalid for it to be correctly fixed by DOMParser, otherwise the HTML would be valid and *not* render how we'd like it to
         * Invalid => `<span>test <em>title </span>here</em>
         * Invalid (converted) => `<span>test <em>title </em></span><em>here</em>
         * Valid => `<span>test <span>title </span>here</span>
         */

        parser.querySelector("#content").innerHTML = parser
          .querySelector("#content")
          .innerHTML.replaceAll("<em ", "<span ")
          .replaceAll("</em>", "</span>")
      }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-11 01:15:13

我会重温你的例子,只是想给出一个想法。下面的代码不是一个干净的函数,请根据您的需要进行调整。

代码语言:javascript
复制
const str = "sometimes the fox can fly really high";
const arr = ['the fox can', 'fox can fly', 'really high'];

// First, find the indices of start and end positions for your substrings.
// Call them event points and push them to an array.
eventPoints = [];
arr.forEach((a, i) => {
  let index = strLower.indexOf(a)
  while (index !== -1) {
    let tagClass = `highlight-${i}`
    eventPoints.push({ pos: index, className: tagClass, eventType: "start" })
    eventPoints.push({ pos: index + a.length, className: tagClass, eventType: "end" })
    index = strLower.indexOf(a, index + 1)
  }
  return
});

// Sort the event points based on the position properties
eventPoints.sort((a, b) => a.pos < b.pos ? -1 : a.pos > b.pos ? 1 : 0);

// Init the final string, a stack and an index to keep track of the current position on the full string
let result = "";
let stack = [];
let index = 0;
// Loop over eventPoints
eventPoints.forEach(e => {
    // concat the substring between index and e.pos to the result
    result += str.substring(index, e.pos);
    if (e.eventType === "start") {
        // when there is a start event, open a span
        result += `<span class="${e.className}">`;
        // keep track of which span is opened
        stack.push(e.className);
    }
    else {
        // when there is an end event, close tags opened after this one, keep track of them, reopen them afterwards
        let tmpStack = [];
        while (stack.length > 0) {
            result += "</span>";
            let top = stack.pop();
            if (top === e.className) {
                break;
            }
            tmpStack.push(top);
        }
        while (tmpStack.length > 0) {
            let tmp = tmpStack.pop();
            result += `<span class="${tmp}">`;
            stack.push(tmp);
        }
    }
    index = e.pos;
});

result += str.substring(index, str.length)


console.log(result);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71073444

复制
相关文章

相似问题

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