Compare Word Highlight Implementations

Benchmark created by Levi Hackwith on


Preparation HTML

<div>
<div>test</div>
<div>test</div>
<div>test</div>
<div>
   <span>test</span>
   <div>test</div>
   <div>test</div>
</div>
</div>

Setup

window.location.search = "?terms=test&partial=true";

Test runner

Ready to run.

Testing in
TestOps/sec
Old Way
function highlightWord(node, word, instance, partial) {
  // Iterate into this nodes childNodes
  if (node.hasChildNodes) {
    for (var cn=0;cn<node.childNodes.length;cn++) {
      highlightWord(node.childNodes[cn],word,instance,partial);
    }
  }
  
  // And do this node itself
  if (node.nodeType == 3) { // text node
    var tempNodeVal = node.nodeValue.toLowerCase();
    var tempWordVal = word.toLowerCase();

    var ni = 0;
    if (partial) {
        ni = tempNodeVal.indexOf(tempWordVal);
    } else {
        var re = new RegExp("\\b" + tempWordVal + "\\b", "i");
        ni = tempNodeVal.search(re);
    }
            
    if (ni != -1) {    
        var pn = node.parentNode;
        if (pn.className.substr(0,10) != "searchword") {
            // word has not already been highlighted!
            var nv = node.nodeValue;
            // Create a load of replacement nodes
            var before = document.createTextNode(nv.substr(0,ni));
            var docWordVal = nv.substr(ni,word.length);
            var after = document.createTextNode(nv.substr(ni+word.length));
            var hiWordText = document.createTextNode(docWordVal);
            var hiWord = document.createElement("span");
            hiWord.className = "searchword" + (instance % 5 + 1);
            hiWord.appendChild(hiWordText);
            pn.insertBefore(before,node);
            pn.insertBefore(hiWord,node);
            pn.insertBefore(after,node);
            pn.removeChild(node);
        }
    }
  }
}

function searchHighlight() {
  if (!document.createElement) return;
  ref = window.location.search;
  if (ref.indexOf('?') == -1) return;
  qs = ref.substr(ref.indexOf('?')+1);
  qsa = qs.split('&');
  var partial = true;  
  for (i=0;i<qsa.length;i++) {
    if (ref.indexOf('=') == -1) return;
    qsip = qsa[i].split('=');
    if (qsip[0] == 'partial') {
        partial = (qsip[1] == 'true');
    }
    if (qsip[0] == 'terms') {
      words = unescape(qsip[1].replace(/\+/g,' ')).split(/\s+/);
      for (w=0;w<words.length;w++) {
        highlightWord(document.getElementsByTagName("body")[0],words[w],w,partial);
      }
    }
  }
}

searchHighlight();
 
ready
New Way
function hasClass(element, className) {
    'use strict';

    return (" " + element.className + " ").indexOf(" " + className + " ") > -1;
}

function highlightWord(node, word, instance, partial) {
    'use strict';

    var textNodeType = 3,
        numNodes = node.childNodes.length,
        nodeIndex = -1,
        parentNode,
        nodeValue,
        highlightClassName = "searchword",
        isHighlighted,
        classListSupported = document.createElement("div").classList ? true : false,
        i = 0;

    if (numNodes > 0) {
        for (i = 0; i < numNodes; i += 1) {
            highlightWord(node.childNodes[i], word, instance, partial);
        }
    }

    if (node.nodeType === textNodeType) {
        nodeValue = node.nodeValue;
        if (partial === true) {
            nodeIndex = nodeValue.toLowerCase().indexOf(word.toLowerCase());
        } else {
            nodeIndex = nodeValue.search(new RegExp("\\b" + word + "\\b", "i"));
        }

        if (nodeIndex > -1) {
            parentNode = node.parentNode();
            if (classListSupported === true) { // >  Gecko And Webkit Support
                isHighlighted = parentNode.classList.contains(highlightClassName);
            } else { // Everyone else
                isHighlighted = hasClass(parentNode, highlightClassName);
            }
            if (isHighlighted === false) {
                parentNode.nodeValue.replace(new RegExp(word), '<span class = "' + highlightClassName + '">' + word + '</span>', 'gi');
            }
        }
    }
}

function searchHighlight() {
    'use strict';

    var queryString = window.location.search,
        queryStringParams = [],
        parameterParts,
        parameterName,
        parameterValue,
        searchTerms = [],
        numQueryStringParams,
        numSearchTerms = 0,
        partialWordSearch = false,
        paramIndex = 0,
        documentBody,
        searchTermIndex = 0;

    if (queryString.length > 0) {
        queryString = queryString.substring(1); // Remove the ? from the queryString
        queryStringParams = queryStringParams.split("&");
        numQueryStringParams = queryStringParams.length;

        if (numQueryStringParams > 0) {
            documentBody = document.getElementsByTagName("body")[0];

            for (paramIndex = 0; paramIndex < numQueryStringParams; paramIndex += 1) {
                parameterParts = queryStringParams[paramIndex].split("=");
                if (parameterParts.length === 2) {
                    parameterName = parameterParts[0];
                    parameterValue = decodeURIComponent(parameterParts[1]);

                    switch (parameterName.toLowerCase()) {
                    case 'partial':
                        partialWordSearch = true;
                        break;
                    case 'terms':
                        searchTerms = parameterValue.split(" ");
                        numSearchTerms = searchTerms.length;
                        break;
                    }
                }
            }
            for (searchTermIndex = 0; searchTermIndex < numSearchTerms; searchTermIndex += 1) {
                highlightWord(documentBody, searchTerms[searchTermIndex], searchTermIndex, partialWordSearch);
            }
        }
    }
}

searchHighlight();
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by Levi Hackwith on