I would like to highlight search terms on a page, but not mess with any HTML tags. I was thinking of something like:
$(\'.searchResult *\').each(function()
// 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 = "<span class='highlight'>" + term + "</span>";
$("*", base).contents().each( function(i, el) {
if (el.nodeType === 3) {
var data = el.data;
if (data = data.replace(re, replacement)) {
var wrapper = $("<span>").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);
});
}
Use contents()
1, 2, 3 to get all nodes including text nodes, filter out the non-text nodes, and finally replace the nodeValue
of each remaining text node using regex. This would keep the html nodes intact, and only modify the text nodes. You have to use regex instead of simple string substitutions as unfortunately we cannot do global replacements when the search term is a string.
function highlight(term) {
var regex = new RegExp("(" + term + ")", "gi");
var localRegex = new RegExp("(" + term + ")", "i");
var replace = '<span class="highlight">$1</span>';
$('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 = $('<div>').append(this.nodeValue.replace(regex, replace));
$(this).before(wrapped.contents()).remove();
});
}
We can't make it a one-liner and much shorter easily now, so I prefer it like this :)
See example here.
Here's a naive implementation that just blasts in HTML for any match:
<!DOCTYPE html>
<html lang"en">
<head>
<title>Select Me</title>
<style>
.highlight {
background:#FF0;
}
</style>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
$(function () {
hightlightKeyword('adipisicing');
});
function hightlightKeyword(keyword) {
var replacement = '<span class="highlight">' + keyword + '</span>';
var search = new RegExp(keyword, "gi");
var newHtml = $('body').html().replace(search, replacement);
$('body').html(newHtml);
}
</script>
</head>
<body>
<div>
<p>Lorem ipsum dolor sit amet, consectetur <b>adipisicing</b> 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.</p>
<p>Lorem ipsum dolor sit amet, <em>consectetur adipisicing elit</em>, 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.</p>
<p>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.</p>
</div>
</body>
</html>
My reputation is not high enough for a comment or adding more links, so I am sorry to write a new answer without all references.
I was interested in the performance of the mentioned solutions above and added some code for measurement. To keep it simple I added only these lines:
var start = new Date();
// hightlighting code goes here ...
var end = new Date();
var ms = end.getTime() - start.getTime();
jQuery("#time-ms").text(ms);
I have forked the solution of Anurag with these lines and this resulted in 40-60ms in average.
So I forked this fiddle and made some improvements to fit my needs. One thing was the RegEx-escaping (plz see the answer from CoolAJ86 in "escape-string-for-use-in-javascript-regex" in stackoverflow). Another point was the prevention of a second 'new RegExp()', as the RegExp.test-function should ignore the global flag and return on the first matching (plz see javascript reference on RegExp.test).
On my machine (chromium, linux) I have runtimes about 30-50ms. You can test this by yourself in this jsfiddle.
I also added my timers to the highest rated solution of galambalazs, you can find this in this jsFiddle. But this one has runtimes of 60-100ms.
The values in milliseconds become even higher and of much more importance when running (e.g. in Firefox about a quarter of a second).
I'd give the Highlight jQuery plugin a shot.
I spent hours searching the web for code that could highlight search terms as the user types, and none could do what I wanted until I combined a bunch of stuff together to do this (jsfiddle demo here):
$.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 '<span class="' + highlightClass + '">' + match + "</span>";
}, false);
replaceParentsWithChildren($(selectorToExclude + ' .' + highlightClass));//Do not highlight children of this selector.
}