MrRed
MrRed

Reputation: 697

Escape regex special characters in javascript but still retain string integrity for matching keywords

Im trying to highlight some text on my web page as the user types in the search box. My search algo is simply matching each space separated keyword. I thought this function was perfect until I started adding brackets to my search term. That kicks up SyntaxError: Invalid regular expression: ****: Unterminated group" as its getting interpreted by the regex. Ive tried to escape the brackets and other characters but then the highlighting isn't working.

https://codepen.io/anon/pen/YOaYEv

highlight (str) {
    // this line works but prevents highlighting multiple keywords that arent connected
    // var replacedStr = (this.search || '').replace(/[-[\]{}()*+!<=:?.\\^$|#\s,]/g, '\\$&')

    // you can comment this line and uncomment above to see a different but not perfect option
    var replacedStr = (this.search || '').replace(/ /g, '|')
    return str.replace(new RegExp(replacedStr, 'gi'), match => {
      return '<span class="font-weight-bold">' + match + '</span>'
    })
}

So i need to escape the brackets. Which ive tried in the commented line but then the function fails to highlight all keywords in the text :/

Any ideas?

Upvotes: 1

Views: 940

Answers (1)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627101

You need to escape each non-whitespace chunk you want to search for and highlight. Also, there is no need to use a callback inside replace to replace with a whole match, you may use a $& backreference.

See the updated JS:

vm = new Vue({
    el: "#app",
    data() {
        return {
            search: null,
            message: 'Search this text for matches (check the bracketed area too)'
        };
    },
    computed: {},
    methods: {
        highlight (str) {
          var replacedStr = (this.search || '').trim().split(/\s+/).map(x => x.replace(/[-[\]{}()*+!<=:?.\\^$|#\s,]/g, '\\$&')).join("|");
          return str.replace(new RegExp(replacedStr, 'gi'), 
            '<span class="teal--text text--darken-1 font-weight-bold">$&</span>');
        }
    }
});

Here:

  • .trim().split(/\s+/).map(x => x.replace(/[-[\]{}()*+!<=:?.\\^$|#\s,]/g, '\\$&')).join("|") - trims the input string with trim(), then splits out all non-whitespace chunks with .split(/\s+/), then these chunks are escaped with .map(x => x.replace(/[-[\]{}()*+!<=:?.\\^$|#\s,]/g, '\\$&')), and then .join("|") creates a regex pattern with a list of alternatives.
  • In the '<span class="teal--text text--darken-1 font-weight-bold">$&</span>' string replacement pattern, the $& parts stands for the whole match value.

Upvotes: 1

Related Questions