I am trying to create a text search function but I am having a hard time getting it to work when there is html inside the element. Here is some simple html to demonstrate my problem.
<b>
<input type="checkbox" value="" />
I need replaced
</b>
And here is where I am currently at for javascript. It works great assuming there is no html inside.
$("*", search_container).each(function() {
var replaceTxt = $(this).text().replace(new RegExp("(" + search_term + ")", 'i'), '<span style="color: #0095DA;" class="textSearchFound">$1</span>');
$(this).text().replaceWith(replaceTxt);
});
As the user is typing I need to replace the text with the span. So the content should look like the following as he/she types.
<b>
<input type="checkbox" value="" />
<span style="color: #0095DA" class="textSearchFound">I need</span> replaced
</b>
UPDATE
After looking over the question that was voted a duplicate I came up with this. Which although may be close, it inserts the html into the DOM as text which I do not want. Please vote to reopen this.
$("*", search_container).each(function() {
var node = $(this).get(0);
var childs = node.childNodes;
for(var inc = 0; inc < childs.length; inc++) {
//Text node
if(childs[inc].nodeType == 3){
if(childs[inc].textContent) {
childs[inc].textContent = childs[inc].textContent.replace(new RegExp("(" + search_term + ")", 'i'), '<span style="color: #0095DA;" class="textSearchFound">$1</span>');
}
//IE
else {
childs[inc].nodeValue = childs[inc].nodeValue.replace(new RegExp("(" + search_term + ")", 'i'), '<span style="color: #0095DA;" class="textSearchFound">$1</span>');
}
}
}
});
Answer: Use the jQuery text() method You can simply use the jQuery text() method to get all the text content inside an element. The text() method also return the text content of child elements.
Use the textContent property to change the text of a div element, e.g. div. textContent = 'Replacement text' . The textContent property will set the text of the div to the provided string, replacing any of the existing content.
It's not pretty, but the best way to do this would be to loop through every node on the page recursively. When you come across a text node, check if it contains your search string. If you find it, delete the original text node and replace it with a text node of the text before the match, a new element with the highlighted match, and a text node of what's after the match.
Here's an example function:
var highlightSomeText = function(needle, node){
node = node || document.body; // initialize the first node, start at the body
if(node.nodeName == 'SCRIPT') return; // don't mess with script tags
if(node.nodeType == 3){ // if node type is text, search for our needle
var parts = node.nodeValue.split(needle); // split text by our needle
if(parts.length > 1){ // if we found our needle
var parent = node.parentNode
for(var i = 0, l = parts.length; i < l;){
var newText = document.createTextNode(parts[i++]); // create a new text node, increment i
parent.insertBefore(newText, node);
if(i != l){ // if we're not on the last part, insert a new span element for our highlighted text
var newSpan = document.createElement('span');
newSpan.className = 'textSearchFound';
newSpan.style.color = '#0095DA';
newSpan.innerHTML = needle;
parent.insertBefore(newSpan, node);
}
}
parent.removeChild(node); // delete the original text node
}
}
for(var i = node.childNodes.length; i--;) // loop through all child nodes
highlightSomeText(needle, node.childNodes[i]);
};
highlightSomeText('I need');
And a demo on jsfiddle: http://jsfiddle.net/8Mpqe/
Edit: Here is an updated method which uses Regexp to be case-insensitive: http://jsfiddle.net/RPuRG/
Updated lines:
var parts = node.nodeValue.split(new RegExp('(' + needle + ')','i'));
Using a regular expression which is wrapped in a capturing group ()
the results of the capture will be spliced into the array of parts.
newSpan.innerHTML = parts[i++];
The text for the new span element will be the next part in our array.
Edit 2: Since this function is being called on every keyup event of some text field, the asker wanted to remove the highlighted span tags before each run. To do this, it was necessary to unwrap each highlighted element, then to call normalize
on the parent node to merge adjacent text nodes back together. Here's a jQuery example:
$('.textSearchFound').each(function(i, el){
$(el).contents().unwrap().
parent()[0].normalize();
});
And here's the completed working demo: http://jsfiddle.net/xrL3F/
You can use the JQuery .contents()
method to get the child nodes of an element. The text nodes will have nodeType == 3
. You can then get the text of a text node using its textContent
property.
var getNonEmptyChildTextNodes = function($element) {
return $element.contents().filter(function() {
return (this.nodeType == 3) && (this.textContent.trim() != '');
});
};
var $textNode = getNonEmptyChildTextNodes($('b')).first(),
text = $textNode[0].textContent;
Then to perform the replacement, you can do the following:
var search_term = 'I need',
regExp = new RegExp(search_term, 'gi');
var result = regExp.exec(text);
if (result !== null) {
var $elements = $(),
startIndex = 0;
while (result !== null) {
$elements = $elements.add(document.createTextNode(text.slice(startIndex, result.index)));
$elements = $elements.add('<span style="color: #0095DA;" class="textSearchFound">' + result[0] + '</span>');
startIndex = regExp.lastIndex;
result = regExp.exec(text);
}
$elements = $elements.add(document.createTextNode(text.slice(startIndex)));
$textNode.replaceWith($elements);
}
jsfiddle
Here is a jsfiddle showing the complete search & wrap using the code above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With