2

I want to make a word bold in given paragraph. Here is a javascript code.

        var hlWord = "idm";
        var nregex = new RegExp(hlWord,"gi");
        var div = document.getElementById("SR").innerHTML;
        var rword = div.replace(nregex,"<b>"+hlWord+"</b>");
        document.getElementById("SR").innerHTML = rword;  

Here is a HTML code.

<div id="SR">
Download here free idm.
<a href="http://www.anywebsite.com/idm">click here to download</a>
</div>  

This is work well and make all idm bold but here is a problem that it also change url to like this

<a href="http://www.anywebsite.com/<b>idm</b>">click here to download</a>  

This is not a valid url.This is the problem that this code make the url damaged. Please tell me how can I avoid this.
Thanks...

Axeem
  • 670
  • 4
  • 16
  • 26
  • I don't know how deep your example goes but for your posted example, you could just use `var nregex = /idm/i;`. – fbynite Apr 15 '13 at 03:59

2 Answers2

0

You can iterate through all the text nodes with the methods in this thread, change them and replace them with new bold ones.

var hlWord = "idm";
var nregex = new RegExp(hlWord,"gi");

var sr = document.getElementById('SR');

function escape_html(html) {
    return html.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

(function findTextNodes(current) {
    // make a shadow copy of the child nodes.
    var current_children = Array.prototype.slice.call(current.childNodes);
    for(var i = 0; i < current_children.length; i++) {
        var child = current.childNodes[i];
        // text node
        if(child.nodeType == 3) {
            var value = escape_html(child.nodeValue);
            var html = value.replace(nregex, '<b>' + hlWord + '</b>');
            if (html != value) {
                var node = document.createElement('div');
                node.innerHTML = html;
                // make a shadow copy of the child nodes.
                var childNodes = Array.prototype.slice.call(node.childNodes);
                // replace the plain text node with the bold segments
                for (var j = 0; j < childNodes.length; j++) {
                    var c = childNodes[j];
                    current.insertBefore(c, child);
                }
                current.removeChild(child);
            }
        }
        else {
            findTextNodes(child);
        }
    }
})(sr);

Check the code example at jsFiddle.

UPDATE:

  • Passerby pointed out that innerHTML should be used carefully. Escape text nodeValue before processing.
Community
  • 1
  • 1
Arie Xiao
  • 13,909
  • 3
  • 31
  • 30
  • Though rare, but some text have meaning as HTML code, so directly modifying `innerHTML` may lead to unexpected result: http://jsfiddle.net/r9Qrr/3/ – Passerby Apr 15 '13 at 04:10
  • If you're going to use someone else's code, at least reference it: http://stackoverflow.com/questions/3963872/javascript-add-elements-in-textnode – Ian Apr 15 '13 at 04:17
  • @Passerby You're right. The text nodeValue needs to be escaped before processing replacement. – Arie Xiao Apr 15 '13 at 04:20
  • @lan I didn't reference that thread. I searched for text node traversal and found another thread which was posted earlier that the one you listed, and I had attached the link ***this thread*** in the first paragraph the first time I posted my answer. – Arie Xiao Apr 15 '13 at 04:30
  • @ArieShaw I don't think so, your code is almost exactly the same as the one I found and provided. And you never included anything in the first time you posted the answer - I can see your edit history and I remember your original answer. Nothing was added until I said something. Anyways, it doesn't matter, you made a great solution – Ian Apr 15 '13 at 08:12
  • @lan You mean I was cheating, that I stole someone's work without reference it, that matters. And I INSIST THAT I DIDN'T REFERENCE THE THREAD YOU GAVE. I searched for text node traversal and I have provided the link. I can see my edit history and I'm sure I wrote [***this thread***](http://stackoverflow.com/questions/2579666/getelementsbytagname-equivalent-for-textnodes). I spent a few minutes on the question and was pleased to figure out the way. It wasn't that sophisticated or extremely difficult to work out, not like those algorithm questions which have optimal solutions. – Arie Xiao Apr 15 '13 at 09:49
0

After some try-and-fail, I made a working demo that may be more complicated than you might have think:

http://jsfiddle.net/4VKNk/

var cache=[];
var reg=/idm/gi;
var id=function(ID){return document.getElementById(ID);}
function walkElement(ele){
    if(ele.childNodes.length>0){
        for(var i=0;i<ele.childNodes.length;i++){
            walkElement(ele.childNodes[i]);
        }
    }else if(ele.nodeType==3){//text node
        if(reg.test(ele.nodeValue)){
            cache.push(ele);
        }
    }
}
id("test").onclick=function(){
    cache=[];
    walkElement(id("SR"));
    while(cache.length>0){
        var ele=cache.shift();
        var val=ele.nodeValue;
        var pnt=ele.parentNode;
        var nextSibling=ele.nextSibling;
        var i=0;
        var r,tmp;
        pnt.removeChild(ele);
        while(r=reg.exec(val)){
            tmp=document.createTextNode(val.substring(i,r.index));
            if(nextSibling){
                pnt.insertBefore(tmp,nextSibling);
                tmp=document.createElement("strong");
                tmp.appendChild(document.createTextNode("idm"));
                pnt.insertBefore(tmp,nextSibling);
            }else{
                pnt.appendChild(tmp);
                tmp=document.createElement("strong");
                tmp.appendChild(document.createTextNode("idm"));
                pnt.appendChild(tmp);
            }
            i=reg.lastIndex;
        }
        if(i<val.length-1){
            tmp=document.createTextNode(val.substring(i,val.length));
            if(nextSibling){
                pnt.insertBefore(tmp,nextSibling);
            }else{
                pnt.appendChild(tmp);
            }
        }
    }
};

I took the approach of DOM manipulation.

Explanation:

  1. Walk through the whole DOM tree under target element, and cache all TEXT_NODE (nodeType==3);
  2. Use RegExp.exec() method to get the index of each match;
  3. While you find a match, add back the text that come before it, and then add a highlight element (<strong>) that contains the match; continue this step;
  4. If we still have text left, add it back.

The reason I need to cache the TEXT_NODEs first, is that if we directly modify it in walkElement, it will change childNodes.length of its parent, and break the process.

Passerby
  • 9,715
  • 2
  • 33
  • 50