Using Color Transition to Guide the Reader

Richard A. DeVenezia, August 2003

The Problem

A single page contains a large number of links, presented in a list. Each item in the list links to a section of the page further down. Suppose there are quite a few sections that can appear in one screenful. This happens more often with higher resolution displays. After clicking the link the reader might not easily locate the destination the link took them to.

Show me the test

The Solution

Use javascript to briefly alter the destination section background and fade it out. The color and color motion draws the eye irresistibly to the destination. I call this spot lighting.

Making it Work

For all the simplicity of the concept, the means to achieve it might seem quite involved.

The document must be specially constructed for the process to work. As such it might be a suitable construct for html pages that are generated via cgi, jsp, php or asp.

Initially I thought to alter the document body by using JavaScript to insert a SPAN just prior to the destination and color tranisition that. For your own programming challenge give it a try.

Details of Construction

  1. Some links are collected in a list. The construct is thus:

    <LI>
    <A HREF="#destination" onclick="spot(this)">short bit of information<A>
    </LI>

    When the link is clicked, the browser will scroll the page to the destination and run the spot() method

  2. The content at the destination is preceded by a paragraph repeating the short bit of information. The paragraph serves as a section header and is the component that will endure the color transition initiated by spot(this). Make special note of the ID of the paragraph, it is the destination, preceded by the letter "p". The CLASS of the paragraph is used to provide additional consistent styling (learn more about CSS) .

    <P><A NAME="destination"></A></P>
    <HR NOSHADE="NOSHADE">
    <P ID="pdestination" CLASS="SectionHead">short bit of information</P>
    <P>Some detailed content</P>

Why so much construction ? Well, first I'm pretty sure valid HTML must have an <A> tag inside a block element such as <P>, <DIV>, <UL>, <OL>, <FORM>, etc... Second, I'm pretty sure valid HTML requires <A HREF point to an <A> tag with a NAME property. Most browsers however allow for slightly less than valid HTML (such as not wrapping <A> and HREF pointing to an element with matching ID).

When the page is constructed as described, all the pieces are in place and ready for javascript to operate upon them.

Details of Javascript

Several javascript functions need to be programmed, the most difficult being the color transition system. The javascript is stored in an external file and is referenced in the html HEAD.

<SCRIPT LANGUAGE="JAVASCRIPT" SRC="spotlight.js">

Let's examine the code in spotlight.js in the order of user action

The user clicks on the link which runs spot(this). The function looks for a local page reference, the stuff in the href after the # is the destination. Then it gets the element pdestination that will be spotlighted.

spot.onColor = '#DDDD99'
spot.offColor ='#FFFFFF'

//--------------------------------------------------------------
function spot (element) {
  var re = new RegExp ("(.*)#(.*)")
  var arr = re.exec (element.href)
  var pid = arr[2]

  po = document.getElementById('p'+pid)

  if (!po) return

  spot.set  (po,spot.onColor)            // set an eye grabbing color, not to garish now!
  spot.fade (po,spot.offColor,40,1250 )  // fade it out in 1.25 seconds
}

Here are the two functions that are called by spot(). If a destination is transitioning and the link is clicked again, the current transition needs to be stopped before another can start.

//--------------------------------------------------------------
spot.set = function (element,color)
{
  if ( element.transition )
    element.transition.doStop()

  element.style.backgroundColor = color
}

and

//--------------------------------------------------------------
spot.fade = function (element,color,steps,duration)
{
  element.transition = new transition (element, color, steps, duration)
  element.transition.doStart()
}

spotFade() instantiates (by using the new operator) a function named transition (), to get a transition object that records the color endpoints and keeps track of how far along the transition is. An important piece here is the obj argument. The obj is the <A> document element that was clicked. This information is stored in the transition object and will thus be available to the transition methods. The transition object becomes a property of the element so that the element can stop it's running transition if necessary.

//--------------------------------------------------------------
transition = function (obj, color, steps, duration)
{
  this.obj  = obj      // element whose background will change
  this.from = hexColorString (obj.style.backgroundColor).substr(1)
  this.to   = hexColorString (color).substr(1)
  this.step = 0        // current step

  if (!steps || !duration) {
    this.steps = 20;       // defaults, 20 steps lasting 1 second
    this.interval = 50;
  }
  else
  {
    this.steps = steps               // number of steps to take
    this.interval = duration / steps // time between steps (ms)
  }
}

spotFade() also invokes doStart(). A transition object will have three methods; doStart(), doStep() and doStop(). These methods are defined as prototype function properties of the transition function, which means all transition objects will have the functions available as methods.

Note the use of setInterval(). This technique requires a function local copy of this be utilized in the anonymous function being passed to setInterval(), if you pass simply this then it will resolve to window at function execution time, which is highly undesirable in this context.

//--------------------------------------------------------------
transition.prototype.doStart = function ()
{
  var thisObj = this
  this.timerId = setInterval ( function () { thisObj.doStep() }, this.interval )
}

Note the assignment of null. When the last existing reference to an object is destroyed, the object is available for garbage collection. Since we are stopping the interval timer, there is no reason to further maintain the transition object.

//--------------------------------------------------------------
transition.prototype.doStop = function ()
{
  clearInterval (this.timerId)
  this.obj.transition = null
}

//--------------------------------------------------------------
transition.prototype.doStep = function () {
  var from  = this.from
  var   to  = this.to
  var step  = this.step
  var steps = this.steps

  var r0 = parseInt (from.substr(0,2),16)  // base 16
  var g0 = parseInt (from.substr(2,2),16)
  var b0 = parseInt (from.substr(4,2),16)

  var r1 = parseInt (  to.substr(0,2),16)
  var g1 = parseInt (  to.substr(2,2),16)
  var b1 = parseInt (  to.substr(4,2),16)

  var r = Math.floor (r0 * ((steps-step)/steps) + r1 * (step/steps))
  var g = Math.floor (g0 * ((steps-step)/steps) + g1 * (step/steps))
  var b = Math.floor (b0 * ((steps-step)/steps) + b1 * (step/steps))

  this.obj.style.backgroundColor = hexColorString (r,g,b)

  this.step ++

  if ( this.step > this.steps )
    this.doStop()
}

Finally, hexColorString() is a function that assembles a color value #rrggbb given r,g,b values or rgb(r,g,b) string.

//--------------------------------------------------------------
function hexColorString (r,g,b) {
  // if only one argument presume it's a color specifier that
  // needs to be returned as #rgb, other construct #rgb from
  // r,g,b arguments (presumed to be numbers)

  if (arguments.length == 1) {
    var reColorHex = /#([a-f0-9]){2}([a-f0-9]){2}([a-f0-9]){2}/i ;

    if (r.match (reColorHex))
      return r

    var reColorRgb = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/ ;

    var c
    if (c = r.match (reColorRgb))
      return hexColorString (parseInt(c[1],10)
                            ,parseInt(c[2],10)
                            ,parseInt(c[3],10))

    return '#FF0000' // a color to indicate a problem
  }

  var r = r.toString(16); if (r.length == 1) r = '0'+r;
  var g = g.toString(16); if (g.length == 1) g = '0'+g;
  var b = b.toString(16); if (b.length == 1) b = '0'+b;

  return "#" + r + g + b
}


The test

Click on a link to jump to the section and have the section 'show' itself.


Section 1

This is section one full of details about section one that you should read. It comes first.


Section 2

This is section two full of details about section two that you should read. It comes after one and is in the middle.


Section 3

This is section three full of details about section three that you should read. It comes after section two and is last.


Comments or questions ? Contact Richard A. DeVenezia