JavaScript Equal Height Columns

The desire for equal height columns in a CSS layout is nothing new; there are many solutions available, some use JavaScript, others use CSS with negative margins, and then, there’s the faux columns method using background images. All of these methods have their place as perfectly valid solutions, and, depending on the situation, may be the best solution available. It’s the JavaScript methods available that are the subject this article; most of the solutions available – certainly those on the first page when googling ‘equal height javascript’ – include a variation of either of the lines below:

column[i].style.height = maxHeight + 'px';
// or
column[i].style.minHeight = maxHeight + 'px';

The use of inline styles creates an edge case that can result in a large, ugly, gap between the equalized columns and the footer when printing the page; if footer is pushed on to an otherwise blank page it becomes wasteful. Example 1: Layout without equalised columns Example 2: Equalised columns with traditional JavaScript (pdf of print view).

Basic requirements

When deciding to write my own version of the script, rather than sending an array, or limiting the script to a certain number of columns, I decided to send the requirements as an expression, ie

PWCC.equalHeights("content=sidebar");

To avoid the printing problem in example 2, CSS is created dynamically and assigned to the relevant media type – in this case ‘screen’ or ‘screen, handheld’. I’ve used a script by Bobby van der Sluis – modified to allow the specification of the media type and moved it into my namespace – to create the CSS.

function createStyleRule(selector, declaration) {
//becomes
PWCC.createStyleRule = function (selector, declaration, media) {
if (media == null) {
  media = 'screen';
}

style_node.setAttribute("media", "screen");
//becomes
style_node.setAttribute("media", media);

The function

Using the traditional version in example two, the inline styles make it necessary to loop through each column twice, first to get maximum height, and, subsequently to set the height on each element. In the function below the use of dynamic CSS allows a class name to be created at the start of the script and applied to each column in the initial loop – very slightly reducing processing power; in the script below a random number is appended to the class name to help ensure there are no clashes with the static CSS – with a bit of care it can be removed.

PWCC.equalHeight = function (expr, className, media) {
  if (className == null) {
    className = 'equalHeight';
  }
  if (media == null) {
    media = 'screen'; //default
  }

  //split expr at = signs
  var sections = expr.split("=");

  //generate class name
  var classRandom = Math.floor(Math.random()*999999);
  className = className + '-' + classRandom;

  //get column heights, add class name to html elemenets
  var objects = new Array();
  var maxHeight = 0;
  for(var i=0; i < sections.length; i++) {
    objects[i] = document.getElementById(sections[i]);
    PWCC.addClass(objects[i], className);
    //addClass is in base.js - it adds a class to an element.

    if (objects[i].offsetHeight > maxHeight) {
      maxHeight = objects[i].offsetHeight;
    }
  }

  //create css declaration
  var classDeclaration = 'min-height: ' + maxHeight + 'px;'
  var ie6Declaration = 'height: ' + maxHeight + 'px;'
  PWCC.createStyleRule('.' + className, classDeclaration, media);
  PWCC.createStyleRule('* html .' + className, ie6Declaration, media);
}

The IE6 declaration has been made using a * html hack; I considered this a better option than the others available, browser sniffing, or, JavaScript conditional comments; others’ opinions may differ and prefer to avoid the * html hack. Finally to call the script once the page loads, add a function call to the load event

PWCC.addLoadEvent(function() {
  //addLoadEvent is in base.js - adds code to window.onload
  PWCC.equalHeight('content=sidebar');
});

The final working version can be seen in example 3, along with the source JavaScript; the function above makes calls to my base.js file.