4umi.com/web/javascript/hilite

Highlight-a-text

Text Javascript

The Javascript on this page will highlight all matches to a query entered in the input box. A normal search will highlight any and all words separately, while a literal phrase search will look for exact matches of the entered phrase. A word is defined as every unbroken run of non-space characters. Case-sensitivity can be toggled on and off separately. It is even possible to have your input treated as a regular expression, although this may lead to unpredictable results if you don't know what you are doing. Refresh the page to remove the highlighting.

The form

Your query
Options:

High on the list of known issues features the problem with HTML tags in between or inside words. The script makes no attempt to distuinigh between block and phrase elements, does not even glue runs of textnodes together, but checks every node separately, thus missing possible matches to a query. Perhaps an incentive towards a markup style using less tags with more meaning.

The script

The heart of the machine is function highlight. The first parameter s is the string to search for. The second parameter o of type object is optional. It provides a means to restrict the search to a given element. The element is dissected into all its child elements. Where a match is found, the textnode is broken apart and a <span> element is inserted. The function uses the dom level 2 createElement() and cloneNode() method and advanced regular expressions, so it will not run in browsers equipped with less than Javascript version 1.2.

A global variable times keeps track of the number of queries entered by the user, and is used to calculate a different background colour for each query. Different background colours for different matches would be a more common approach, and is possible by rewriting one or two lines, or feeding the matches on their own to the function. An array of well chosen colourcodes (such as these) is also more common than the cold calculations, probably for a reason.

The function returns the number of replaced textual fragments. On this page, the calling function hi displays this number next to the search boxes.

var times = 0;

function hex( n ) { return ( n<16 ? '0' : '' ) + n.toString(16); }

function hi( f ) {
 f = f.elements, s = f.query.value;
 f.result.value = highlight( s ) + 'x ' + s;
 return false;
}

function highlight( s, o ) {
  if( !s ) { return 0; }
  var d = window.document, f = d.forms.f.elements;
  if( !f.regex.checked ) { s = s.replace( /([\\|^$()[\]{}.*+?])/g, '\\$1' ); }
  if( /^\s*$/.test( s ) ) { return 0; }
  if( !f.phrase.checked ) { s = s.split( /\s+/ ).join( '|' ); }
  o = [ o || d.documentElement || d.body ];
  var r = new RegExp( s, f.cases.checked ? 'g' : 'gi' ),
    h = d.createElement( 'span' ), i = 0, j, k, l, m, n=0, t;
  h.style.color = '#000';
  h.style.backgroundColor = '#'+( times%2 ? ''+hex(((times+1)%5)*51)+'ff' : 'ff'+hex((times%5)*51)+'' )+'00';
  times++;
  do {
    m = o[i];
    if( m.nodeType===3 ) {
      r.lastIndex = 0;
      l = r.exec(m.nodeValue);
      if( l !== null ) {
        k = l[0].length;
        if( r.lastIndex > k ) {
          m.splitText( r.lastIndex - k );
          m = m.nextSibling;
        }
        if( m.nodeValue.length > k ) {
          m.splitText(k);
          o[i++] = m.nextSibling;
        }
        t = h.cloneNode( true );
        t.appendChild( d.createTextNode( l[0] ) );n++;
        m.parentNode.replaceChild( t, m );
      }
    } else {
      j = m.childNodes.length;
      while ( j ) { o[i++] = m.childNodes.item( --j ); }
    }
  } while( i-- );
  return n;
}


function unhighlight( s, o ) {
 var d = window.document;
 s = s.replace(/([\\|^$()[\]{}.*+?])/g, '\\$1').split( /\s+/ ).join( '|' );
 o = o || d.documentElement || d.body;
 var a = o.getElementsByTagName( 'span' ), i = a.length, j,
  re = new RegExp( '^' + s + '$', 'i' );
 while( i-- ) {
  j = a[i].firstChild;
  if( j ) {
   if( j.nodeType===3 && re.test( j.nodeValue ) ) {
    a[i].parentNode.replaceChild( d.createTextNode( j.nodeValue ), a[i] );
   }
  }
 }
 return false;
}

Some random filler text

The rest of the page offers some more text with all sorts of letters in various patterns and non-patterns for your highlighting pleasure.

Lorem Ipsum is the well known dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.

Less random filler

To illustrate a sample query discussed in the comp.lang.javascript newsgroup regarding the regular expression pattern number\s\d, here is number 1 and this is number 2. A bit further down is number 3, which brings us to the last number: 4!

Reference