How to CSS a two column list of items?

31,823

Solution 1

You just need the CSS multi-column properties, e.g. ul { column-count: 2; }.

See Can I Use for support information on CSS Columns.

Solution 2

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() &lt;= $vCols]" />
    <xsl:variable name="vIterR" select="item[position() &lt;= $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.

Solution 3

You could try something like:

.option:nth-of-type(n+5) {
    position: relative;
    left: 14ex;
    top: -6em;
}

.option:nth-of-type(n+9) {
    color: red;
    left: 28ex;
    top: -12em;
}

... for four items a column, where line-height is 1.5em and column width will be 14ex.

But because this uses relative positioning, you'll probably have to reduce the size of the container.

Also, according to w3schools, this property isn't available in IE8 or lower.

Solution 4

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.

Share:
31,823
Alex Angas
Author by

Alex Angas

Updated on April 06, 2020

Comments

  • Alex Angas
    Alex Angas about 4 years

    I need to display a two column list of items according to the following rules:

    • Container for the columns has fluid width
    • Width of both columns needs to be equal
    • Items are dynamically rendered and at least one will be displayed
    • Item ordering needs to flow down the left column first, then the right
    • Items need to line up evenly across the bottom or in the case of an odd number the extra item should show in the left column

    Here is an example:

    ~ Item 1   | ~ Item 6
    ~ Item 2   | ~ Item 7
    ~ Item 3   | ~ Item 8
    ~ Item 4   | ~ Item 9
    ~ Item 5   |
    

    The HTML can be anything as long as it solves this problem. I'm restricted to using XSLT to wrap HTML around what the server spits out. I have access to two XSLT parameters: one that tells me the current item number and one that tells me how many items there are.

    My CSS skills are basic/intermediate and I don't know where to start here. Any ideas on whether this is achievable and how to do it?

    Update:

    Thanks for the answers. Consensus seems to be either use the A List Apart article or a table which I'd prefer as it's simpler. The problem with the table is that the server gives me the items in sorted order. To use a table would mean XSLT trickery to re-sort, wouldn't it?

    <tr>
        <td>Item 1</td>
        <td>Item 4</td>
    </tr>
    <tr>
        <td>Item 2</td>
        <td>Item 5</td>
    </tr>
    <tr>
        <td>Item 3</td>
        <td>&nbsp;</td>
    </tr>