Invalid string replacement in JavaScript?

I need to highlight, case insensitive, 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 hard-coded regular expression like /foo/i is not a sufficient solution.

What is the easiest way to do this?

(This is an example of a more general problem described in the title, but I believe that it is best to solve a specific, useful example.)

+50
javascript string replace case-insensitive
Nov 11 '08 at 12:49
source share
7 answers

When preparing a search string, you can use regular expressions. In PHP, for example. There is a preg_quote function that replaces all regular expression characters in a string with their escaped versions.

Here is a function for javascript:

 function preg_quote( str ) { // http://kevin.vanzonneveld.net // + original by: booeyOH // + improved by: Ates Goral (http://magnetiq.com) // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // + bugfixed by: Onno Marsman // * 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(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); } 

(taken from http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_preg_quote/ )

So you can do the following:

 function highlight( data, search ) { return data.replace( new RegExp( "(" + preg_quote( search ) + ")" , 'gi' ), "<b>$1</b>" ); } 
+67
Nov 11 '08 at
source share
 function highlightWords( line, word ) { var regex = new RegExp( '(' + word + ')', 'gi' ); return line.replace( regex, "<b>$1</b>" ); } 
+63
Nov 11 '08 at 13:06
source share

You can enhance the RegExp object with a function that performs special characters for you:

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

Then you can use what others have suggested without any worries:

 function highlightWordsNoCase(line, word) { var regex = new RegExp("(" + RegExp.escape(word) + ")", "gi"); return line.replace(regex, "<b>$1</b>"); } 
+13
Nov 11 '08 at 14:07
source share

Regular expressions are beautiful, as long as keywords are really words, you can simply use the RegExp constructor instead of a literal to create one of the variable:

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

Difficulty arises if “keywords can have punctuation, since punctuation tends to have special meaning in regular expressions. Unfortunately, unlike most other languages ​​/ libraries with regexp support, there is no standard function for removing punctuation for regular expressions in JavaScript.

And you cannot be completely sure which characters need escaping, because not every regexp implementation in the browser is guaranteed to be exactly the same. (In particular, new browsers may add new functionality.) And backslash characters, which are not special, do not guarantee operation, although in practice this happens.

So the best thing you can do is one of:

  • attempt to catch every special character in common browser usage today [add: see Sebastian’s recipe]
  • backslash - avoid all non-literal characters. care: \ W will also match Unicode characters other than ASCII, which you really don't want.
  • just make sure there are no non-alphanumeric characters in your keyword before searching

If you use this to highlight words in HTML that already have markup, you have problems. Your word may appear in the element name or attribute value, in which case an attempt to wrap <b> around it will cause a split. In more complex scenarios, it is even possible to inject HTML into the XSS security hole. If you need to deal with markup, you will need a more complex approach, separating '<...>, before trying to process each piece of text yourself.

+5
Nov 11 '08 at 13:15
source share

Something like that:

 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); } } 

Then it could be called like this:

 var result = "The Quick Brown Fox Jumped Over The Lazy Brown Dog".highlight("brown","text-highlight"); 
+4
Sep 14 '12 at 20:00
source share

For the poor with dysrexia or regexophobia:

 function replacei(str, sub, f){ let A = str.toLowerCase().split(sub.toLowerCase()); let B = []; let x = 0; for (let i = 0; i < A.length; i++) { let n = A[i].length; B.push(str.substr(x, n)); if (i < A.length-1) B.push(f(str.substr(x + n, sub.length))); x += n + sub.length; } return B.join(''); } s = 'Foo and FOO (and foo) are all -- Foo.' t = replacei(s, 'Foo', sub=>'<'+sub+'>') console.log(t) 

Output:

 <Foo> and <FOO> (and <foo>) are all -- <Foo>. 
+1
Feb 14 '17 at 0:02
source share

Why not just create a new regular expression for every call to your function? You can use:

 new Regex([pat], [flags]) 

where [pat] is the string for the template, and [flags] are the flags.

0
Nov 11 '08 at 12:59
source share



All Articles