为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕仙森 2021-12-02 19:50:35
我正在寻找一些解决方案来帮助从具有突出显示功能的 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 字符串进行变形?任何帮助或指导将不胜感激。
查看完整描述

3 回答

?
慕盖茨4494581

TA贡献1850条经验 获得超11个赞

我对WYSIWYG项目有完全相同的要求。

这个怎么运作 ?

  1. 通过将 HTML 元素映射到它们的纯文本版本,同时保持元素的原始大小。

  2. 然后对于每个匹配项,使用先前的映射和setSelectionRange选择回匹配项中包含的每个元素的相应部分

  3. 正如我在这个相关问题中所解释的那样,使用execCommand标记每个。然后您可以使用getSelection() .anchorNode.parentElement 来获得一个现成的包装选择,在此处与您的匹配相对应。

  4. 然后你可以应用任何你想要的风格!

使用这种方法,您将拥有一些重要的优势:

  • 支持多项选择(我不知道单个浏览器允许使用其本机搜索进行多项选择)

  • 支持在多个元素上进行搜索传播(这里也是,大多数使用的浏览器都没有此功能)

  • 支持正则表达式:)

  • 根据需要操作匹配项,对样式没有限制,您甚至可以修改内容(例如用于自动更正目的)

  • 如果您也在进行 WYSIWYG,则映射到源窗格。(查看如何在内容窗格中选择在下面的 ACE 编辑器中突出显示相应的源)

这是一个快速展示:

//img1.sycdn.imooc.com//61a8b3310001ce7f19010608.jpg

提示 我强烈建议您将所有 execCommand 调用放在requestAnimationFrame() 中,因为每次调用都会触发强制回流,这将使您的应用程序性能下降。通常,浏览器会触发 60 次回流/秒,但在这里,假设您有 300 个匹配项,您将添加 300 次额外的回流。通过在 requestAnimationFrame() 中调用 execCommand,您将告诉浏览器使用计划的回流而不是添加更多。


查看完整回答
反对 回复 2021-12-02
?
陪伴而非守候

TA贡献1757条经验 获得超8个赞

您可以使用的一件事是Range对象的getClientRects方法:https : //developer.mozilla.org/en-US/docs/Web/API/range/getClientRects


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


查找节点并不是那么简单(尤其是在结构复杂的情况下),但是您可以遍历所有文本节点以匹配要搜索的元素的 textContent 中的搜索索引。


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


完成此操作后,您可以根据这些结果创建一个Range,然后添加具有使用getClientRects获得的矩形坐标的元素。通过给出 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>


查看完整回答
反对 回复 2021-12-02
?
白板的微信

TA贡献1883条经验 获得超3个赞

因此,与其重新创建该 HTML 部分并进行一些重新渲染,不如在客户端进行。通过谷歌搜索我发现:


查看完整回答
反对 回复 2021-12-02
  • 3 回答
  • 0 关注
  • 153 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信