A one-line solution to highlighting search matches

I’ve recently been making some improvements to the search page of this blog, and wanted to implement a feature where the search term was highlighted everywhere it appeared in the results.

Screenshot of Search page with instances of "grid" serach query highlighted in the results

Although libraries for this exist, I wanted to try to implement it myself as it seemed like an interesting challenge. I initially spent hours coming up with weird and convoluted solutions, until I realised that the String.replace() method could solve this in one line!

Primer on String.replace()

The replace() method on Strings allows us to simultaneously search for matches of a substring within an original string, and replace that substring with something new. Here’s a basic example of how we can replace the substring “Hi” with "Hello", within the original string “Hi, how are you?”.

const substring = "Hi";
const original = "Hi, how are you?";

original.replace(substring, "Hello");

// Result => "Hello, how are you?"

Instead of searching for a basic substring, we can also use regular expressions, which can allow us to search for all instances of a substring.

const substring = new RegExp("Hi", "gi"); // "g" for global, "i" for case-insensitive
const original = "Hi, how are you? hi, I'm alright";

original.replace(substring, "Hello");

// Result => "Hello, how are you? Hello, I'm alright"

What I didn’t realise before was that the second argument of the replace() method could be a function. This function has one argument, which is the original matched string.

const substring = new RegExp("Hi", "gi");
const original = "Hi, how are you? hi, I'm alright";

original.replace(substring, (match) => {

  // Replace with the original text, but upper case
  return match.toUpperCase();
  
});

// Result => "HI, how are you? HI, I'm alright"

This allows us to have greater control over what we replace the substring with. And, it provides the exact use case that I needed which is to modify the original string in order to highlight it.

One-line match highlighting

Here’s the solution I came up with:

const term; // search query we want to highlight in results 
const results; // search results

results.replace(new RegExp(term, "gi"), (match) => `<mark>${match}</mark>`);

Let’s break this down. We have two variables; term, the search term that we want to highlight, and results, the string of text that the search term may be in.

Using the replace() method, I am performing a search for all instances (g) of the query in any case (i). With each match found, I am replacing the original matched text with the same text, but with the <mark> element surrounding it.

The <mark> HTML element is used to highlight text, so is the perfect element for this use case.

You can see how this fits within my greater search implementation on my GitHub repository for this theme.

A limitation

One limitation of this solution to note is that it only works if your search string is plain text. That is, we can't use this replace() if the original string (i.e. the results variable) includes HTML. This is because it will reaplce all instances of the search term, including in HTML attributes such as the href of links.

Take this example:

const term = "visibility"
const results = `
<h2>
    <a href="/the-visibility-property-isnt-just-about-visibility/">
        The visibility property isn’t just about visibility
    </a>
</h2>
`

results.replace(new RegExp(term, "gi"), (match) => `<mark>${match}</mark>`);
This will add the mark element to the link matches too :(

This will result in the link itself having the <mark> element around it:

<h2>
    <a href=\"/the-<mark>visibility</mark>-property-isnt-just-about-<mark>visibility</mark>/\">
        The <mark>visibility</mark> property isn’t just about <mark>visibility</mark>
    </a>
</h2>

This wasn't an issue for me because, due to the way my search script is implemented, I was easily able to separate the text I wanted to perform the highlight on. However, if you need to perform the highlight on actual HTML, you may need to use one of the highlighting libraries out there.

blog comments powered by Disqus