突出显示搜索词(仅选择叶节点)

我想在页面上突出显示搜索字词,但不要弄乱任何HTML标记。 我想的是:

$('.searchResult *').each(function() { $(this.html($(this).html().replace(new RegExp('(term)', 'gi'), '$1')); )}; 

但是, $('.searchResult *').each匹配所有元素,而不仅仅是叶节点。 换句话说,匹配的一些元素在其中包含HTML。 所以我有几个问题:

  1. 我怎样才能匹配叶节点?
  2. 是否有一些内置的jQuery RegEx函数来简化事情? 类似于: $(this).wrap('term', $('', { 'class': 'highlight' }))
  3. 有没有办法做一个简单的字符串替换而不是RegEx?
  4. 还有其他更好/更快的方法吗?

非常感谢!

[ 看到它在行动 ]

 // escape by Colin Snover // Note: if you don't care for (), you can remove it.. RegExp.escape = function(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } function highlight(term, base) { if (!term) return; base = base || document.body; var re = new RegExp("(" + RegExp.escape(term) + ")", "gi"); //... just use term var replacement = "" + term + ""; $("*", base).contents().each( function(i, el) { if (el.nodeType === 3) { var data = el.data; if (data = data.replace(re, replacement)) { var wrapper = $("").html(data); $(el).before(wrapper.contents()).remove(); } } }); } function dehighlight(term, base) { var text = document.createTextNode(term); $('span.highlight', base).each(function () { this.parentNode.replaceChild(text.cloneNode(false), this); }); } 

使用contents() 1,2,3获取包括文本节点在内的所有节点,过滤掉非文本节点,最后使用regex替换每个剩余文本节点的nodeValue 。 这将使html节点保持完整,并且仅修改文本节点。 您必须使用正则表达式而不是简单的字符串替换,但遗憾的是,当搜索项是字符串时,我们无法进行全局替换。

 function highlight(term) { var regex = new RegExp("(" + term + ")", "gi"); var localRegex = new RegExp("(" + term + ")", "i"); var replace = '$1'; $('body *').contents().each(function() { // skip all non-text nodes, and text nodes that don't contain term if(this.nodeType != 3 || !localRegex.test(this.nodeValue)) { return; } // replace text node with new node(s) var wrapped = $('
').append(this.nodeValue.replace(regex, replace)); $(this).before(wrapped.contents()).remove(); }); }

我们现在不能轻易做到单线,更短,所以我更喜欢这样:)

见这里的例子

我将突出显示 jQuery插件。

我花了几个小时在网上搜索可以突出显示用户类型的搜索字词的代码,没有人可以做我想要的东西,直到我把一堆东西组合起来做这个( 这里的jsfiddle演示 ):

 $.fn.replaceText = function(search, replace, text_only) { //http://stackoverflow.com/a/13918483/470749 return this.each(function(){ var v1, v2, rem = []; $(this).find("*").andSelf().contents().each(function(){ if(this.nodeType === 3) { v1 = this.nodeValue; v2 = v1.replace(search, replace); if(v1 != v2) { if(!text_only && /<.*>/.test(v2)) { $(this).before( v2 ); rem.push(this); } else { this.nodeValue = v2; } } } }); if(rem.length) { $(rem).remove(); } }); }; function replaceParentsWithChildren(parentElements){ parentElements.each(function() { var parent = this; var grandparent = parent.parentNode; $(parent).replaceWith(parent.childNodes); grandparent.normalize();//merge adjacent text nodes }); } function highlightQuery(query, highlightClass, targetSelector, selectorToExclude){ replaceParentsWithChildren($('.' + highlightClass));//Remove old highlight wrappers. $(targetSelector).replaceText(new RegExp(query, "gi"), function(match) { return '' + match + ""; }, false); replaceParentsWithChildren($(selectorToExclude + ' .' + highlightClass));//Do not highlight children of this selector. } 

我已经制作了一个纯JavaScript版本,并将其打包到Google Chrome插件中,我希望对某些人有所帮助。 核心function如下所示:

页内荧光笔的GitHub页面

 function highlight(term){ if(!term){ return false; } //use treeWalker to find all text nodes that match selection //supported by Chrome(1.0+) //see more at https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker var treeWalker = document.createTreeWalker( document.body, NodeFilter.SHOW_TEXT, null, false ); var node = null; var matches = []; while(node = treeWalker.nextNode()){ if(node.nodeType === 3 && node.data.indexOf(term) !== -1){ matches.push(node); } } //deal with those matched text nodes for(var i=0; i 

这是一个天真的实现,只是在任何匹配的HTML中爆炸:

    Select Me      

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

我的声誉不足以发表评论或添加更多链接,所以很抱歉在没有所有引用的情况下撰写新答案。

我对上述解决方案的性能感兴趣,并添加了一些测量代码。 为了简单起见,我只添加了以下几行:

 var start = new Date(); // hightlighting code goes here ... var end = new Date(); var ms = end.getTime() - start.getTime(); jQuery("#time-ms").text(ms); 

我已经用这些线分叉了Anurag的解决方案,这导致平均40-60ms。

所以我分叉了这个小提琴并做了一些改进以满足我的需求。 有一件事是RegEx-escape(plz在stackoverflow中看到CoolAJ86在“escape-string-for-use-in-javascript-regex”中的答案)。 另一点是防止第二个’新的RegExp()’,因为RegExp.test函数应该忽略全局标志并返回第一个匹配(plz参见RegExp.test上的javascript参考)。

在我的机器上(铬,linux),我的运行时间约为30-50毫秒。 你可以在这个jsfiddle中自己测试一下。

我还将我的计时器添加到了最高级别的galambalaz解决方案中,你可以在这个jsFiddle中找到它。 但是这个运行时间为60-100ms。

运行时(例如在Firefox中大约四分之一秒),以毫秒为单位的值变得更高并且更加重要。