Creating a table with dynamically highlighted columns like Crazy Egg’s pricing table

Update Aug 29, 2007: Examples 9 and 10, and the downloadable zip now accommodate for JavaScript-disabled browsers.

Update Aug 28, 2007: links to examples fixed. Again.

I like Crazy Egg‘s pricing table on their Pricing & Signup page. When you click on “Sign Up” for an option, that plan’s column highlights, the other plans vanish, and a signup form takes their place. There is a number of impressive things happening within this small area. I wanted to try and recreate the behavior step by step, and share the power of combining CSS, JavaScript, and images in clever ways.

If you just clicked over to look at Crazy Egg’s site, then clicked back, consider visiting there again when you’re finished reading this tutorial. If you’re as impressed with the way their site is put together as I am, you, or someone you know/work with, could probably benefit from Crazy Egg’s amazing click tracking visualization tools. Their prices sure make it easy to get started, so give it a consideration while you’re giving them traffic.

Crazy Egg's Pricing Table

For those who like to skip ahead: view the final example or download a zip with all the html, CSS, JavaScript, and supporting images used in this article.

Getting Started

First I need a table, and I’ll model my example off of Crazy Egg. I’ll use a left column for the descriptions, a top row for headings, and a matrix of features in the body. We’re going to make use of thead, tfoot, and tbody.

<table>
  <thead>
    <tr>
      <th>'s go here
    <tr>

  </thead>
  <tfoot>
    <tr>
      <td>'s go here
    </tr>
  </tfoot>

  <tbody>
    <tr>
      <td>'s go here
    </tr>
  </tbody>
</table>

View example 1, the bare table.

Next Step: Add class

By default, all the table cells look alike. To establish that cells in each column are related, I need to add classes to all the <td> and <th> tags.

<table>
  <thead>

    <tr>
      <th class="desc">
      <th class="choiceA">
      <th class="choiceB">
      ...
    <tr>

  </thead>
  <tfoot>
    <tr>
      <td class="desc">
      <td class="choiceA">

      <td class="choiceB">
    </tr>
  </tfoot>
  <tbody>
    <tr>
      <td class="desc">

      <td class="choiceA">
      <td class="choiceB">
    </tr>
  </tbody>
</table>

View example 2, adding class, which also shows the checkbox added. Here’s a screenshot.

Showing how columns are assigned a class

Next Step: Establish what a selected state looks like

We’d like to indicate a selected column by adding class="on" to the appropriate cells. Since all cells already have a class name assigned, the on class name will added to the class attribute value with a space.

<table>
  ...
  <tbody>

  ...
    <tr>
      <td class="desc">
      <td class="choiceA on">
      <td class="choiceB">
    </tr>

   ...
  </tbody>
</table>

View example 3, the selected state.

Selected state is indicated by adding class=on

Next Step: Switching selected columns with JavaScript

When someone hits the “Choose” button in a particular column, we want that entire column to take on the selected state. We need a JavaScript for that.

In English, here’s what we want to happen:

  1. When someone clicks “Choose” for choice A
  2. remove the class name of “on” from all other cells
  3. then, take every cell with the class for choice A
  4. and add ” on” to it.

Here are the tools needed to do that:

  • A javascript function called getElementsByClassName, which allows us to target elements based on their class value. (I’m using one written by Robert Nyman)
  • A javascript function called addClassName (also via Robert Nyman, although the one I’m using he hasn’t placed on his web site yet. This function will be used to add “on” as a class name to the selected columns.
  • A javascript function called removeClassName, which is used to unselect cells by removing the “on” value from their class attributes (via Robert Nyman)
  • A custom javascript function to tie the behavior together.

Attach the custom function to the “Choose” link’s onclick event:

...
<tfoot>
  <tr>
    ...
    <td class="choiceA">
      <a href="#" onclick="activateThisColumn('choiceA');return false;">
        Choose
      </a>

    </td>
    <td class="choiceB">
      <a href="#" onclick="activateThisColumn('choiceB');return false;">
        Choose
      </a>
    </td>

    ...
  </tr>
</tfoot>
...

View example 4, selectable columns. Try it – it works!

Next Step: Introducing Images

If we were just changing background colors, it wouldn’t have nearly the same coolness as the Crazy Egg table, whose selected column seems to pop out, or extend beyond the boundaries of the table.

It’s obvious that a background image is used with the selected table header cell. What isn’t obvious is that the neighboring table header cells are also using background images, which have a solid color on the bottom, and white along the top. This is evident when turning on cell outlines using Firefox’s Web Developer toolbar:

I’m going to make some images that accomplish the same thing. Here are the images I’ll be using:

regular <th>
selected <th>
left column <th>
regular <td>
selected <td>
left column <td>
regular <td> in tfoot
selected <td> in tfoot
left side <td> in tfoot
choose button

View example 5, images and cell dimensions in place. Looking good – we now have a decent-looking table with some dynamic behavior.

But we can’t stop. Let’s introduce even more dynamic behavior.

Next Step: Showing a form, hiding some table columns.

On Crazy Egg, when a column is selected, all the other columns disappear, the selected column moves to the left, and a form appears in the space to the right. The cancel button of the form then reshows all the other previously hidden columns.

First, I’ll draw out the form that will exist outside the table.

View example 6, a form is born.

Then we must style the form to take up the exact dimensions of the four unselected columns.

View example 7, a form is styled and dimensionalized.

The form will have to be absolutely positioned over table. So we’ll introduce a container element to both the form and the table to be our relative positioned parent.

<div id="prices">

  <div id="formcontainer">
  ...
  </div>

  
  <table id="pricetable">
  ...
  </table>
  
</div>

View example 8, a form is positioned.

Ooops! The form shouldn’t show by default, and we need to have all other cells except the cells in the selected column and cells in the far left column. We’ll make use of a new function, hasclass, that checks for class=”side”. We’ll modify our JavaScript function “activateThisColumn” to only hide and show the appropriate cells.

function activateThisColumn(column) {
  var table = document.getElementById('pricetable');
  var form = document.getElementById('formcontainer');

  // first, remove the 'on' class from all other th's
  var ths = table.getElementsByTagName('th');
  for (var g=0; g<ths.length; g++) {
    removeClassName(ths[g], 'on');
    if (!hasclass(ths[g],'side')) {
      ths[g].style.display = 'none';
    }
  }
  // then, remove the 'on' class from all other td's
  var tds = table.getElementsByTagName('td');
  for (var m=0; m<tds.length; m++) {
    removeClassName(tds[m], 'on');
    if (!hasclass(tds[m],'side')) {
      tds[m].style.display = 'none';
    }
  }

  // now, add the class 'on' to the selected th
  var newths = getElementsByClassName(column, 'th', table);
  for (var h=0; h<newths.length; h++) {
    addClassName(newths[h], 'on');
    newths[h].style.display = '';
    // not all browsers like display = 'block' for cells
  }
    // and finally, add the class 'on' to the selected td
  var newtds = getElementsByClassName(column, 'td', table);
  for (var i=0; i<newtds.length; i++) {
    addClassName(newtds[i], 'on');
    newtds[i].style.display = '';
    // not all browsers like display = 'block' for cells

  }
  // show the form!
  form.style.display = 'block';
}

View example 9, a form is revealed when requested.

But we still need to hide the form and keep our selected columns when the form is cancelled. We’ll add a function to the onclick event of the cancel button.

<input
  type="image"
  alt="Cancel"
  src="i/button_cancel.gif"
  onclick="hideTheForm();return false;" />

And the function says:

function hideTheForm() {
  // get the form
  var form = document.getElementById('formcontainer');
  // hide the form
  form.style.display = 'none';

  // now get the hidden table cells and show them again
  var table = document.getElementById('pricetable');
  var tds = table.getElementsByTagName('td');
  for (var i=0; i<tds.length; i++) {
    tds[i].style.display = '';
  }
  var ths = table.getElementsByTagName('th');
  for (var k=0; k<ths.length; k++) {
    ths[k].style.display = '';
  }
}

View example 10, a form is revealed when requested, and dismissed from view when cancelled.

The finished product

Download a zip with all the html, CSS, JavaScript, and supporting images.

There’s more going on with Crazy Egg’s form than what I’ve mentioned in this article. Mainly, Crazy Egg invokes a JavaScript library to animate the form’s appearance rather than switching from ‘none’ to ‘block’ and back again so bluntly. That extra step is beyond the scope of this article, but that kind of detail and work put into the form gives a clear indication that the folks at Crazy Egg really know what they are doing. (Also, IE has some display issues for my form, but those could easily be overcome with conditional comments.)

I hope you enjoy the post, and see what some scripting packaged with images and CSS can accomplish in a small amount of space.


This post was ported from an old host and CMS, so many comments were lost. Below are the comments that I found were most helpful regarding this post that I salvaged. Some links or attributions may not be working correctly.


Andrew said:
Is there any way to pass the column id (Choice A, B, C or D) to the form as a hidden field?
So when the visitor clicks on the Choose button, the choice is registered on the form – hidden – so this can then be passed to the receiving system?
I have pretty much everything else sorted just need to let our marketing automation system know which product the visitor is interested in.
Cheers,
Andrew

CSS Guy said:
@Andrew
Good question. Answer is yes. In the form, add this hidden input:
<input type=”hidden” name=”optionSelection” id=”optionSelection” value=”” />
And modify the activateThisColumn function like so (note the part in bold at the bottom):
function activateThisColumn(column) {
var table = document.getElementById(‘pricetable’);
var form = document.getElementById(‘formcontainer’);
// first, remove the ‘on’ class from all other th’s
var ths = table.getElementsByTagName(‘th’);
for (var g=0; g removeClassName(ths[g], ‘on’);
if (!hasClass(ths[g],’side’)) {
ths[g].style.display = ‘none’;
}
}
// then, remove the ‘on’ class from all other td’s
var tds = table.getElementsByTagName(‘td’);
for (var m=0; m removeClassName(tds[m], ‘on’);
if (!hasClass(tds[m],’side’)) {
tds[m].style.display = ‘none’;
}
}
// now, add the class ‘on’ to the selected th
var newths = getElementsByClassName(column, ‘th’, table);
for (var h=0; h addClassName(newths[h], ‘on’);
newths[h].style.display = ”;
// not all browsers like display = ‘block’ for cells
}
// and finally, add the class ‘on’ to the selected td
var newtds = getElementsByClassName(column, ‘td’, table);
for (var i=0; i addClassName(newtds[i], ‘on’);
newtds[i].style.display = ”;
// not all browsers like display = ‘block’ for cells
}
var optionSelection = document.getElementById(‘optionSelection’);
optionSelection.value = column;
// show the form!
form.style.display = ‘block’;
}
Now when someone clicks on the ‘choose’ link for a column, the class name for that column is inserted into a hidden input. Hope that helps.

One thought on “Creating a table with dynamically highlighted columns like Crazy Egg’s pricing table

Comments are closed.