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:
- Walk through the whole DOM tree under target element, and cache all TEXT_NODE (
nodeType==3
);
- Use
RegExp.exec()
method to get the index of each match;
- 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;
- 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.