qbert
qbert

Reputation: 129

Angular Highlight Directive to Highlight Multiple Search Queries

Variations of this have been asked before, but I can't find anything specific to what I'm looking for. I'm terrible with Regex and I figure my issue mostly lies there. I have a simple-ish Angular Directive (that I snagged online, never used Directives before) that highlights whatever string I searched for in my results from a MySQL stored procedure on the back-end. The problem is, it will only work if I search for one word.

For example if I search for "network" it will wrap every instance of the string "network" in a span with a class anywhere I point the directive to. However, if I search "network drive" it won't highlight anything because it's looking for that string specifically.

Here's the part of the directive that has this functionality:

  getFormattedText() {
    const re = new RegExp(`(${this.searchedWord})`, 'gi');
    return this.content.replace(re, `<span class="${this.classToApply}">$1</span>`);
  }

I was trying to make searchedWord an array of strings or a string and then apply the span element to each matched string but I ended up making things worse.

Any nudge in the right direction is much appreciated.

Thanks!

Upvotes: 2

Views: 949

Answers (1)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626691

I suggest:

  • Keeping the search words in some kind of an associative array
  • Create a dynamic regex pattern based on | alternation operator to match all searched words, while placing the longer terms first, and shorter ones at the end (that is, the searched word list must be sorted by length in the descending order)
  • Do not forget to escape the searched words before putting them into the regex pattern
  • When the searched word is matched in the text, use an arrow function (or a callback method) and get the class by the searched word from the initial array.

Here is a code snippet that you may adjust for use within your code:

const content = "When connected to Network, you can save the data on the network dRive.";
const items = [
    {'searchedWord': 'network', 'classToApply':'blue'},
    {'searchedWord': 'network drive', 'classToApply':'red'}
];

const arr = items.map(x => x.searchedWord.toLowerCase());
arr.sort((x,y) => y.length - x.length);
const re = new RegExp( arr.map(x => x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')).join("|"), "gi");

function getClassBySW(sw) {
    return items.find(x => x.searchedWord == sw.toLowerCase()).classToApply;
}
function getFormattedText() {
    return content.replace(re, (m) => `<span class="${getClassBySW(m)}">${m}</span>`);
}

const highlightedContent = getFormattedText();
document.body.innerHTML = highlightedContent;
console.log( highlightedContent )
.red {
  color: #FF0000;
}

.blue {
  color: #0000FF;
}

Upvotes: 1

Related Questions