54

我需要不区分大小写地突出显示 JavaScript 字符串中的给定关键字。

例如:

  • highlight("foobar Foo bar FOO", "foo")应该返回"<b>foo</b>bar <b>Foo</b> bar <b>FOO</b>"

我需要代码适用于任何关键字,因此使用硬编码的正则表达式就像/foo/i不是一个足够的解决方案。

最简单的方法是什么?

(这是标题中详述的更普遍问题的一个例子,但我觉得最好用一个具体的、有用的例子来解决。)

4

7 回答 7

72

如果您准备搜索字符串,则可以使用正则表达式。例如,在 PHP 中有一个函数 preg_quote,它将字符串中的所有正则表达式字符替换为其转义版本。

这是 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'), '\\$&')
}

因此,您可以执行以下操作:

function highlight(str, search) {
    return str.replace(new RegExp("(" + preg_quote(search) + ")", 'gi'), "<b>$1</b>");
}
于 2008-11-11T12:54:48.690 回答
64
function highlightWords( line, word )
{
     var regex = new RegExp( '(' + word + ')', 'gi' );
     return line.replace( regex, "<b>$1</b>" );
}
于 2008-11-11T13:06:44.033 回答
13

您可以使用为您执行特殊字符转义的函数来增强 RegExp 对象:

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

然后您就可以毫无顾虑地使用其他人的建议:

function highlightWordsNoCase(line, word)
{
  var regex = new RegExp("(" + RegExp.escape(word) + ")", "gi");
  return line.replace(regex, "<b>$1</b>");
}
于 2008-11-11T14:07:47.717 回答
6

只要关键字是真正的单词,正则表达式就可以了,您可以只使用 RegExp 构造函数而不是文字来从变量创建一个:

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

如果“关键字”可以包含标点符号,则会出现困难,因为标点符号在正则表达式中往往具有特殊含义。不幸的是,与大多数其他支持正则表达式的语言/库不同,JavaScript 中没有标准的函数来转义正则表达式的标点符号。

而且您不能完全确定哪些字符需要转义,因为并非每个浏览器的正则表达式实现都保证完全相同。(特别是,较新的浏览器可能会添加新功能。)不特殊的反斜杠转义字符不能保证仍然有效,尽管实际上它确实有效。

因此,您能做的最好的事情之一是:

  • 试图捕捉当今常用浏览器中的每个特殊字符 [添加:参见塞巴斯蒂安的食谱]
  • 反斜杠转义所有非字母数字。关心:\W 也将匹配非 ASCII Unicode 字符,这是您并不真正想要的。
  • 只需确保在搜索之前关键字中没有非字母数字

但是,如果您使用它来突出显示 HTML 中已经包含标记的单词,那么您就有麻烦了。您的“单词”可能出现在元素名称或属性值中,在这种情况下,尝试将 < b> 包裹在它周围会导致损坏。在更复杂的情况下,甚至可能是对 XSS 安全漏洞的 HTML 注入。如果您必须处理标记,您将需要一种更复杂的方法,将 '< ... >' 标记,然后再尝试单独处理每段文本。

于 2008-11-11T13:15:49.757 回答
6

像这样的东西怎么样:

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

然后可以这样调用:

var result = "The Quick Brown Fox Jumped Over The Lazy Brown Dog".highlight("brown","text-highlight");
于 2012-09-14T20:00:26.270 回答
3

对于那些患有失调症或正则恐惧症的穷人:

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)

输出:

<Foo> and <FOO> (and <foo>) are all -- <Foo>.
于 2017-02-14T00:02:38.140 回答
0

为什么不在每次调用您的函数时创建一个新的正则表达式?您可以使用:

new Regex([pat], [flags])

其中 [pat] 是模式的字符串,[flags] 是标志。

于 2008-11-11T12:59:41.317 回答