Currently I search within a div within an html file and remove the hideMe class if a result is found inside it, to reveal the found hymn. I’m wondering if I can search the hymn without punctuation (removing punctuation from both input and output), while also excluding the info class from the search.
<div id="himnario">
<div id="1" class="song hideMe">
<div class="info">I don't want this info to be searched</div>
<div class="tuneName">This tune should be searched</div>
<ol>
<li>Verse 1</li>
<li>Verse 2</li>
</ol>
</div>
<div id="2" class="song hideMe">...</div>
</div>
My search code presently is:
$("#himnario div.song:Contains("+item+")").removeClass('hideMe').highlight(item);
isHighlighted = true; //check if highlighted later and unhighlight, for better performance
(extending jquery with “Contains” as follows)
return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
Also, I am using a jquery plugin for highlighting the results, so I suppose this would complicate things. If need be, the highlight could be disfunctional for those places where punctuation gets in the way.
Of course, the more efficient the better since this will be part of a mobile app… If removing the info class from the search takes a lot of time, I will have to just delete it from the file because it isn’t absolutely essential.
I found the following code from here that might help, which is supposed to strip invalid characters, but not sure how to incorporate it into the custom Contains function properly with my limited coding ability.
Return Regex.Replace(strIn, "[^\w\.@-]", "")
Thanks so much in advance for your help.
Edit: Here is the preferred solution thanks to @Nick:
$('#himnario').children().addClass('hideMe'); // hide all hymns
//http://stackoverflow.com/questions/12152098/jquery-search-contains-without-punctuation-excluding-specific-class
// Get rid of punctuation in your search item - this only allows alphanumeric
item2 = item.toUpperCase().replace(/<(.|\n)*?>|[^a-z0-9\s]/gi, '');
// Loop though each song
$('#himnario').children().each(function() {
var $this_song = $(this);
// Examine the song title & the ordered list, but not the hidden info (first child)
$this_song.children('.tuneName, ol').each(function() {
// Get the html, strip the punctuation and check if it contains the item
if ($(this).html().toUpperCase().replace(/<(.|\n)*?>|[^a-z0-9\s]/gi, '').indexOf(item2) !== -1) {
// If item is contained, change song class
$this_song.removeClass('hideMe').highlight(item); //original search phrase
isHighlighted = true; //check later, for better performance
return false; // Prevents examination of song lines if the title contains the item
}
});
});
Highlight function:
/*
highlight v3
Highlights arbitrary terms.
<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>
MIT license.
Johann Burkard
<http://johannburkard.de>
<mailto:jb@eaio.com>
*/
jQuery.fn.highlight = function(pat) {
function innerHighlight(node, pat) {
var skip = 0;
if (node.nodeType == 3) {
var pos = node.data.toUpperCase().indexOf(pat);
if (pos >= 0) {
var spannode = document.createElement('span');
spannode.className = 'highlight';
var middlebit = node.splitText(pos);
var endbit = middlebit.splitText(pat.length);
var middleclone = middlebit.cloneNode(true);
spannode.appendChild(middleclone);
middlebit.parentNode.replaceChild(spannode, middlebit);
skip = 1;
}
}
else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
for (var i = 0; i < node.childNodes.length; ++i) {
i += innerHighlight(node.childNodes[i], pat);
}
}
return skip;
}
return this.each(function() {
innerHighlight(this, pat.toUpperCase());
});
};
jQuery.fn.removeHighlight = function() {
return this.find("span.highlight").each(function() {
this.parentNode.firstChild.nodeName;
with (this.parentNode) {
replaceChild(this.firstChild, this);
normalize();
}
}).end();
};
jQuery works quickest if you go straight to an element by its id, and then filter from there. So, I’ll assume your HTML is like this:
To find the songs most efficiently, you do this:
Note:
children()is much better thanfind()because it only searches to a depth of one level. Not specifying.songwill speed things up if there are only songs as children. If so, you are going much faster already.Once you’ve got the children, you can use
each()which is not the absolutely fastest way, but it’s OK. So this examines each song/child:For your case:
I haven’t done anything with the highlighting. I also haven’t tested this, but it should work fine once you get any small bugs out 🙂
EDIT: In light of your “song title” field, you can do the following:
This version should be quicker than looping through each individual line. Note, too, that I’ve removed the
indexandindex2vars from the.eachcalls as you don’t use them.