如何在不中断的情况下突出显示 html 内容字符串中的搜索文本

2023-12-05

我正在寻找一些解决方案,帮助从带有突出显示功能的 html 字符串中搜索术语。我可以通过从字符串中删除 html 内容来做到这一点。但问题是我将无法看到它突出显示的原始内容。我确实有以下功能,可以在没有 html 标记的情况下搜索和突出显示字符串。

private static updateFilterHTMLValue(value: string, filterText: string): string
{
    if (value == null) {
        return value;
    }

    let filterIndex: number = value.toLowerCase().indexOf(filterText);
    if (filterIndex < 0) {
        return null;
    } 
    return value.substr(0, filterIndex) 
        + "<span class='search-highlight'>" 
        + value.substr(filterIndex, filterText.length) 
        + "</span>" 
        +   value.substr(filterIndex + filterText.length, value.length - (filterIndex + filterText.length));
}

因此,为了管理使用 html 对字符串的搜索,我创建了可以使用 html 搜索字符串的新函数。 (我在搜索正确的字符串匹配之前删除了 html 部分)

private static test(value: string, filterText: string): string {
    if (value == null) {
        return value;
    }
    // Check for raw data without html
    let valueWithoutHtml = TextFilterUtils.removeTextHtmlTags(value);
    let filterIndex: number = valueWithoutHtml.toLowerCase().indexOf(filterText);
    if (filterIndex < 0) {
        return null;
    } else {
        // TODO: 
        // just need to figure how we can highlight properly 
        // real issue is to identify proper index for warping   <span class='search-highlight'> </span> 
        return "";
    }
}

我们如何对 html 字符串进行扭曲?任何帮助或指导将不胜感激。


你可以使用的一件事是获取客户端矩形的方法Range目的:https://developer.mozilla.org/en-US/docs/Web/API/range/getClientRects

这允许您添加带有搜索坐标的 div,从而无需操作 DOM 即可突出显示文本。

查找节点并不是那么简单(特别是如果您有复杂的结构),但您可以迭代所有文本节点以匹配要搜索的元素的 textContent 中的搜索索引。

因此,首先将搜索结果与 DOM 进行匹配。在示例中,我使用递归生成器,但任何递归循环都可以。基本上,您需要做的是遍历每个文本节点以匹配搜索的索引。因此,您将遍历每个后代节点并计算文本长度,以便将搜索与节点相匹配。

完成此操作后,您可以创建一个Range从这些结果中,然后使用获得的矩形的坐标添加元素获取客户端矩形。通过给定 z-index 负值和绝对位置,它们将出现在文本下方的正确位置。因此,您将获得突出显示效果,但不会触及您正在搜索的 HTML。 像这样:

document.querySelector('#a').onclick = (e) => {

  let topParent = document.querySelector('#b');
  let s, range;
  let strToSearch = document.querySelector('#search').value
  let re = RegExp(strToSearch, 'g')

  removeHighlight()
  s = window.getSelection();
  s.removeAllRanges()
  // to handle multiple result you need to go through all matches
  while (match = re.exec(topParent.textContent)) {

    let it = iterateNode(topParent);
    let currentIndex = 0;
    // the result is the text node, so you can iterate and compare the index you are searching to all text nodes length
    let result = it.next();

    while (!result.done) {
      if (match.index >= currentIndex && match.index < currentIndex + result.value.length) {
        // when we have the correct node and index we add a range
        range = new Range();
        range.setStart(result.value, match.index - currentIndex)

      }
      if (match.index + strToSearch.length >= currentIndex && match.index + strToSearch.length < currentIndex + result.value.length) {
        // when we find the end node, we can set the range end
        range.setEnd(result.value, match.index + strToSearch.length - currentIndex)
        s.addRange(range)

        // this is where we add the divs based on the client rects of the range
        addHighlightDiv(range.getClientRects())


      }
      currentIndex += result.value.length;
      result = it.next();
    }
  }
  s.removeAllRanges()

}


function* iterateNode(topNode) {
  // this iterate through all descendants of the topnode
  let childNodes = topNode.childNodes;
  for (let i = 0; i < childNodes.length; i++) {
    let node = childNodes[i]
    if (node.nodeType === 3) {
      yield node;
    } else {
      yield* iterateNode(node);
    }
  }

}

function addHighlightDiv(rects) {
  for (let i = 0; i < rects.length; i++) {

    let rect = rects[i];
    let highlightRect = document.createElement('DIV')
    document.body.appendChild(highlightRect)
    highlightRect.classList.add('hl')
    highlightRect.style.top = rect.y + window.scrollY + 'px'
    highlightRect.style.left = rect.x + 'px'
    highlightRect.style.height = rect.height + 'px'
    highlightRect.style.width = rect.width + 'px'

  }

}

function removeHighlight() {
  let highlights = document.querySelectorAll('.hl');
  for (let i = 0; i < highlights.length; i++) {
    highlights[i].remove();
  }
}
.hl {
  background-color: red;
  position: absolute;
  z-index: -1;
}
<input type="text" id="search" /><button id="a">search</button>
<div id="b">
  <h1>Lorem ipsum dolor sit amet</h1>, consectetur
  <h2>adipiscing elit, sed do</h2> eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <strong>exercitation <span>ullamco laboris</span> nisi ut aliquip ex ea commodo</strong> consequat. Duis aute irure dolor
  in reprehenderit in voluptate velit <em>esse cillum dolore eu fugiat nulla pariatur.</em> Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在不中断的情况下突出显示 html 内容字符串中的搜索文本 的相关文章

随机推荐