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
A WebTip by Dan Boresjo
