Substituição de seqüência insensível a maiúsculas e minúsculas em JavaScript?

Eu preciso destacar, caso insensível, dadas as palavras-chave em uma string JavaScript.

Por exemplo:

  • highlight("foobar Foo bar FOO", "foo") deve retornar "foobar Foo bar FOO"

Eu preciso que o código funcione para qualquer palavra-chave e, portanto, usar uma expressão regular codificada como /foo/i não é uma solução suficiente.

Qual é a maneira mais fácil de fazer isso?

(Esse é um exemplo de um problema mais geral detalhado no título, mas acho que é melhor abordar com um exemplo concreto e útil.)

Você pode usar expressões regulares se preparar a string de pesquisa. No PHP, por exemplo, há uma function preg_quote, que substitui todos os regex-chars em uma string pelas suas versões com escape.

Aqui está uma function para 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"); } 

(Extraído de http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_preg_quote/ )

Então você pode fazer o seguinte:

 function highlight( data, search ) { return data.replace( new RegExp( "(" + preg_quote( search ) + ")" , 'gi' ), "$1" ); } 
 function highlightWords( line, word ) { var regex = new RegExp( '(' + word + ')', 'gi' ); return line.replace( regex, "$1" ); } 

Você pode melhorar o object RegExp com uma function que faz um caractere especial escaping para você:

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

Então você seria capaz de usar o que os outros sugeriram sem preocupações:

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

Expressões regulares são boas desde que palavras-chave sejam realmente palavras, você pode simplesmente usar um construtor RegExp em vez de um literal para criar um a partir de uma variável:

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

A dificuldade surge se as palavras-chave podem ter pontuação, pois a pontuação tende a ter um significado especial nas expressões regulares. Infelizmente, ao contrário da maioria das outras linguagens / bibliotecas com suporte ao regexp, não existe uma function padrão para escaping da pontuação para regexps em JavaScript.

E você não pode ter certeza absoluta de quais caracteres precisam escaping, porque nem toda implementação de regexp do navegador é garantida como sendo exatamente a mesma. (Em particular, os navegadores mais novos podem adicionar novas funcionalidades.) E os caracteres que escapam da barra invertida que não são especiais não garantem que ainda funcionem, embora, na prática, isso aconteça.

Então, o melhor que você pode fazer é um dos seguintes:

  • tentando capturar cada caractere especial no uso comum do navegador hoje [add: veja a receita de Sebastian]
  • escape de barra invertida todos os não alfanuméricos. care: \ W também corresponderá a caracteres Unicode não-ASCII, o que você realmente não deseja.
  • apenas certifique-se de que não haja alfanuméricos na palavra-chave antes da pesquisa

Se você estiver usando isso para destacar palavras em HTML que já possuem marcação, você tem problemas. Sua “palavra” pode aparecer em um nome de elemento ou valor de atributo. Nesse caso, tentar envolver um em torno dele causará um dano. Em cenários mais complicados, possivelmente, até mesmo uma injeção de HTML para o buraco de segurança do XSS. Se você tiver que lidar com a marcação, precisará de uma abordagem mais complicada, dividindo a marcação ‘< ...>‘ antes de tentar processar cada trecho de texto por conta própria.

E algo assim:

 if(typeof String.prototype.highlight !== 'function') { String.prototype.highlight = function(match, spanClass) { var pattern = new RegExp( match, "gi" ); replacement = "$&"; return this.replace(pattern, replacement); } } 

Isso poderia então ser chamado assim:

 var result = "The Quick Brown Fox Jumped Over The Lazy Brown Dog".highlight("brown","text-highlight"); 

Para os pobres com disregexia ou regexofobia:

 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) 

Por que não apenas criar um novo regex em cada chamada para sua function? Você pode usar:

 new Regex([pat], [flags]) 

onde [pat] é uma string para o padrão, e [flags] são os flags.