javascript - Replace text inside an element with text containing html, without removing html already present - Stack Overflow

admin2025-04-21  0

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>');
            }
        }
    }
});

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>');
            }
        }
    }
});
Share Improve this question edited Mar 10, 2014 at 22:01 Metropolis asked Mar 10, 2014 at 16:00 MetropolisMetropolis 6,62219 gold badges60 silver badges87 bronze badges 5
  • Is this to be used on a page you can control the html for, or a more general case where you want to be able to replace text and you don't have control of the structure of the html? – Jason Aller Commented Mar 10, 2014 at 16:07
  • @JasonAller A more general cause. I need this to work under all conditions where I dont know what will be in the elements. – Metropolis Commented Mar 10, 2014 at 16:11
  • Maybe try this plugin? bartaz.github.io/sandbox.js/jquery.highlight.html – wirey00 Commented Mar 10, 2014 at 16:15
  • That other one that is supposed to be a duplicate does not work while trying to insert html. – Metropolis Commented Mar 10, 2014 at 17:06
  • 5 This question has been erroneously marked as a duplicate. The question which is referenced as the dupe does not cover the use case specified in this question. It only handles cases where the user wants to replace text with text, not replacing a section of text with new HTML elements, so the solution is significantly different. – nderscore Commented Mar 21, 2014 at 4:09
Add a ment  | 

2 Answers 2

Reset to default 6

It's not pretty, but the best way to do this would be to loop through every node on the page recursively. When you e 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/8Mpqe/


Edit: Here is an updated method which uses Regexp to be case-insensitive: http://jsfiddle/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 pleted working demo: http://jsfiddle/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 plete search & wrap using the code above.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1745171924a288708.html

最新回复(0)