Search code examples
jqueryhighlighting

Highlight search terms (select only leaf nodes)


I would like to highlight search terms on a page, but not mess with any HTML tags. I was thinking of something like:

$('.searchResult *').each(function() {
    $(this.html($(this).html().replace(new RegExp('(term)', 'gi'), '<span class="highlight">$1</span>'));
)};

However, $('.searchResult *').each matches all elements, not just leaf nodes. In other words, some of the elements matched have HTML inside them. So I have a few questions:

  1. How can I match only leaf nodes?
  2. Is there some built-in jQuery RegEx function to simplify things? Something like: $(this).wrap('term', $('<span />', { 'class': 'highlight' }))
  3. Is there a way to do a simple string replace and not a RegEx?
  4. Any other better/faster way of doing this?

Thanks so much!


Solution

  • [See it in action]

    // escape by Colin Snover
    // Note: if you don't care for (), you can remove it..
    RegExp.escape = function(text) {
        return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
    }
    
    function highlight(term, base) {
      if (!term) return;
      base = base || document.body;
      var re = new RegExp("(" + RegExp.escape(term) + ")", "gi"); //... just use term
      var replacement = "<span class='highlight'>" + term + "</span>";
      $("*", base).contents().each( function(i, el) {
        if (el.nodeType === 3) {
          var data = el.data;
          if (data = data.replace(re, replacement)) {
            var wrapper = $("<span>").html(data);
            $(el).before(wrapper.contents()).remove();
          }
        }
      });
    }
    
    function dehighlight(term, base) {
      var text = document.createTextNode(term);
      $('span.highlight', base).each(function () {
        this.parentNode.replaceChild(text.cloneNode(false), this);
      });
    }