2

I have a JavaScript String which looks like this:

From Windows to Linux

With JavaScript, how can I highlight the words From and Linux, via a substring which looks like this:

From Linux

so the string looks like this in the end:

<mark>From</mark> Windows to <mark>Linux</mark>

This is my current implementation of the function to do that job:

  function highlightSearchTerm(string, substring) {
    const regex = new RegExp(`(${substring})`, 'ig');
    return string.replace(regex, '<mark>$1</mark>');
  }

I call it like this:

highlightSearchTerm("From Windows to Linux", "from linux")

It works well, the only thing that is missing is to make it work when the substring has words which are not directly next to each other.

These substrings for instance work:

  • from windows
  • From
  • to Linux

While these don't (Words are not directly next to each other in the main string):

  • Windows Linux
  • from To
  • Linux from
5
  • How exactly are you calling highlightSearchTerm ? Commented Feb 6, 2023 at 9:28
  • hi @Jhecht, I call it like this: highlightSearchTerm("From Windows to Linux", "from linux") for example. I use svelte, so in the end the returned value just gets displayed as HTML. Commented Feb 6, 2023 at 9:30
  • try using highlightSearchTerm("From windows to linux", "from|linux") and lmk if that works Commented Feb 6, 2023 at 9:30
  • Hi, that seems to work. Why does that work? Commented Feb 6, 2023 at 9:33
  • excellent! I'll write out an explanation in an answer, give me a second. Commented Feb 6, 2023 at 9:34

2 Answers 2

1

Short Answer

Call highlightSearchTerm() with a pipe(|) between the terms to achieve the desired output.

Longer Answer

The answer has to deal with how you are building your Regex.

The function

function highlightSearchTerm(string, substring) {
    const regex = new RegExp(`(${substring})`, 'ig');
    return string.replace(regex, '<mark>$1</mark>');
  }

It's important to understand what the corresponding RegExp object that is created reads like, and how it equates to a form that we would maybe write out directly.

First, if we call

// assume substring = 'hello';
new RegExp(`(${substring})`, 'ig');
// Equivalent: /(hello)/ig;

Notice that the grouped item is looking for the word hello.

Now, if we supply something that has multiple things we want in it, such as hi and you then if we supply them as a single string separated by space, e.g.

const substring = 'hey you';
new RegExp(`(${substring})`,'ig');
// Equivalent: /(hey you)/ig

This will not give us what we want because instead of looking for hey or you, the parser is now looking hey you as a phrase.

However, if we separate those things by a pipe (|) we get

// assume substring = 'hey|you';
new RegExp(`(${substring})`,'ig');
// Equivalent: /(hey|you)/ig

This now looks for either hey or you in the string. This is because the pipe character in RegEx is the OR.

If you'd like to expand the search for multiple phrases, you separate each specific one by a pipe, e.g.

new RegExp('(hey|you|that guy)', 'ig');

Will search for the words hey and you and the phrase (space included) that guy.

Sign up to request clarification or add additional context in comments.

9 Comments

Okay, thank you very much. That makes sense :) Do you think this solution is the shortest and most efficient it can get for highlighting a string with multiple words? Or is there a better solution?
i mean it is a solution. You'll learn in programming that there are multiple different ways you could solve a problem, and you tend to use the one that makes the most sense for the problem. For what you are trying to do, this is likely fine.
Are you familiar with a bit of Svelte by any chance. I am asking because I have another function which is called validateSearchTerm and returns true if the substring is valid. And I would like to know if my current solution is overcomplicated. Maybe I should open another question for it.
I do in fact know a bit of svelte -- built a small Tauri app in it.
Sorry, my real question is: Can I somehow make the same which I just did with RegExp, but instead of returning a formatted string return true or false, depending on if the substring is within the original string?
|
1

You can use the Pipe | just like @Jhecht explained above, alternatively you can split your substring and doing it this way:

function highlightSearchTerm(string, substring) {
  let arr = substring.split(' ');

  arr.forEach(el => {
      const regex = new RegExp(el, 'ig'),
            temp = el;
            
      el = el.replace(regex, `<mark>${el}</mark>`);
      string = string.replace(temp, el);
  })
  return string;
}

let text = document.querySelector('div').innerHTML;

document.querySelector('div').innerHTML = highlightSearchTerm(text, 'From Linux');
<div>From Windows to Linux</div>

this is how you return true or false if your text includes the substring

let text = document.querySelector('div').innerHTML;

function isIncludesSubstring(text, substring){
  let arr = substring.split(' '),
      arrResult = [];
  arr.forEach(el => {
    const regex = new RegExp(el, 'ig');
    arrResult.push(regex.test(text));
  });
  
  /* arrResult includes true or false based on whether substring single word
     is included in the text or not, the function will return true if all words are included
     else it will return false */
  return arrResult.includes(false) ? false : true;
}

console.log(isIncludesSubstring(text, 'From Windows Linux'))

console.log(isIncludesSubstring(text, 'To Windows from'))

console.log(isIncludesSubstring(text, 'From Test Linux'))
<div>From Windows to Linux</div>

4 Comments

secret third option: combine our two answers and just replace the space character with the pipe character before making the regex
@Jhecht exactly, that's a nice Idea
Yes, this is my final solution now: function validateSearchTerm(string, substring, highlight = false) { const regex = new RegExp((${substring.trim().replace(/\s\s+/g, " ").split(" ").join("|")}), 'ig'); if (highlight) { return string.replace(regex, '<mark>$1</mark>'); } return regex.test(string); }
you could simplify this code a bit by use the Array.prototype.every method instead of the forEach + includes. MDN Ref

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.