I need to display a two column list of items according to the following rules:
The problem with a-list-apart, from what I could see, is that if you split a list of 11 items into 2 colums, I would want the 6th item to be in the first list. alistapart I think puts the 11th item instead in the second column.
My fix was to use jQuery-Columns Plugin. For example to split a ul with a class of .mylist into 2 columns you would do
$(document).ready( function () {
$('.mylist').cols(5);
});
Here's a live example on jsfiddle
CSS can't do this. You need to collect the list on the server and distribute the items in two table columns (you can use CSS to make the two columns the same width).
Note: There are browser extensions for multi column layout but they are not portable.
[EDIT] I'm aware of the article on alistapart but I can't see a difference to my solution: The proposed multi column layout gives each item a fixed position (by giving each item a unique CSS ID and then positioning it somehow). This means you need to generate the HTML and sort the items on the server and then use lots of tricks to position them.
It's much more simple to use a table with two columns and drop the items into them during rendering.
I know that people dismiss HTML table based layouts, but what the heck. They work. If you are vain, you are free to go the extra mile and find a pure CSS based way to do it. :-)
So here goes an XSLT solution.
<xml>
<item id="1">Item 1</item>
<item id="2">Item 2</item>
<item id="3">Item 3</item>
<item id="4">Item 4</item>
<item id="5">Item 5</item>
<item id="6">Item 6</item>
<item id="7">Item 7</item>
<item id="8">Item 8</item>
<item id="9">Item 9</item>
</xml>
With this XSL 1.0 template applied (the number of columns is even configurable):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/xml">
<xsl:variable name="vCols" select="2" />
<xsl:variable name="vCount" select="count(item)" />
<xsl:variable name="vRows" select="ceiling($vCount div $vCols)" />
<xsl:variable name="vIterC" select="item[position() <= $vCols]" />
<xsl:variable name="vIterR" select="item[position() <= $vRows]" />
<xsl:variable name="vSelf" select="." />
<table>
<xsl:for-each select="$vIterR">
<xsl:variable name="vRowIdx" select="position()" />
<tr>
<xsl:for-each select="$vIterC">
<xsl:variable name="vOffset" select="$vRows * (position() - 1)" />
<td>
<xsl:value-of select="$vSelf/item[$vRowIdx + $vOffset]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Yields:
<table>
<tr>
<td>Item 1</td>
<td>Item 6</td>
</tr>
<tr>
<td>Item 2</td>
<td>Item 7</td>
</tr>
<tr>
<td>Item 3</td>
<td>Item 8</td>
</tr>
<tr>
<td>Item 4</td>
<td>Item 9</td>
</tr>
<tr>
<td>Item 5</td>
<td></td>
</tr>
</table>
In case there is only one item, it produces:
<table>
<tr>
<td>Item 1</td>
</tr>
</table>
So no two columns in this situation.
In any case the table will always be well-formed (e.g. no jagged rows). The one thing that this solution does not do is sorting the output. The output will always be in document order. You seem to get the items sorted properly, so this should not be a big problem.
One of your criteria is reading the list down, so a simple solution is a two-column table (@Aaron Digulla), with the first half of your list in the first column, etc. You can use any HTML, right? We've resorted to this method for some of our lists, since we didn't want to edit our CSS every time a list changed (nor did we want to remember to edit the classes in the HTML).
Method 1 in the ALA article would be the 2nd simplest solution (and more ideal from a purist point of view). Since you know the current item number and how many items there are, you could add some logic to get list items in the correct order.
CSS can easily style either method.
You just need the CSS multi-column properties, e.g.
ul { column-count: 2; }
.
See Can I Use for support information on CSS Columns.
If you can divide the list using little javascript (which I am not good at), then this should work:
<script type="text/javascript">
var i=0;
var n=4;
if (i==0)
{
document.write("<div id='listFlow'><ul>");
}
for (i=1;i<=15;i++)
{
if (i==n)
{
document.write("<li>The number is " + i + "</li>" +"</ul><ul>");
var n=n+4;
continue;
}
document.write("<li>The number is " + i + "</li>");
}
document.write("</ul></div>");
</script>
<style type="text/css">
#listFlow ul{
float:left;
width:200px;
margin:0 0 0 10px;
padding:0;
}
#listFlow ul li{
list-style-position:inside;
list-style-type:disc;
margin:0;
padding:0;
width:180px;
}
</style>
Note: Please look at the CSS and NOT the javascript. This is just to show that how the lists can flow as described.