1

So I'm making a firefox addon to highlight words and reg. expressions and I'm having some troubles optimizing it. This was the 1st attempt:

function highlight (searchText, replacement) {
    var walker = document.createTreeWalker(document.body);
    while(walker.nextNode()){
        if(walker.currentNode.nodeType === 3 && searchText.test(walker.currentNode.nodeValue)){
            var html = walker.currentNode.data.replace(searchText, replacement);
            var wrap = document.createElement('div');
            var frag = document.createDocumentFragment();
            wrap.innerHTML = html;
            while (wrap.firstChild) {
                frag.appendChild(wrap.firstChild);
            }
            walker.currentNode.parentNode.replaceChild(frag,walker.currentNode);            
        }
    }
}

But the walker.currentNode.parentNode.replaceChild(frag,walker.currentNode); line replaces the current node so the while(walker.nextNode()) stopped working.

I've solved it like this but i was looking for a cleaner solution:

function highlight (searchText, replacement) {
    var walker = document.createTreeWalker(document.body);
    var nextnode=true;
    while(nextnode){
        if(walker.currentNode.nodeType === 3 && searchText.test(walker.currentNode.nodeValue)){
            //1~2 ms
            var html = walker.currentNode.data.replace(searchText, replacement);
            //~11-12 ms           
            var wrap = document.createElement('div');
            var frag = document.createDocumentFragment();
            //~11-12 ms
            wrap.innerHTML = html;
            //~36-37 ms          
            while (wrap.firstChild) {                
                frag.appendChild(wrap.firstChild);    
            }
            //73~74 ms
            var nodeToReplace=walker.currentNode;
            nextnode=walker.nextNode();
            nodeToReplace.parentNode.replaceChild(frag,nodeToReplace);
            //83~85 ms            
        }else{
            nextnode=walker.nextNode();
        }
    }
}

Also I'm trying to improve performance so I've made some test to look for the slower parts of the code (I've tested using a 1.64 mb lorem ipsum) so here are my questions:

  1. Is there a faster alternative for the wrap.innerHTML = html; that is adding 25 ms to the code?

  2. I'm pretty sure that this can't be optimized while (wrap.firstChild) {frag.appendChild(wrap.firstChild);} but it adds 37 ms so suggestions are welcome.

Feel free to use this code the snippet is a working example of the code and shows how to use the it. Edited to show latest changes, you may need to edit the excludes to be less restrictive.

var regexp = /lorem|amet/gi;
highlight (regexp,'<span style="Background-color:#33FF33">$&</span>');

function highlight (searchText, replacement) {
    var excludes = 'html,head,style,title,link,script,noscript,object,iframe,canvas,applet';
    var wrap = document.createElement('div');
    var frag = document.createDocumentFragment();
    
    var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
    var nextnode=true;
    while(nextnode){
        if(searchText.test(walker.currentNode.nodeValue)
           && (excludes + ',').indexOf(walker.currentNode.parentNode.nodeName.toLowerCase() + ',') === -1
          ){
            var html = walker.currentNode.data.replace(searchText, replacement);
            wrap.innerHTML = html;
            while (wrap.firstChild) {                
                frag.appendChild(wrap.firstChild);    
            }
            var nodeToReplace=walker.currentNode;
            nextnode=walker.nextNode();
            nodeToReplace.parentNode.replaceChild(frag,nodeToReplace);            
        }else{
            nextnode=walker.nextNode();
        }
    }
}
<h1>HTML Ipsum Presents</h1>

<p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris
  placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis
  tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>

<h2>Header Level 2</h2>

<ol>
  <li>Lorem ipsum dolor sit amet, consectetuer lorem adipiscing elit.</li>
  <li>Aliquam tincidunt mauris eu risus.</li>
</ol>

<blockquote>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis
    elit sit amet quam. Vivamus pretium ornare est.</p>
</blockquote>

<h3>Header Level 3</h3>

<ul>
  <li>Lorem ipsum dolor sit amet, consectetuer lorem adipiscing elit.</li>
  <li>Aliquam tincidunt mauris eu risus.</li>
</ul>
Sdar
  • 11
  • 2
  • If you're only looking for textNodes, why don't you pass an `NodeFilter.SHOW_TEXT` ? Your while loop may take less time and you avoid a useless `if(walker.currentNode.nodeType === 3)` – Kaiido Jul 27 '15 at 01:21
  • Also you may win a little bit by defining your wrap and frag elements out of the while loop and use `node.cloneNode()` – Kaiido Jul 27 '15 at 01:37
  • Yeah.. sorry i may add the ability to search through other nodes using an exclusion list.. but you're right, as it is right now a nodefilter should do the job just fine, but as it only takes 1 ms to find all the nodes the performance gain will be negligible. – Sdar Jul 27 '15 at 01:41
  • I've added the nodefilter and wrap and frag are now defined out of the while, it's faster now on webs with lot of nodes. About node.cloneNode ... are you saying to use it instead of documentfragment? – Sdar Jul 27 '15 at 01:55
  • No, I think that `node.cloneNode()` can be a little bit faster than `document.createNode()` may it be a `documentFragment`, a `textNode` or an `element`. But it really varies from implementation to others. – Kaiido Jul 27 '15 at 02:01
  • I've found another problem... it seems like web css are textnodes... I've checked the nodeNames ... and it says there are #text ... so i need to find a way to exclude css nodes... :S – Sdar Jul 27 '15 at 10:35
  • Ok i can just check for the parentnode like this `walker.currentNode.parentNode.nodeName` and then exclude html,head,style,title,link,script,noscript,object,iframe,canvas,applet. – Sdar Jul 27 '15 at 10:47

0 Answers0