Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace text inside an element with text containing html, without removing html already present

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>');
            }
        }
    }
});
like image 507
Metropolis Avatar asked Mar 10 '14 16:03

Metropolis


People also ask

How do I get text within an element?

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.

How do I edit text inside a div?

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.


2 Answers

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/

like image 161
nderscore Avatar answered Oct 23 '22 04:10

nderscore


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.

like image 34
John S Avatar answered Oct 23 '22 03:10

John S