Polity Research Ltd
IT { innovation; analysis; design; development; review }

How to spread lists across multiple columns

Problem

You wish to spread a list across multiple columns without breaking standard HTML list semantics.

Solution

Begin by defining CSS classes for each n-column list type you wish to use, for example mcol2 for two-column lists, mcol3 for three, etc as follows:
  ul.mcol2 li {margin: 0; position: relative}
  ul.mcol3 li {margin: 0; position: relative}
  ul.mcol4 li {margin: 0; position: relative}
And add some JavaScript to relayout the elements of these lists when the document is loaded or resized, as follows:
function multiColumnLayout(olElem, nCols) {
  var items = olElem.getElementsByTagName('li');
  var colWidth = olElem.clientWidth/nCols;
  var colLen = Math.max(1, Math.floor(items.length / nCols));
  var hAcc = 0, hOff = 0, olHeight = 0;
  for (var i=0; i<items.length; i++) {
    var c = items[i];
    var m = Math.floor(i / colLen);
    c.style.left = (m*colWidth)+'px';
    if (i>0 && (i % colLen == 0)) {
      olHeight = Math.max(olHeight, hAcc-hOff);
      hOff = hAcc;
    }
    c.style.top = '-'+hOff+'px';
    hAcc += c.offsetHeight;
  }
  olHeight = Math.max(olHeight, hAcc-hOff);
  olElem.style.height = olHeight + 'px';
}

function multiColumnLayoutAll() {
  for (var nc=2; nc<5; nc++) {
    var cls = 'mcol'+nc;
    var lists = document.getElementsByClassName(cls);
    for (var i=0; i<lists.length; i++) {
  	  multiColumnLayout(lists[i], nc);
    }
  }
}
The above script uses a method, getElementsByClassName, that is not available in all browsers, so we define one as follows:
if (document.getElementsByClassName==null) {
  document.getElementsByClassName = function(cl) {
    var retnode = [];
    var myclass = new RegExp('\\b'+cl+'\\b');
    var elem = this.getElementsByTagName('*');
    for (var i = 0; i < elem.length; i++) {
      var classes = elem[i].className;
      if (myclass.test(classes)) retnode.push(elem[i]);
    }
    return retnode;
  }; 
}
Lastly, we need to make sure our layout code is execute on load and resize events as follows:
window.onresize = function() {
  multiColumnLayoutAll();
}

window.onload = function() {
  multiColumnLayoutAll();
}
Now you can specify how many columns to spread a list over simply by assigning a class to the outer element:
  <ul class=mcol4>
    <li>aqua</li><li>black</li><li>blue</li><li>fuchsia</li>
    <li>gray</li><li>green</li><li>lime</li><li>maroon</li>
    <li>navy</li><li>olive</li><li>purple</li><li>red</li>
    <li>silver</li><li>teal</li><li>yellow</li><li>white</li>
  </ul>
Here is what it looks like in your browser:

  • aqua
  • black
  • blue
  • fuchsia
  • gray
  • green
  • lime
  • maroon
  • navy
  • olive
  • purple
  • red
  • silver
  • teal
  • yellow
  • white

Boookmark this using:  Del.icio.us  |  Digg  |  Technorati  |  Blinklist  |  Furl  |  reddit
Copyright © 2003-2008 Polity Research Ltd. All Rights Reserved.