Angular2 searchTerm 突出显示

Angular2 searchTerm highlighting

以下场景。

我用material2写了一个angular2应用。

在我的 SideNav 中有一个搜索输入字段。当用户在其中输入时,他将被重定向(通过路由)到搜索组件,同时将搜索到的词作为路由参数传递。

搜索组件显示应用程序的所有页面,其中包含搜索词(后台索引)。用户单击条目后,他将被重定向到该页面,搜索到的词将作为查询参数附加。我现在试图在页面上突出显示搜索词的所有外观,用户将被重定向到。目前我正在这样做:

subscription: ISubscription;
searchTerm: string;

constructor(private router: Router, private elementRef: ElementRef) {}

ngOnInit(): void {
  this.subscription = this.router.routerState.queryParams.subscribe(queryParams => {
    let searchTerm = queryParams['searchTerm'];
    if (searchTerm) {
      this.searchTerm = searchTerm;
    } else {
      this.searchTerm = null;
    }
  });
}

ngAfterContentInit(): void {
  if (this.searchTerm && isStaticDoc) {  
    let regExp = new RegExp(`(${this.searchTerm})`, 'i');
    this.highlightWords(this.elementRef.nativeElement, regExp);
  }
}

ngOnDestroy(): void {
  this.subscription.unsubscribe();
}

highlightWords(node, regExp: RegExp) {
  if (!node || ! regExp) {
    return;
  }
  if (node.nodeType === 3) {
  let regs = regExp.exec(node.nodeValue);
    if (regs) {
      let match = document.createElement('span');
      match.appendChild(document.createTextNode(regs[0]));
      match.classList.add('search-hl');

      let after = node.splitText(regs.index);
      after.nodeValue = after.nodeValue.substring(regs[0].length);
      node.parentNode.insertBefore(match, after);
    }
  } else if (node.hasChildNodes()) {
    for (let i = 0; i < node.childNodes.length; i++) {
      this.highlightWords(node.childNodes[i], regExp);
    }
  }
}

现在的问题是,我得到一个错误 RangeError: Maximum call stack size exceeded,这可能是一个提示,递归级别太深了。 我已经尝试过使用第 3 方库,它们中的任何一个都不是真的可以从 angular2 中使用,最重要的是,编写的代码并不难......但它不起作用。

对于如何按照相同或相似的方法在最大调用堆栈大小下暂存有任何想法吗?


tl;dr 试图在页面上突出显示 searchTerm(作为 queryParam 传递)的所有外观 -> 我的方法(参见代码)不是 由于最大调用堆栈大小而工作。


编辑: 使用 rc4 atm,即将升级,但这应该不是问题(我猜)

感谢 user3791775 我想出了一个解决方案。

highlightWords(html: string, searchTerm: string): string {

  let regExp = new RegExp(`(${searchTerm})`, 'i');
  let results = regExp.exec(html);
  
  if (results) {
    let before = html.substr(0, results.index);
    let after = html.substr(results.index + searchTerm.length);
    
    let indexOpenTag = before.lastIndexOf('<');
    let indexCloseTag = before.lastIndexOf('>');
    let indexOpenTagAfter = after.indexOf('<');
    let indexCloseTagAfter = after.indexOf('>');
    
    if (indexOpenTag <= indexCloseTag && indexOpenTagAfter <= indexCloseTagAfter) {
      return `${before}<span class="search-hl">${results[0]}</span>${this.highlightWords(after, searchTerm)}`;
    } else {
      return `${before}${results[0]}${this.highlightWords(after, searchTerm)}`;
    }
  } else {
    return html;
  }
}

可以通过以下方式使用

let ref = document.getElementById('my-highlicht-content');
ref.innerHtml = this.highlightWords(ref.innerHtml, this.searchTerm)

感谢您的帮助!


编辑: 有另一个 edgecase,这也使得有必要检查关键字后面的部分。更新了我的示例。