Case insensitive string replacement in JavaScript?

57,265

Solution 1

You can use regular expressions if you prepare the search string. In PHP e.g. there is a function preg_quote, which replaces all regex-chars in a string with their escaped versions.

Here is such a function for javascript (source):

function preg_quote (str, delimiter) {
  //  discuss at: https://locutus.io/php/preg_quote/
  // original by: booeyOH
  // improved by: Ates Goral (https://magnetiq.com)
  // improved by: Kevin van Zonneveld (https://kvz.io)
  // improved by: Brett Zamir (https://brett-zamir.me)
  // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
  //   example 1: preg_quote("$40")
  //   returns 1: '\\$40'
  //   example 2: preg_quote("*RRRING* Hello?")
  //   returns 2: '\\*RRRING\\* Hello\\?'
  //   example 3: preg_quote("\\.+*?[^]$(){}=!<>|:")
  //   returns 3: '\\\\\\.\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:'

  return (str + '')
    .replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&')
}

So you could do the following:

function highlight(str, search) {
    return str.replace(new RegExp("(" + preg_quote(search) + ")", 'gi'), "<b>$1</b>");
}

Solution 2

function highlightWords( line, word )
{
     var regex = new RegExp( '(' + word + ')', 'gi' );
     return line.replace( regex, "<b>$1</b>" );
}

Solution 3

You can enhance the RegExp object with a function that does special character escaping for you:

RegExp.escape = function(str) 
{
  var specials = /[.*+?|()\[\]{}\\$^]/g; // .*+?|()[]{}\$^
  return str.replace(specials, "\\$&");
}

Then you would be able to use what the others suggested without any worries:

function highlightWordsNoCase(line, word)
{
  var regex = new RegExp("(" + RegExp.escape(word) + ")", "gi");
  return line.replace(regex, "<b>$1</b>");
}

Solution 4

Regular expressions are fine as long as keywords are really words, you can just use a RegExp constructor instead of a literal to create one from a variable:

var re= new RegExp('('+word+')', 'gi');
return s.replace(re, '<b>$1</b>');

The difficulty arises if ‘keywords’ can have punctuation in, as punctuation tends to have special meaning in regexps. Unfortunately unlike most other languages/libraries with regexp support, there is no standard function to escape punctation for regexps in JavaScript.

And you can't be totally sure exactly what characters need escaping because not every browser's implementation of regexp is guaranteed to be exactly the same. (In particular, newer browsers may add new functionality.) And backslash-escaping characters that are not special is not guaranteed to still work, although in practice it does.

So about the best you can do is one of:

  • attempting to catch each special character in common browser use today [add: see Sebastian's recipe]
  • backslash-escape all non-alphanumerics. care: \W will also match non-ASCII Unicode characters, which you don't really want.
  • just ensure that there are no non-alphanumerics in the keyword before searching

If you are using this to highlight words in HTML which already has markup in, though, you've got trouble. Your ‘word’ might appear in an element name or attribute value, in which case attempting to wrap a < b> around it will cause brokenness. In more complicated scenarios possibly even an HTML-injection to XSS security hole. If you have to cope with markup you will need a more complicated approach, splitting out ‘< ... >’ markup before attempting to process each stretch of text on its own.

Solution 5

What about something like this:

if(typeof String.prototype.highlight !== 'function') {
  String.prototype.highlight = function(match, spanClass) {
    var pattern = new RegExp( match, "gi" );
    replacement = "<span class='" + spanClass + "'>$&</span>";

    return this.replace(pattern, replacement);
  }
}

This could then be called like so:

var result = "The Quick Brown Fox Jumped Over The Lazy Brown Dog".highlight("brown","text-highlight");
Share:
57,265
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

    I need to highlight, case insensitively, given keywords in a JavaScript string.

    For example:

    • highlight("foobar Foo bar FOO", "foo") should return "<b>foo</b>bar <b>Foo</b> bar <b>FOO</b>"

    I need the code to work for any keyword, and therefore using a hardcoded regular expression like /foo/i is not a sufficient solution.

    What is the easiest way to do this?

    (This an instance of a more general problem detailed in the title, but I feel that it's best to tackle with a concrete, useful example.)

  • tvanfosson
    tvanfosson over 15 years
    Of course, you need to be careful with what you are replacing in and what you are searching on as @bobince notes. The above will work well for plain text and most searches if you are careful to quote your regex characters...
  • Herb Caudill
    Herb Caudill almost 15 years
    This will run into trouble if there are regex characters in the word being replaced. @okoman's solution gets around that.
  • Nathan Wall
    Nathan Wall over 11 years
    You shouldn't use a RegExp for this at all. You can pass the 'gi' flags as a third argument to replace. You don't have to use preg_quote or create a RegExp or anything of the sort.
  • Jerinaw
    Jerinaw over 11 years
    ? in javascript RegExp need to be escaped with double backslashes like \\?
  • Tomalak
    Tomalak over 11 years
    @Jerinaw What do you think my RegExp.escape function does?
  • Jerinaw
    Jerinaw about 11 years
    stackoverflow.com/questions/889957/… I've run into problems where the question mark needed to be escaped with double \ but I guess in [] you don't need to escape it.
  • Tomalak
    Tomalak about 11 years
    @Jerinaw In fact, you need to escape the question mark only once for the regex, so you end up with \? when you use a regex literal. But you need to escape the backslash itself for JS strings, so you end up with \\? when you build the regex from a string. And yes, in a character class the only character you really must escape is ].
  • YellowAfterlife
    YellowAfterlife about 10 years
    There's indeed a "flags" method in String.replace, but it is non-standard, thus unreliable. The best approach would be to make a "polyfill" method that selects an appropriate option.
  • Mike Gleason jr Couturier
    Mike Gleason jr Couturier over 8 years
    @YellowAfterlife The flags are passed to the regex here, not to the String.replace function so it is ok :)
  • Santosh
    Santosh about 8 years
    This doesn't work if the work is dot or period, how to make it work in case of dot or period, or multiple periods (ex: "..." )
  • tvanfosson
    tvanfosson about 8 years
    @helpme those are special characters in a regular expression. You need to quote them using a backslash first. Note that the backslash is also a quote character in a string so you need to use in two in the replacement string. word.replace(/\./g, '\\.')
  • Damon
    Damon over 4 years
    please don't encourage monkey patching in javascript