Reputation: 53
Following scenario.
I wrote a angular2 application with material2.
In my SideNav is a search input field. When a user types in it, he is redirected (via routing) to the search component, while the searched word is handed over as a routing parameter.
The search component shows all pages of the application, which contain the searched word (index in the background). Once the user clicks on the entry, he's redirected to this page, and the searched word is appended as a query parameter. I'm now trying to highlight all appearances of the searchword on the page, the user gets redirected to. At the moment i'm doing this:
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);
}
}
}
Now the issue is, that i get an error RangeError: Maximum call stack size exceeded
, which might be a hint, that the recursion level is way to deep.
I've already tried to use 3rd party libraries, bot non of them is really made to be used from angular2 and on top, the written code isn't that difficult... but its not working.
Any ideas how to stage beneath the maximum call stack size following the same or an similar approach?
tl;dr trying to highlight all appearances of searchTerm(which is passed over as a queryParam) on the page -> my approach (see code) is not working due to max call stack size.
Edit: Using rc4 atm, upgrading soon, but this shouldn't be an issue (i guess)
Upvotes: 1
Views: 797
Reputation: 53
Thanks to user3791775 I've come up with an solution.
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;
}
}
This can be used the following way
let ref = document.getElementById('my-highlicht-content');
ref.innerHtml = this.highlightWords(ref.innerHtml, this.searchTerm)
Thanks for helping!
Edit: Had another edgecase, which made it necessary to inspect the part after the keyword as well. Updated my example.
Upvotes: 2